嵌入式知识点总结 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));           // 输出:123
    return 0;
}

1.2.(##)符号连接操作符

功能:将两个标记(Token)拼接为一个标记。

用法:## 操作符会将它两边的宏参数或标记拼接在一起,形成新的标记。

#include <stdio.h>

#define CONCAT(x, y) x##y

int main() {
    int xy = 100;
    printf("%d\n", CONCAT(x, y)); // 输出:100
    return 0;
}
  • CONCAT(x, y)xy 拼接为 xy,因此 xy 变量被正确解析。
  • ## 在生成代码时非常有用,例如动态生成变量名或函数名。

2.关键字volatile有什么含意?并举出三个不同的例子?

2.1.并行设备的硬件寄存器

存储器映射的硬件寄存器通常加volatile,因为寄存器随时可以被外设硬件修改。当声明指向设备寄存器的指针时一定要用volatile,它会告诉编译器不要对存储在这个地址的数据进行假设。

就比如我们常用的MDK中,你单纯给一个寄存器赋值,不加volatile会被优化掉,程序会略过这个内容去编译别的部分。

#define XBYTE ((volatile unsigned char*)0x8000) // 假设硬件寄存器的基地址

void set_register() {
    XBYTE[2] = 0x55; // 写入 0x55
    XBYTE[2] = 0x56; // 写入 0x56
    XBYTE[2] = 0x57; // 写入 0x57
    XBYTE[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 = 1
    counter(); // 输出:Count = 2
    counter(); // 输出:Count = 3
    return 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" {
#endif

void function_in_c();

#ifdef __cplusplus
}
#endif
  • 仅限在 C++ 环境中使用
    • C 编译器不支持 extern "C" 关键字,因此在混合编译时需要通过宏区分语言环境(__cplusplus 宏用于判断是否是 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 intint const 表达同样的含义。

对于指针的 const 修饰,其位置决定了是修饰指针本身,还是指针指向的内容。

保护机制:
使用 const 的核心目的之一是防止数据被意外修改,提高代码的安全性和可读性。

类和对象:
对象或成员函数被 const 修饰后,只能调用其他 const 成员函数,确保不会修改对象的状态。

通过合理使用 const,可以编写更安全、健壮的代码。

8.new/delete与malloc/free的区别是什么?

8.1.类型安全性

newdelete

  • new 是 C++ 的运算符,delete 也是运算符,具有类型安全性。new 会返回正确类型的指针,无需强制转换。使用时,编译器会自动计算所需内存的大小。
  • delete 会释放通过 new 分配的内存,并自动调用对象的析构函数。
int* p = new int;        // 分配内存并返回指向 int 类型的指针
delete p;                // 释放内存并调用析构函数

 mallocfree

  • mallocfree 是 C 标准库函数,malloc 返回的是 void* 指针,必须显式转换为实际的类型指针。它没有类型安全性,容易导致错误。
  • malloc 只是为内存分配空间,并不调用构造函数,而 free 只是释放内存,并不调用析构函数。
int* p = (int*)malloc(sizeof(int));  // 需要手动转换类型
free(p);                             // 只释放内存

8.2.构造函数与析构函数

newdelete

  • 当使用 new 分配内存时,会自动调用类的构造函数来初始化对象。
  • 当使用 delete 释放内存时,会自动调用类的析构函数。
class MyClass {
public:
    MyClass() { cout << "Constructor called" << endl; }
    ~MyClass() { cout << "Destructor called" << endl; }
};

MyClass* obj = new MyClass;  // 自动调用构造函数
delete obj;                  // 自动调用析构函数

 mallocfree

  • malloc 不会调用构造函数,仅分配内存;free 不会调用析构函数,仅释放内存。
MyClass* obj = (MyClass*)malloc(sizeof(MyClass));  // 不会调用构造函数
free(obj);                                         // 不会调用析构函数

8.3.内存管理

newdelete

  • new 在分配内存时会计算所需内存的大小,并根据类型自动计算。delete 自动处理内存释放及相关清理工作。

mallocfree

  • malloc 需要明确指定需要分配的字节数,不会考虑对象的类型。free 只能释放 malloccalloc 分配的内存,并且不能自动调用析构函数。
int* p = (int*)malloc(10 * sizeof(int));  // 需要手动计算内存大小

8.4.对象的内存对齐和初始化

newdelete

  • new 会调用类的构造函数进行初始化,并且会适当地进行内存对齐。
  • delete 会释放内存并自动调用析构函数。

mallocfree

  • malloc 只分配原始内存,不会初始化对象。如果需要初始化对象,必须手动进行。
  • free 只会释放内存,而不会调用析构函数。
int* p = new int(5);  // 自动初始化
delete p;              // 自动释放并调用析构函数
特性new/deletemalloc/free
语言C++C
类型安全类型安全,自动推导和转换需要手动类型转换
构造函数/析构函数自动调用构造函数/析构函数不调用构造函数/析构函数
内存分配自动计算内存大小需要手动指定内存大小
内存初始化支持初始化不会初始化内存
使用方式运算符,使用 newdelete函数,使用 mallocfree

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 bytes
    int k[5];       // 5 × 4 bytes = 20 bytes
    char c;         // 1 byte
} DATE;

typedef struct data {
    int cat;        // 4 bytes
    DATE 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 = 6
    int j = 1;  // j = 1

    if (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;      // 返回自增后的值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/956679.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式Linux驱动开发之platform

关键词&#xff1a;rk3399 嵌入式驱动 Linux platform 前言 前面的嵌入式Linux驱动都是描述从特定的SOC与特定设备之间的直接两两通信。而Linux不是为单一某一SOC结构而设计的操作系统&#xff0c;它可以运行在X86、ARM等多种架构多种SOC平台上&#xff0c;如果驱动程序按照S…

KubeSphere部署安装,接入KubeKey安装的k8s集群

KubeSphere安装接入KubeKey安装的k8s集群 文章目录 KubeSphere安装接入KubeKey安装的k8s集群 一.NFS安装配置1.服务器安装NFS服务2.下载并部署 NFS Subdir External Provisioner1).下载部署文件2).创建 NameSpace3).创建 RBAC 资源4).配置 deployment.yaml5).部署 Storage Clas…

从密码学原理与应用新方向到移动身份认证与实践

相关学习资料放下面啦&#xff01; 记得关注❤️&#xff5e;后续分享更多资料 通过百度网盘分享的文件&#xff1a;从密码学原理与应... 链接https://pan.baidu.com/s/1mHpHkvPuf8DUwReQkoYQlw?pwdGza7 提取码&#xff1a;Gza7 复制这段内容打开「百度网盘APP 即可获取」 记…

Java 特殊文件、 properties文件、xml文件

一. 属性文件.properties 1. #注释 2. 内容都是一些键值对信息&#xff0c;每行都是一个键值对&#xff1b;键不能重复&#xff1b; 3. 属性文件的后缀一般都是properties结尾 4. 使用程序读取properties属性文件里面的数据 (1) Properties&#xff1a;是一个Map集合(键值对集合…

抽象设计如何提升用户体验?

抽象设计在网页设计中可以通过多种方式提升用户体验&#xff0c;以下是具体的应用和作用&#xff1a; 一、增强视觉吸引力 视觉冲击力&#xff1a;抽象元素往往具有强烈的视觉冲击力&#xff0c;能够迅速吸引用户的注意力。通过大胆的色彩、不寻常的形状和丰富的纹理&#xff…

MATLAB中while循环例子,for循环嵌套例子

while循环例子 for循环解决斐波那契数列 for循环嵌套例子 注意最后都有 e n d end end

行人识别检测数据集,yolo格式,PASICAL VOC XML,COCO JSON,darknet等格式的标注都支持,准确识别率可达99.5%

作者简介&#xff1a; 高科&#xff0c;先后在 IBM PlatformComputing从事网格计算&#xff0c;淘米网&#xff0c;网易从事游戏服务器开发&#xff0c;拥有丰富的C&#xff0c;go等语言开发经验&#xff0c;mysql&#xff0c;mongo&#xff0c;redis等数据库&#xff0c;设计模…

【vitePress】基于github快速添加评论功能(giscus)

一.添加评论插件 使用giscus来做vitepress 的评论模块&#xff0c;使用也非常的简单&#xff0c;具体可以参考&#xff1a;giscus 文档&#xff0c;首先安装giscus npm i giscus/vue 二.giscus操作 打开giscus 文档&#xff0c;如下图所示&#xff0c;填入你的 github 用户…

JAVA使用自定义注解,在项目中实现EXCEL文件的导出

首先定义一个注解 Retention(RetentionPolicy.RUNTIME) Target(ElementType.FIELD) public interface Excel {/*** 导出时在excel中排序*/int sort() default Integer.MAX_VALUE;/*** 导出到Excel中的名字.*/String name() default "";/*** 首行字段的批注*/String …

有限元分析学习——Anasys Workbanch第一阶段笔记(14)静定与超静定问题、约束类型介绍、简支梁挠度求解和自定义材料库建立

目录 0 序言 1 静定与超静定问题 2 Workbranch中Supports介绍 3 简支梁挠度的有限元求解 4 自定义材料库建立 0 序言 静定与超静定问题、约束类型介绍、简支梁挠度求解和自定义材料库建立(内容对应视频22到24课)。 1 静定与超静定问题 在有限元分析中&#xff0c;不同的…

领域算法 - 大数据处理

大数据处理 文章目录 大数据处理一&#xff1a;hash分流二&#xff1a;双层桶1&#xff1a;什么是双层桶2&#xff1a;双层桶案例 三&#xff1a;外排序1&#xff1a;经典问题2&#xff1a;位图排序法3&#xff1a;多路归并排序 四&#xff1a;bitMap1&#xff1a;添加 -> 异…

以太网实战AD采集上传上位机——FPGA学习笔记27

一、设计目标 使用FPGA实现AD模块驱动采集模拟电压&#xff0c;通过以太网上传到电脑上位机。 二、框架设计 数据位宽转换模块&#xff08;ad_10bit_to_16bit&#xff09;&#xff1a;为了方便数据传输&#xff0c;数据位宽转换模块实现了将十位的 AD 数据转换成十六位&#…

JavaWeb 快速入门 javaScript(预测爆文) | 019

今日推荐语 人经常推翻自己&#xff0c;经常不同意昨天的自己&#xff0c;这也是常态。——纪静蓉 日期 学习内容 打卡编号2025年01月20日JavaWeb快速入门javaScript019 前言 哈喽&#xff0c;我是菜鸟阿康。 今天大概学习了下 js 的的基础知识&#xff0c;js …

[c语言日寄]内存初阶:大端字节序和小端字节序

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…

【MySQL】数据库-图书管理系统(CC++实现)

一.预期功能 该图书管理系统设计提供基本的设计模版&#xff0c;涉及数据库的增删查改等操作&#xff0c;包含登录功能&#xff0c;图书管理功能&#xff0c;图书借阅功能&#xff0c;用户管理功能等基础功能&#xff0c;详细功能查看以下菜单表&#xff0c;共包含三个菜单&am…

Linux-C/C++--深入探究文件 I/O (下)(文件共享、原子操作与竞争冒险、系统调用、截断文件)

经过上一章内容的学习&#xff0c;了解了 Linux 下空洞文件的概念&#xff1b;open 函数的 O_APPEND 和 O_TRUNC 标志&#xff1b;多次打开同一文件&#xff1b;复制文件描述符&#xff1b;等内容 本章将会接着探究文件IO&#xff0c;讨论如下主题内容。  文件共享介绍&…

RabbitMQ-消息可靠性以及延迟消息

目录 消息丢失 一、发送者的可靠性 1.1 生产者重试机制 1.2 生产者确认机制 1.3 实现生产者确认 &#xff08;1&#xff09;开启生产者确认 &#xff08;2&#xff09;定义ReturnCallback &#xff08;3&#xff09;定义ConfirmCallback 二、MQ的持久化 2.1 数据持久…

springboot基于前后端分离的摄影知识网站

Spring Boot 基于前后端分离的摄影知识网站 一、项目概述 Spring Boot 基于前后端分离的摄影知识网站&#xff0c;是一个专为摄影爱好者、专业摄影师打造的知识共享与交流平台。借助 Spring Boot 强大的后端架构搭建能力&#xff0c;结合前端独立开发的灵活性&#xff0c;整合…

B站评论系统的多级存储架构

以下文章来源于哔哩哔哩技术 &#xff0c;作者业务 哔哩哔哩技术. 提供B站相关技术的介绍和讲解 1. 背景 评论是 B站生态的重要组成部分&#xff0c;涵盖了 UP 主与用户的互动、平台内容的推荐与优化、社区文化建设以及用户情感满足。B站的评论区不仅是用户互动的核心场所&…

Linux Bash 中使用重定向运算符的 5 种方法

注&#xff1a;机翻&#xff0c;未校。 Five ways to use redirect operators in Bash Posted: January 22, 2021 | by Damon Garn Redirect operators are a basic but essential part of working at the Bash command line. See how to safely redirect input and output t…