嵌入式知识点总结 C/C++ 专题提升(一)-关键字
针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。
目录
1.C语言宏中"#“和"##"的用法
1.1.(#)字符串化操作符
1.2.(##)符号连接操作符
2.关键字volatile有什么含意?并举出三个不同的例子?
2.1.并行设备的硬件寄存器
2.2.中断服务程序中修改的变量
2.3.多线程中共享的变量
3.关键字static的作用是什么?
3.1.在函数体内定义静态变量
3.2.在模块内定义静态变量
3.3.在模块内定义静态函数
4.在C语言中,为什么 static 变量只初始化一次?
5.extern"c”的作用是什么?
6.const有什么作用?
6.1.定义变量为常量
6.2.修饰函数的参数
6.3.修饰函数的返回值
6.4.节省空间,避免不必要的内存分配
7.什么情况下使用const关键字?
8.new/delete与malloc/free的区别是什么?
8.1.类型安全性
8.2.构造函数与析构函数
8.3.内存管理
8.4.对象的内存对齐和初始化
9.strlen("\0")=? sizeof("\0")=?
10.sizeof和strlen有什么区别?
11.不使用 sizeof,如何求int占用的字节数?
12.C语言中 struct与 union的区别是什么?
13.左值和右值是什么?
14.什么是短路求值?
15.++a和a++有什么区别?两者是如何实现的?
1.C语言宏中"#“和"##"的用法
1.1.(#)字符串化操作符
功能:将宏参数转换为字符串字面量。
用法:# 操作符会将紧随其后的参数转换为一个带双引号的字符串。
#include <stdio.h>
#define STR(x) #x
int main() {printf("%s\n", STR(Hello, World!)); // 输出:Hello, World!printf("%s\n", STR(123)); // 输出:123return 0;
}
1.2.(##)符号连接操作符
功能:将两个标记(Token)拼接为一个标记。
用法:## 操作符会将它两边的宏参数或标记拼接在一起,形成新的标记。
#include <stdio.h>#define CONCAT(x, y) x##yint main() {int xy = 100;printf("%d\n", CONCAT(x, y)); // 输出:100return 0;
}
CONCAT(x, y)
将x
和y
拼接为xy
,因此xy
变量被正确解析。##
在生成代码时非常有用,例如动态生成变量名或函数名。
2.关键字volatile有什么含意?并举出三个不同的例子?
2.1.并行设备的硬件寄存器
存储器映射的硬件寄存器通常加volatile,因为寄存器随时可以被外设硬件修改。当声明指向设备寄存器的指针时一定要用volatile,它会告诉编译器不要对存储在这个地址的数据进行假设。
就比如我们常用的MDK中,你单纯给一个寄存器赋值,不加volatile会被优化掉,程序会略过这个内容去编译别的部分。
#define XBYTE ((volatile unsigned char*)0x8000) // 假设硬件寄存器的基地址void set_register() {XBYTE[2] = 0x55; // 写入 0x55XBYTE[2] = 0x56; // 写入 0x56XBYTE[2] = 0x57; // 写入 0x57XBYTE[2] = 0x58; // 写入 0x58
}
- 如果未声明
volatile
,编译器可能优化为直接写入最后的值0x58
。 - 声明了
volatile
后,编译器会逐条生成机器代码,确保硬件设备能够接收到完整的写入操作序列。
2.2.中断服务程序中修改的变量
volatile提醒编译器,它后面所定义的变量随时都有可能改变。因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
当中断服务程序(ISR)修改一个变量,主程序可能在等待该变量的改变。在这种情况下,使用 volatile
避免主程序读取优化后的缓存值,确保从内存中获取最新值。
#include <stdbool.h>volatile bool flag = false; // 用于主程序和中断之间的通信void ISR() {flag = true; // 中断触发时修改变量
}void main() {while (!flag) {// 等待中断触发}// 中断触发后执行其他操作
}
- 如果未声明
volatile
,主程序可能会认为flag
始终未改变,从而陷入死循环。 - 使用
volatile
后,每次都会直接从内存读取flag
的值,确保中断修改可以被感知。
2.3.多线程中共享的变量
在多线程环境中,不同线程可能会访问或修改同一个变量。volatile
确保每个线程都能读取到变量的最新值,而不是被优化后的缓存值。
#include <pthread.h>
#include <stdbool.h>volatile bool stop = false; // 多线程共享变量void* thread_func(void* arg) {while (!stop) {// 执行线程操作}return NULL;
}int main() {pthread_t thread;pthread_create(&thread, NULL, thread_func, NULL);// 主线程控制其他操作sleep(2);stop = true; // 通知线程停止pthread_join(thread, NULL);return 0;
}
- 如果未声明
volatile
,线程可能会读取到未更新的stop
值,导致逻辑错误。 - 声明
volatile
后,线程每次都会从内存中读取stop
的值。
3.关键字static的作用是什么?
3.1.在函数体内定义静态变量
在函数体,只会被初始化一次,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
静态变量具有静态存储周期,只会被初始化一次,且在函数调用结束后其值不会丢失,而是保持到下一次函数调用。
#include <stdio.h>void counter() {static int count = 0; // 静态变量,仅初始化一次count++;printf("Count = %d\n", count);
}int main() {counter(); // 输出:Count = 1counter(); // 输出:Count = 2counter(); // 输出:Count = 3return 0;
}
static
保证变量只初始化一次,即使函数被多次调用。- 变量在函数作用域内可见,但其值会在多次调用中保持。
3.2.在模块内定义静态变量
当在函数体外(即全局作用域)使用 static
时,变量的作用域被限制在当前文件,不能被其他文件中的代码访问。这种变量称为局部全局变量。
在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量(只能被当前文件使用)。
// file1.c
#include <stdio.h>static int local_var = 10; // 静态全局变量void print_local_var() {printf("local_var = %d\n", local_var);
}// file2.c
extern void print_local_var();int main() {print_local_var(); // 如果没有 static,local_var 可直接被访问return 0;
}
- 限制全局变量的作用域,仅在当前文件中可见。
- 避免命名冲突,特别是在大型项目中。
3.3.在模块内定义静态函数
当函数使用 static
关键字修饰时,其作用域被限制在当前文件,不能被其他文件调用。这种函数被称为静态函数。
在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用(只能被当前文件使用)。
// file1.c
#include <stdio.h>static void local_function() {printf("This is a static function.\n");
}void call_local_function() {local_function();
}// file2.c
extern void call_local_function();int main() {call_local_function(); // 正常调用 file1.c 的接口函数// local_function(); // 错误:无法访问静态函数return 0;
}
- 限制函数作用域,仅在当前文件中可见。
- 适合用于实现模块内部的辅助功能,避免函数命名冲突。
作用域 | static 的用途 |
---|---|
函数体内变量 | 变量只初始化一次,值在函数多次调用中保持不变。 |
模块内变量(全局变量) | 变量作用域仅限于当前文件,防止全局命名冲突。 |
模块内函数 | 函数作用域仅限于当前文件,适用于模块内部使用的辅助功能。 |
注意,我们很多时候是要避免各种全局变量的,因此我们除了利用结构体,就是利用第一点,函数体内变量这个办法。
4.在C语言中,为什么 static 变量只初始化一次?
静态变量 (static
) 存储在内存的 静态存储区(也称为 全局数据区)。
对于所有的对象(不仅仅是静态对象),初始化都只有一次,而由于静态变量具有“记忆“功能,初始化后,一直都没有被销毁,都会保存在内存区域中,所以不会再次初始化。存放在静态区的变量的生命周期一般比较长,它与整个程序“同生死、共存亡”,所以它只需初始化一次。而auto变量,即自动变量由于它存放在栈区,一旦函数调用结束,就会立刻被销毁。
类型 | 存储位置 | 生命周期 | 初始化次数 |
---|---|---|---|
静态变量 | 静态存储区 | 程序运行期间始终存在 | 1 次 |
自动变量 | 栈区 | 随函数调用创建,随函数结束销毁 | 每次重新初始化 |
5.extern"c”的作用是什么?
extern"℃"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern"C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。
extern "C"
的主要作用是实现 C++ 和 C 之间的兼容性:
- C++ 和 C 在函数符号(名称)处理上有本质区别。
- C++ 支持 函数重载,因此采用了 名称修饰(Name Mangling) 技术,使同名函数可以根据参数的类型和数量生成唯一的符号。
- C 不支持函数重载,函数名称在编译后直接对应符号表中的函数名。
- 如果 C++ 代码直接调用 C 的函数(或者反之),名称修饰会导致链接器无法找到正确的符号。
extern "C"
告诉编译器关闭 C++ 的名称修饰,按照 C 的方式处理符号表。
应用如下:
// C++ 文件
#include "example.h"extern "C" {#include "example.h"
}int main() {print_message("Hello from C++");return 0;
}
在 C 头文件 中添加 extern "C"
包装,避免名称修饰问题:
#ifdef __cplusplus
extern "C" {
#endifvoid function_in_c();#ifdef __cplusplus
}
#endif
- 仅限在 C++ 环境中使用:
- C 编译器不支持
extern "C"
关键字,因此在混合编译时需要通过宏区分语言环境(__cplusplus
宏用于判断是否是 C++ 编译器)。
- C 编译器不支持
- 仅影响链接(Linking)阶段:
extern "C"
并不改变代码的编译方式,只是改变符号表的生成方式。
6.const有什么作用?
6.1.定义变量为常量
局部变量或全局变量 可以通过 const
来定义为常量,一旦赋值后,该常量的值就不能被修改。
const int N = 100; // 定义常量N,值为100
// N = 50; // 错误:常量的值不能被修改
const int n; // 错误:常量在定义时必须初始化
6.2.修饰函数的参数
使用 const
修饰函数参数,表示该参数在函数体内不能被修改。这样可以保证函数不会无意间修改传入的参数值,增加代码的可维护性。
void func(const int x) {// x = 10; // 错误:x 是常量,不能修改
}
6.3.修饰函数的返回值
a. 返回指针类型并使用 const
修饰
- 当函数返回指针时,若用
const
修饰返回值类型,那么返回的指针所指向的数据内容不能被修改,同时该指针也只能赋值给被const
修饰的指针。
const char* GetString() {return "Hello";
}const char* str = GetString(); // 正确,str 被声明为 const
// char* str = GetString(); // 错误,str 未声明为 const,不能修改返回值
b. 返回普通类型并使用 const
修饰
- 如果
const
用于修饰普通类型的返回值,如int
,由于返回值是临时的副本,在函数调用结束后,返回值的生命周期也随之结束,因此将其修饰为const
是没有意义的。
const int GetValue() {return 5;
}int x = GetValue(); // 正确,返回值可以赋给普通变量
// const int y = GetValue(); // 不必要的,因为返回值会是临时变量,不会被修改
6.4.节省空间,避免不必要的内存分配
const
关键字还可以帮助优化内存管理。当你使用 const
来定义常量时,编译器会考虑将常量放入只读存储区,避免了额外的内存分配。对于宏(#define
)和 const
常量,它们在内存分配的方式上有所不同。
#define PI 3.14159 // 使用宏定义常量 PI
const double pi = 3.14159; // 使用 const 定义常量 pi
使用宏定义的常量(如 PI
)会在编译时进行文本替换,所有使用该宏的地方都会被替换为常量值,因此不会单独分配内存;而 const
常量则会在内存中分配空间,通常存储在只读数据区。
double i = PI; // 编译期间进行宏替换,不会分配内存
double I = pi; // 分配内存,存储常量 pi
宏定义常量的每次使用都会进行文本替换,因此会进行额外的内存分配。相反,const
常量只会分配一次内存。
#define PI 3.14159 // 宏定义常量 PI
double j = PI; // 这里会进行宏替换,不会再次分配内存
double I = PI; // 宏替换后再次分配内存
7.什么情况下使用const关键字?
序号 | 使用场景 | 示例 | 说明 |
---|---|---|---|
1 | 修饰一般常量 | const int x = 2; int const x = 2; | 定义只读的常量,const 位置灵活。 |
2 | 修饰常数组 | const int arr[8] = {1,2,3,4,5,6,7,8}; int const arr[8] = {1,2,3,4,5,6,7,8}; | 定义的数组内容不可修改。 |
3 | 修饰常对象 | const A obj; A const obj; | 定义的类对象不可被修改,且需立即初始化。 |
4 | 修饰指针相关 | - const int *p; (指向常量的指针,p 的内容不可变,p 本身可变)- int *const p; (指针常量,p 不可变,内容可变)- const int *const p; (指向常量的常量指针,p 和内容都不可变) | 不同组合修饰指针的行为。 |
5 | 修饰常引用 | void func(const int &ref); | 常引用绑定到变量后不能更改其指向对象的值,可保护传入变量不被函数修改。 |
6 | 修饰函数的常参数 | void func(const int var); | 参数不可在函数体内被修改。 |
7 | 修饰函数的返回值 | - const int func(); (返回的值不可修改)- const A func(); (返回的对象不可修改) | 表明返回值不可被外部代码修改。 |
8 | 跨文件使用常量 | extern const int i; | 在其他文件中使用 const 修饰的全局变量。 |
const
的位置:
const
可以放在类型前或类型后,如 const int
与 int const
表达同样的含义。
对于指针的 const
修饰,其位置决定了是修饰指针本身,还是指针指向的内容。
保护机制:
使用 const
的核心目的之一是防止数据被意外修改,提高代码的安全性和可读性。
类和对象:
对象或成员函数被 const
修饰后,只能调用其他 const
成员函数,确保不会修改对象的状态。
通过合理使用 const
,可以编写更安全、健壮的代码。
8.new/delete与malloc/free的区别是什么?
8.1.类型安全性
new
和 delete
:
new
是 C++ 的运算符,delete
也是运算符,具有类型安全性。new
会返回正确类型的指针,无需强制转换。使用时,编译器会自动计算所需内存的大小。delete
会释放通过new
分配的内存,并自动调用对象的析构函数。
int* p = new int; // 分配内存并返回指向 int 类型的指针
delete p; // 释放内存并调用析构函数
malloc
和 free
:
malloc
和free
是 C 标准库函数,malloc
返回的是void*
指针,必须显式转换为实际的类型指针。它没有类型安全性,容易导致错误。malloc
只是为内存分配空间,并不调用构造函数,而free
只是释放内存,并不调用析构函数。
int* p = (int*)malloc(sizeof(int)); // 需要手动转换类型
free(p); // 只释放内存
8.2.构造函数与析构函数
new
和 delete
:
- 当使用
new
分配内存时,会自动调用类的构造函数来初始化对象。 - 当使用
delete
释放内存时,会自动调用类的析构函数。
class MyClass {
public:MyClass() { cout << "Constructor called" << endl; }~MyClass() { cout << "Destructor called" << endl; }
};MyClass* obj = new MyClass; // 自动调用构造函数
delete obj; // 自动调用析构函数
malloc
和 free
:
malloc
不会调用构造函数,仅分配内存;free
不会调用析构函数,仅释放内存。
MyClass* obj = (MyClass*)malloc(sizeof(MyClass)); // 不会调用构造函数
free(obj); // 不会调用析构函数
8.3.内存管理
new
和 delete
:
new
在分配内存时会计算所需内存的大小,并根据类型自动计算。delete
自动处理内存释放及相关清理工作。
malloc
和 free
:
malloc
需要明确指定需要分配的字节数,不会考虑对象的类型。free
只能释放malloc
或calloc
分配的内存,并且不能自动调用析构函数。
int* p = (int*)malloc(10 * sizeof(int)); // 需要手动计算内存大小
8.4.对象的内存对齐和初始化
new
和 delete
:
new
会调用类的构造函数进行初始化,并且会适当地进行内存对齐。delete
会释放内存并自动调用析构函数。
malloc
和 free
:
malloc
只分配原始内存,不会初始化对象。如果需要初始化对象,必须手动进行。free
只会释放内存,而不会调用析构函数。
int* p = new int(5); // 自动初始化
delete p; // 自动释放并调用析构函数
特性 | new /delete | malloc /free |
---|---|---|
语言 | C++ | C |
类型安全 | 类型安全,自动推导和转换 | 需要手动类型转换 |
构造函数/析构函数 | 自动调用构造函数/析构函数 | 不调用构造函数/析构函数 |
内存分配 | 自动计算内存大小 | 需要手动指定内存大小 |
内存初始化 | 支持初始化 | 不会初始化内存 |
使用方式 | 运算符,使用 new 和 delete | 函数,使用 malloc 和 free |
9.strlen("\0")=? sizeof("\0")=?
strlen("\0")=0 ,sizeof("\0")=2。
strlen用来计算字符串的长度(在C/C++中,字符串是以"0"作为结束符的),它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描直到碰到第一个字符串结束符\0为止,然后返回计数器值sizeof是C语言的关键字,它以字节的形式给出了其操作数的存储大小,操作数可以是一个表达式或括在括号内的类型名,操作数的存储大小由操作数的类型决定。
strlen()
函数计算的是 字符串的长度,即从字符串的开头到 第一个空字符 (\0
) 之前的字符数。在这种情况下,字符串 "\0"
只有一个字符,它就是 空字符 \0
,因此它的长度为 0。
strlen("\0"); // 结果是 0,因为字符串仅包含一个 '\0' 终止符
sizeof()
计算的是 操作数的大小(通常是以字节为单位)。在 C 中,字符串字面量 "str"
的实际类型是 字符数组,并且这个数组总是包括一个额外的空字符 \0
作为结束符。因此,字符串 "\0"
实际上是一个包含两个字符的字符数组:'\0'
和 \0
终止符。所以 sizeof("\0")
结果是 2。
sizeof("\0"); // 结果是 2,因为字符串 "\0" 包含两个字符:'\0' 和 '\0' 终止符
10.sizeof和strlen有什么区别?
strlen与 sizeof的差别表现在以下5个方面,
1.sizeof是运算符(是不是被弄糊涂了?事实上,sizeof既是关键字,也是运算符,但不是函数)而strlen是函数。 sizeof后如果是类型,则必须加括弧,如果是变量名,则可以不加括弧。
2. sizeof运算符的结果类型是 size_t,它在头文件中 typedef 为 unsigned int类型。该类型保证能够容纳实现所建立的最大对象的字节大小
3. sizeof可以用类型作为参数,strlen只能用char*作参数,而且必须是以“0结尾的。 sizeof还可以以函数作为参数,如intg(),则 sizeof(g())的值等于 sizeof( int的值,在32位计算机下,该值为4。
4.大部分编译程序的 sizeof都是在编译的时候计算的,所以可以通过 sizeof(x)来定义数组维数。而 strlen则是在运行期计算的,用来计算字符串的实际长度,不是类型占内存的大小。例如,charstr[20]="0123456789",字符数组str是编译期大小已经固定的数组,在32位机器下,为sizeof(char)*20=20,而其 strlen大小则是在运行期确定的,所以其值为字符串的实际长度10.当数组作为参数传给函数时,传递的是指针,而不是数组,即传递的是数组的首地址。
11.不使用 sizeof,如何求int占用的字节数?
#include <stdio.h>#define Mysizeof(value) ((char *)(&value + 1) - (char *)&value)int main() {int i;double f;double *q;// 输出各个变量占用的字节数printf("%d\n", Mysizeof(i)); // 输出 int 类型的字节数printf("%d\n", Mysizeof(f)); // 输出 double 类型的字节数printf("%d\n", Mysizeof(q)); // 输出 double* 类型的字节数return 0;
}
(char *)(&value + 1)
:将 value
的地址向后移动一个 value
类型的单位(如 int
,移动 1 个 int
的大小)。
(char *)&value
:获取 value
的起始地址。
两者相减,即可得到 value
类型的字节数,因为指针的差值以 char
的大小为单位,而 char
是 1 字节。
&value + 1
是类型安全的,它表示从当前地址向后移动一个变量的单位。
将地址强制转换为 (char *)
,使得指针的差值以字节为单位。
12.C语言中 struct与 union的区别是什么?
比较项目 | struct (结构体) | union (联合体) |
---|---|---|
内存分配 | 每个成员有独立的存储空间,大小是所有成员大小的累加值(考虑字节对齐)。 | 所有成员共用同一块内存,大小等于最大成员的大小(考虑字节对齐)。 |
成员访问 | 所有成员可以独立访问且互不影响。 | 同一时刻只能访问一个成员,写入一个成员会覆盖其他成员的值。 |
用途 | 用于保存多个相关但独立的数据。 | 用于在同一存储区域保存多个数据(节省内存)。 |
字节对齐 | 根据成员类型和字节对齐规则进行分配。 | 最大成员决定内存分配,并根据字节对齐规则调整大小。 |
适用场景 | 常用于多种类型数据的组合使用。 | 常用于需要节省内存或多种数据类型共用时。 |
typedef union {double i; // 8 bytesint k[5]; // 5 × 4 bytes = 20 byteschar c; // 1 byte
} DATE;typedef struct data {int cat; // 4 bytesDATE cow; // 24 bytes (union, 8-byte alignment)double dog; // 8 bytes
} too;DATE max;// sizeof(too) + sizeof(max)
-
DATE
的大小:
联合体的大小由 最大成员大小 决定,即 k[5]
,占用 20 字节。
为了满足 8 字节对齐,需要调整到 8 的倍数,实际占用 24 字节。
-
too
的大小:
int cat
: 4 字节。
DATE cow
: 24 字节(由前面计算)。
double dog
: 8 字节。
按 8 字节对齐,too
总大小 = 4 + 4(填充) + 24 + 8 = 40 字节。
-
max
的大小:
与 DATE
相同,占用 24 字节。
-
总大小:
sizeof(too) + sizeof(max) = 40 + 24 = 64
。
13.左值和右值是什么?
左值是指可以出现在等号左边的变量或表达式,它最重要的特点就是可写(可寻址)。也就是说,它的值可以被修改,如果一个变量或表达式的值不能被修改,那么它就不能作为左值。
int a = 10; // a 是左值
a = 20; // a 出现在赋值号的左边,可修改其值
右值是指只可以出现在等号右边的变量或表达式。它最重要的特点是可读。一般的使用场景都是把一个右值赋值给一个左值。
int b = a + 5; // (a + 5) 是右值,提供计算结果但无法修改
通常,左值可以作为右值,但是右值不一定是左值。
类别 | 左值(L-value) | 右值(R-value) |
---|---|---|
定义 | 表示内存中的一个地址,可出现在赋值运算符左侧 | 表示一个值,不占据内存地址,只能出现在赋值运算符右侧 |
特点 | 可寻址、可修改 | 不可寻址、只提供值,不能被修改 |
作用 | 提供一个持久的存储位置,可读写 | 提供数据,通常用于计算或赋值 |
示例 | 变量:int a; a = 5; | 常量或表达式:5; a + 3; |
内存分配 | 与具体内存地址绑定 | 通常是临时值,不绑定内存地址 |
使用场景 | - 出现在赋值号左侧 - 可作为右值 | - 出现在赋值号右侧 - 参与计算 |
互相关系 | 左值可以用作右值 | 右值不能用作左值 |
函数返回值 | 函数返回引用或指针是左值 | 函数返回具体值是右值 |
代码示例 | c<br>int a = 10; a = 20; | c<br>int b = a + 5; |
14.什么是短路求值?
短路求值(Short-Circuit Evaluation)是一种逻辑表达式的求值方式,在逻辑运算(&&
和 ||
)中,一旦可以确定整个表达式的最终结果,后续的部分就不会被执行。
- 逻辑或(
||
):- 如果左侧表达式为
true
,整个表达式为true
,后续表达式不会执行。 - 如果左侧表达式为
false
,需要计算右侧表达式。
- 如果左侧表达式为
- 逻辑与(
&&
):- 如果左侧表达式为
false
,整个表达式为false
,后续表达式不会执行。 - 如果左侧表达式为
true
,需要计算右侧表达式。
- 如果左侧表达式为
#include <stdio.h>
int main() {int i = 6; // i = 6int j = 1; // j = 1if (i > 0 || (j++) > 0); // 短路求值在此发生printf("%o\r\n", j); // 输出 j 的值return 0;
}
条件判断:i > 0 || (j++) > 0
先计算 i > 0
,结果为 true
(因为 i = 6
,大于 0)。
因为 ||
运算中只需要一边为 true
就能确定整个表达式为 true
,因此不会执行右侧的 (j++) > 0
。
j++
不会被执行,j
的值保持不变。
输出:printf("%o\r\n", j);
j
仍然为 1,因此输出 1
(八进制表示为 1
)。
15.++a和a++有什么区别?两者是如何实现的?
++a
(前置自增):先对变量自增 1,再返回变量的值。
a++
(后置自增):先返回变量的值,再对变量自增 1。
a++
的实现过程
int a = 5;
int temp = a; // 保存当前值到临时变量 temp
a = a + 1; // 自增
return temp; // 返回保存的临时变量 temp
++a
的实现过程
int a = 5;
a = a + 1; // 自增
return a; // 返回自增后的值
相关文章:
嵌入式知识点总结 C/C++ 专题提升(一)-关键字
针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。 目录 1.C语言宏中"#“和"##"的用法 1.1.(#)字符串化操作符 1.2.(##)符号连接操作符 2.关键字volatile有什么含意?并举出三个不同的例子? 2.1.并行设备的硬件寄存…...
Android 高版本如何获取App安装列表?
有个需求需要获取App内的安装列表,但是现在在高版本Android中,只能获取到一部分App效果,我获取的代码如下: val calendar Calendar.getInstance()val packageManager context.packageManagerval usageStatsManager context.getSystemService(Context.USAGE_STATS_SERVICE) …...
StarGAN:原理、用途及最新发展
一、引言 StarGAN是一种具有广泛应用的生成模型,具有同时生成多种类别数据的能力。它由Yunjey Choi等人在2017年提出,旨在实现图像多域间迁移,尤其适用于人脸属性转换。StarGAN的提出,标志着生成对抗网络(Generative A…...
TCP报文格式与核心机制
TCP与UDP都是传输层的重要协议,TCP的特性包括有连接、可靠传输、面向字节流、全双工。 TCP的报文格式如下: 一、报文格式 1.源端口号、目的端口号 源端口和目的端口是五元组中重要的两个性质,源端口即数据是从哪里来的,目的端…...
【2024年华为OD机试】 (B卷,100分)- 金字塔,BOSS的收入(Java JS PythonC/C++)
一、问题描述 微商模式收入计算 题目描述 微商模式中,下级每赚 100 元就要上交 15 元。给定每个级别的收入,求出金字塔尖上的人的收入。 输入描述 第一行输入 N,表示有 N 个代理商上下级关系。接下来输入 N 行,每行三个数&am…...
缓存、数据库双写一致性解决方案
双写一致性问题的核心是确保数据库和缓存之间的数据同步,以避免缓存与数据库数据不同步的问题,尤其是在高并发和异步环境下。本文将探讨双写一致性面临的主要问题和解决方案,重点关注最终一致性。 本文讨论的是最终一致性问题 双写一致性面…...
开放银行数据保护与合规实践案例
总体原则 开放银行的数据处理基本原则指的是数据处理者在数据生命周期的各阶段进行各种数 据处理时均应遵循的根本准则,是指导监管机构制定规范、进行管理以及开放银行进 行具体数据处理行为的纲领。根据《民法典》《个人信息保护法》《数据安全法》 《网络安全法…...
51c自动驾驶~合集47
我自己的原文哦~ https://blog.51cto.com/whaosoft/13083194 #DreamDrive 性能爆拉30%!英伟达:时空一致下的生成重建大一统新方案~ 从自车的驾驶轨迹中生成真实的视觉图像是实现自动驾驶模型可扩展训练的关键一步。基于重建的方法从log中生成3D场景…...
2024年AI与大数据技术趋势洞察:跨领域创新与社会变革
目录 引言 技术洞察 1. 大模型技术的创新与开源推动 2. AI Agent 智能体平台技术 3. 多模态技术的兴起:跨领域应用的新风口 4. 强化学习与推荐系统:智能化决策的底层驱动 5. 开源工具与平台的快速发展:赋能技术创新 6. 技术安全与伦理:AI技术的双刃剑 7. 跨领域技…...
【protobuf】二、proto3语法详解①
文章目录 前言Ⅰ. 字段规则Ⅱ. 消息类型的定义和使用1、定义2、使用1️⃣消息类型可作为字段类型使⽤2️⃣可导入其他 .proto 文件的消息并使用 -- import 3、创建通讯录 2.0 版本的 .proto 文件4、通讯录 2.0 版本的读写实现 -- 第一种验证方式5、decode选项 -- 第二种验证方式…...
React 中hooks之useLayoutEffect 用法总结以及与useEffect的区别
React useLayoutEffect 1. useLayoutEffect 基本概念 useLayoutEffect 是 React 的一个 Hook,它的函数签名与 useEffect 完全相同,但它会在所有的 DOM 变更之后同步调用 effect。它可以用来读取 DOM 布局并同步触发重渲染。 2. useLayoutEffect vs us…...
实战经验:使用 Python 的 PyPDF 进行 PDF 操作
文章目录 1. 为什么选择 PyPDF?2. 安装 PyPDF3. PDF 文件的合并与拆分3.1 合并 PDF 文件3.2 拆分 PDF 文件 4. 提取 PDF 文本5. 修改 PDF 元信息6. PDF 加密与解密6.1 加密 PDF6.2 解密 PDF 7. 页面旋转与裁剪7.1 旋转页面7.2 裁剪页面 8. 实战经验总结 PDF 是一种非…...
数据结构与算法之排序: LeetCode 15. 三数之和 (Ts版)
三数之和 https://leetcode.cn/problems/3sum/description/ 描述 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0请你返回所有和为 0 且不重复的三元…...
51c嵌入式~单片机~合集6
我自己的原文哦~ https://blog.51cto.com/whaosoft/13127816 一、STM32单片机的知识点总结 本文将以STM32F10x为例,对标准库开发进行概览。主要分为三块内容: STM32系统结构寄存器通过点灯案例,详解如何基于标准库构建STM32工程 STM3…...
SQL Server Management Studio 表内数据查询与删除指令
查询指令 //select * from 表名称 where 列名称 数据名称 select * from Card_Info where num CC3D4D删除指令,删除数据库有风险,操作不可逆,建议删除前备份,以免删错。 //Delete * from 表名称 where 列名称 数据名称 Delete f…...
Timesheet.js - 轻松打造炫酷时间表
Timesheet.js - 轻松打造炫酷时间表 前言 在现代网页设计中,时间表是一个常见的元素,用于展示项目进度、历史事件、个人经历等信息。 然而,创建一个既美观又功能强大的时间表并非易事。 幸运的是,Timesheet.js 这款神奇的 Java…...
产品经理面试题总结2025【其一】
一、产品理解与定位 1、你如何理解产品经理这个角色? 作为一名互联网产品经理,我理解这个角色的核心在于成为产品愿景的制定者和执行的推动者。具体来说,产品经理是连接市场、用户和技术团队之间的桥梁,负责理解市场需求、用户痛…...
第16章:Python TDD实现多币种货币运算
写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...
【Web】2025-SUCTF个人wp
目录 SU_blog SU_photogallery SU_POP SU_blog 先是注册功能覆盖admin账号 以admin身份登录,拿到读文件的权限 ./article?filearticles/..././..././..././..././..././..././etc/passwd ./article?filearticles/..././..././..././..././..././..././proc/1…...
C++实现矩阵Matrix类 实现基本运算
本系列文章致力于实现“手搓有限元,干翻Ansys的目标”,基本框架为前端显示使用QT实现交互,后端计算采用Visual Studio C。 目录 Matrix类 1、public function 1.1、构造函数与析构函数 1.2、获取矩阵数值 1.3、设置矩阵 1.4、矩阵转置…...
【GORM】初探gorm模型,字段标签与go案例
GORM是什么? GORM 是一个Go 语言 ORM(对象关系映射)库,它让我们可以使用结构体来操作数据库,而无需编写SQL 语句 GORM 模型与字段标签详解 在 GORM 中,模型是数据库表的抽象表示,字段标签&am…...
html全局遮罩,通过websocket来实现实时发布公告
1.index.html代码示例 <div id"websocket" style"display:none;position: absolute;color:red;background-color: black;width: 100%;height: 100%;z-index: 100; opacity: 0.9; padding-top: 30%;padding-left: 30%; padding-border:1px; "onclick&q…...
基于VSCode+CMake+debootstrap搭建Ubuntu交叉编译开发环境
基于VSCodeCMakedebootstrap搭建Ubuntu交叉编译开发环境 1 基于debootstrap搭建目标系统环境1.1 安装必要软件包1.2 创建sysroot目录1.3 运行debootstrap1.4 挂载必要的虚拟文件系统1.5 复制 QEMU 静态二进制文件1.6 进入目标系统1.7 使用目标系统(以安装zlog为例&a…...
C#中System.Text.Json:从入门到精通的实用指南
一、引言 在当今数字化时代,数据的高效交换与处理成为软件开发的核心环节。JSON(JavaScript Object Notation)凭借其简洁、轻量且易于读写的特性,已然成为数据交换领域的中流砥柱。无论是前后端数据交互,还是配置文件…...
【深度学习】Huber Loss详解
文章目录 1. Huber Loss 原理详解2. Pytorch 代码详解3.与 MSELoss、MAELoss 区别及各自优缺点3.1 MSELoss 均方误差损失3.2 MAELoss 平均绝对误差损失3.3 Huber Loss 4. 总结4.1 优化平滑4.2 梯度较好4.3 为什么说 MSE 是平滑的 1. Huber Loss 原理详解 Huber Loss 是一种结合…...
Maven下载配置
目录 Win下载配置maven的环境变量 Mac下载安装配置环境变量 MavenSetting.xml文件配置 Win 下载 https://maven.apache.org/ 在主页面点击Download 点击archives 最好不要下载使用新版本,我使用的是maven-3.6.3,我们点击页面下方的archives࿰…...
JS基础(5):运算符和语句
一.运算符 1.赋值运算符 加减乘除都是一样的,,-,*,/ 2.一元运算符:经常用来计数 自增: 每次只能加一 自减:-- 前置自增 后置自增 结…...
游戏引擎学习第81天
仓库:https://gitee.com/mrxiao_com/2d_game_2 或许我们应该尝试在地面上添加一些绘图 在这段时间的工作中,讨论了如何改进地面渲染的问题。虽然之前并没有专注于渲染部分,因为当时主要的工作重心不在这里,但在实现过程中,发现地…...
网络安全 | 什么是正向代理和反向代理?
关注:CodingTechWork 引言 在现代网络架构中,代理服务器扮演着重要的角色。它们在客户端和服务器之间充当中介,帮助管理、保护和优化数据流。根据代理的工作方向和用途,代理服务器可分为正向代理和反向代理。本文将深入探讨这两种…...
前缀和——模板 二维前缀和
一.题目描述 【模板】二维前缀和_牛客题霸_牛客网 二.题目解析 这道题和上一道题有点异曲同工之妙。输入一个m行n列的矩阵,然后进行q次操作,每次操作输入4个数,作为两个点的坐标,计算这两个点为对角线的矩阵的和。 三.算法原理 …...
oracle使用case when报错ORA-12704字符集不匹配原因分析及解决方法
问题概述 使用oracle的case when函数时,报错提示ORA-12704字符集不匹配,如下图,接下来分析报错原因并提出解决方法。 样例演示 现在有一个TESTTABLE表,本表包含的字段如下图所示,COL01字段是NVARCHAR2类型࿰…...
高等数学学习笔记 ☞ 定积分与积分公式
1. 定积分的基本概念 1.1 定积分的定义 1. 定义:设函数在闭区间上有界。在闭区间上任意插入若干个分点,即, 此时每个小区间的长度记作(不一定是等分的)。然后在每个小区间上任意取,对应的函数值为。 为保证每段的值(即矩形面积)无…...
MLMs之Agent:Phidata的简介、安装和使用方法、案例应用之详细攻略
MLMs之Agent:Phidata的简介、安装和使用方法、案例应用之详细攻略 目录 Phidata简介 Phidata安装和使用方法 1、安装 2、使用方法 (1)、认证 (2)、创建 Agent (3)、运行 Agent (4)、Agent Playground Phidata 案例应用 1、多工具 Agent 2、多模态 Agent …...
如何在不暴露MinIO地址的情况下,用Spring Boot与KKFileView实现文件预览
在现代Web应用中,文件预览是一项常见且重要的功能。它允许用户在不上传或下载文件的情况下,直接在浏览器中查看文件内容。然而,直接将文件存储服务(如MinIO)暴露给前端可能会带来安全风险。本文将介绍如何在不暴露MinI…...
ESP8266固件烧录
一、烧录原理 1、引脚布局 2、引脚定义 3、尺寸封装 4、环境要求 5、接线方式 ESP8266系列模块集成了高速GPI0和外围接口,这可能会导致严重的开关噪声。如果某些应用需要高功率和EMI特性,建议在数字I/0线上串联10到100欧姆。这可以在切换电源时抑制过冲…...
Python 模拟真人鼠标轨迹算法 - 防止游戏检测
一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序,它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言,原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势: 模拟…...
三天急速通关Java基础知识:Day1 基本语法
三天急速通关JAVA基础知识:Day1 基本语法 0 文章说明1 关键字 Keywords2 注释 Comments2.1 单行注释2.2 多行注释2.3 文档注释 3 数据类型 Data Types3.1 基本数据类型3.2 引用数据类型 4 变量与常量 Variables and Constant5 运算符 Operators6 字符串 String7 输入…...
免费使用 Adobe 和 JetBrains 软件的秘密
今天想和大家聊聊如何利用 Edu 教育邮箱免费使用 Photoshop、Illustrator 等 Adobe 系列软件,以及 JetBrains 开发工具。 首先,Adobe 的软件是设计师的必备工具。无论是处理图像的 Photoshop,还是进行矢量设计的 Illustrator,它们…...
Pytorch 自学笔记(三):利用自定义文本数据集构建Dataset和DataLoader
Pytorch 自学笔记(三) 1. Dataset与DataLoader1.1 torch.utils.data.Dataset1.2 torch.utils.data.DataLoader Pytorch 自学笔记系列的第三篇。针对Pytorch的Dataset和DataLoader进行简单的介绍,同时,介绍如何使用自定义文本数据集…...
gradle项目的创建和基本结构简介
文章目录 创建gradle项目(命令行)创建gradle项目(IDEA)项目基本结构和功能Gradle 构建流程测试类体验 创建gradle项目(命令行) yangMacdeMac-mini gradleStudy % gradle init Starting a Gradle Daemon (s…...
wow-agent---Day3 Zigent 智能代理开发框架
这个框架是课程讲解的,但资料比较少,觉得框架比较小众,所以这里只分析代码,打算把更多的精力放在metagpt的学习上,毕竟还是要学教为主流的框架,这对后续维护升级都有帮助,当然感兴趣作为研究&am…...
python 入门
1. Python 概述 1.1 简介 python 是一种面向对象的解释型编程语言,由吉多范罗苏姆开发; 1991 年,公开发行版发布; 因其可以将其他语言制作的模块轻松联接在一起,又被称作胶水语言; 1.2 优点 简单易学&…...
sentinel微服务保护
学习链接 SpringCloudRabbitMQDockerRedis搜索分布式 文章目录 学习链接1.初识Sentinel1.1.雪崩问题及解决方案1.1.1.雪崩问题1.1.2.超时处理1.1.3.仓壁模式1.1.4.断路器1.1.5.限流1.1.6.总结 1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel官网地址github地址…...
接口测试Day10-封装IHRM登录
-登录接口 普通方式实现 登录接口对象层 思路: 动态变化的,写入参数固定不变,直接写到方法实现内响应结果,通过返回值 return 分析: 封装实现: 登录接口测试用例层 封装断言方法 1、创建 文件 assert_uti…...
什么是IP地址、子网掩码、网关、DNS
简单解释 IP地址在网络中用于标识一个节点(或者网络设备的接口) IP地址用于IP报文在网络中的寻址 一个IPv4地址有32 bit。 IPv4地址通常采用“点分十进制”表示。 IPv4地址范围:0.0.0.0~255.255.255.255 网络部分:用来标识一个网络,代表IP地址所属网络。 主机部分:…...
python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)角点检测
角点检测(Corner Detection)是计算机视觉和图像处理中重要的步骤,主要用于提取图像中的关键特征,以便进行后续的任务,比如图像匹配、物体识别、运动跟踪等。下面介绍几种常用的角点检测方法及其应用。 1. Harris角点检…...
软路由系统iStoreOS 一键安装 docker compose
一键安装命令 大家好!今天我来分享一个快速安装 docker-compose 的方法。以下是我常用的命令,当前版本是 V2.32.4。如果你需要最新版本,可以查看获取docker compose最新版本号 部分,获取最新版本号后替换命令中的版本号即可。 w…...
Invicti-Professional-V25.1
01 更新介绍 此更新包括对内部代理的更改。内部扫描代理的当前版本为 25.1.0。内部身份验证验证程序代理的当前版本为 25.1.0。#新功能现在,单击扫描摘要屏幕中的预设扫描图标会将您重定向到具有过滤视图的 “最近扫描” 页面,从而改进导航和对相关扫描…...
【QT】: 初识 QWidget 控件 | QWidget 核心属性(API) | qrc 文件
🔥 目录 1. 控件概述 控件体系的发展阶段 2. QWidget 核心属性 2.1 核心属性概览2.2 用件可用(Enabled) 2.3 坐标系(Geometry) **实例 1: 控制按钮的位置**实例 2: 表白 程序 2.4 窗口标题(windowTiltle&a…...
Spring WebFlux
文章目录 一、概述1、Spring体系定位2、Spring MVC和WebFlux差异 二、入门1、依赖2、ReactorHttpHandlerAdapter(main启动)3、DispatcherHandler(SpringWebFlux启动)4、WebFilter 三、DispatcherHandler理解1、handle 前置知识&am…...