嵌入式知识点总结 C/C++ 专题提升(一)-关键字

news2025/1/20 23:15:09

 针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。

目录

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

重塑商业智能:大数据改变商业的十种方式

在过去几年间&#xff0c;大数据一直在改变许多公司的运营方式。大数据指的是大量且多样的数据集&#xff0c;当这些数据被妥善收集和分析时&#xff0c;人们能够从中获取有价值的洞察信息。随着大数据逐渐应用于中小型企业&#xff0c;它有望彻底变革企业运营模式。以下将介绍…

基于Spring Boot的车间调度管理系统

基于 Spring Boot 的车间调度管理系统 一、系统概述 基于 Spring Boot 的车间调度管理系统是一个为制造企业车间生产活动提供智能化调度和管理解决方案的软件系统。它利用 Spring Boot 框架的便捷性和高效性&#xff0c;整合车间内的人员、设备、物料、任务等资源&#xff0c…

Ubuntu 24.04 LTS 安装 tailscale 并访问 SMB共享文件夹

Ubuntu 24.04 LTS 安装 tailscale 安装 Tailscale 官方仓库 首先&#xff0c;确保系统包列表是最新的&#xff1a; sudo apt update接下来&#xff0c;安装 Tailscale 所需的仓库和密钥&#xff1a; curl -fsSL https://tailscale.com/install.sh | sh这会自动下载并安装 …

Ubuntu 22.04 TLS 忘记root密码,重启修改的解决办法

1.想办法进入这个界面&#xff0c;我这里是BIOS引导的是按Esc按一下就行&#xff0c;UEFI的貌似是按Shift不得而知&#xff0c;没操作过。下移到Advanced options for Ubuntu&#xff0c;按enter 2.根据使用的内核版本&#xff0c;选择带「recovery mode」字样的内核版本&#…

故障诊断 | BWO白鲸算法优化KELM故障诊断(Matlab)

目录 效果一览文章概述BWO白鲸算法优化KELM故障诊断一、引言1.1、研究背景及意义1.2、故障诊断技术的现状1.3、研究目的与内容二、KELM基本理论2.1、KELM模型简介2.2、核函数的选择2.3、KELM在故障诊断中的应用三、BWO白鲸优化算法3.1、BWO算法基本原理3.2、BWO算法的特点3.3、…

TCP状态转移图详解

状态 描述 LISTEN represents waiting for a connection request from any remote TCP and port. SYN-SENT represents waiting for a matching connection request after having sent a connection request. SYN-RECEIVED represents waiting for a confirming connect…

LabVIEW 水电站厂内经济运行系统

基于 LabVIEW 的水电站经济运行系统&#xff0c;主要针对农村小水电站运行管理的不足进行改进&#xff0c;通过精确控制发电与用水量&#xff0c;最小化耗水量并优化负荷分配&#xff0c;提升水电站的运营效率和经济效益。 ​ LabVIEW 在系统中的功能特点 强大的图形化编程环…

蓝桥杯训练—矩形面积交

文章目录 一、题目二、示例三、解析四、代码 一、题目 平面上有两个矩形&#xff0c;它们的边平行于直角坐标系的X轴或Y轴&#xff0c;对于每个矩形&#xff0c;我们给出它的一对相对顶点的坐标&#xff0c;请你编程写出两个矩形的交的面积 输入格式&#xff1a; 输入包含两行…

Flask简介与安装以及实现一个糕点店的简单流程

目录 1. Flask简介 1.1 Flask的核心特点 1.2 Flask的基本结构 1.3 Flask的常见用法 1.3.1 创建Flask应用 1.3.2 路由和视图函数 1.3.3 动态URL参数 1.3.4 使用模板 1.4 Flask的优点 1.5 总结 2. Flask 环境创建 2.1 创建虚拟环境 2.2 激活虚拟环境 1.3 安装Flask…

基于机器学习的电信用户流失预测与数据分析可视化

完整源码项目包获取→点击文章末尾名片&#xff01; 背景描述 根据IBM商业社区分享团队描述&#xff0c;该数据集为某电信公司在加利福尼亚为7000余位用户&#xff08;个人/家庭&#xff09;提供电话和互联网服务的相关记录。描述用户基本情况&#xff0c;包括每位用户已注册的…

InVideo AI技术浅析(五):生成对抗网络

一、特效生成 1. 工作原理 特效生成是计算机视觉中的高级应用,旨在通过算法生成高质量的视觉特效,如风格迁移、图像到图像的翻译等。InVideo AI 使用生成对抗网络(GAN)来实现这一功能。GAN 通过生成器和判别器两个网络的对抗训练,生成逼真的视觉特效。 2. 关键技术模型…

Linux操作系统的灵魂,深度解析MMU内存管理

在计算机的奇妙世界里&#xff0c;我们每天使用的操作系统看似流畅自如地运行着各类程序&#xff0c;背后实则有着一位默默耕耘的 “幕后英雄”—— 内存管理单元&#xff08;MMU&#xff09;。它虽不常被大众所熟知&#xff0c;却掌控着计算机内存的关键命脉&#xff0c;是保障…

线性代数概述

矩阵与线性代数的关系 矩阵是线性代数的研究对象之一&#xff1a; 矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合&#xff0c;是线性代数中的核心概念之一。矩阵的定义和性质构成了线性代数中矩阵理论的基础&#xff0c;而矩阵运算则简洁地表示和…

Reactor 模式在 Edis、Nginx 和 Netty 中的应用与高性能网络模式解析

文章目录 参考文章Reactor 模式在 Edis、Nginx 和 Netty 中的应用与高性能网络模式解析一、Reactor 模式二、Redis 中的 Reactor 模式三、Nginx 中的 Reactor 模式四、Netty 中的 Reactor 模式五、Reactor 模式的优势六、总结 参考文章 redis&#xff0c;nginx&#xff0c;net…

企业级NoSQL数据库Redis

1.浏览器缓存过期机制 1.1 最后修改时间 last-modified 浏览器缓存机制是优化网页加载速度和减少服务器负载的重要手段。以下是关于浏览器缓存过期机制、Last-Modified 和 ETag 的详细讲解&#xff1a; 一、Last-Modified 头部 定义&#xff1a;Last-Modified 表示服务器上资源…

【自动驾驶BEV感知之Transformer】

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 本文为深蓝学院《BEV感知理论与实践》 的学习笔记 以图书馆看书举例 query&#xff1a;查询&#xff0c;感兴趣的东西 Key&#xff1a;索引&…

http转化为https生成自签名证书

背景 项目开发阶段前后交互采用http协议&#xff0c;演示环境采用htttps协议 &#xff0c;此处为个人demo案例 组件 后端&#xff1a;springBoot 前端&#xff1a;vue web 服务&#xff1a;tomcat 部署环境&#xff1a;linux 生成自签名证书 创建目录 存储证书位置 # mkdir -p…

AAPM:基于大型语言模型代理的资产定价模型,夏普比率提高9.6%

“AAPM: Large Language Model Agent-based Asset Pricing Models” 论文地址&#xff1a;https://arxiv.org/pdf/2409.17266v1 Github地址&#xff1a;https://github.com/chengjunyan1/AAPM 摘要 这篇文章介绍了一种利用LLM代理的资产定价模型&#xff08;AAPM&#xff09;…

Unity HybridCLR Settings热更设置

需要热更的程序集放到 热更新Assembly Definitions中。 记得补充元数据AOT dlls&#xff08;在热更新程序集的就不用补充元数据了&#xff09; 打包完成后遇到TypeLoadException: could not load type 时 可能需要在Assets/link.xml中增加对应的设置 <assembly fullname&q…

PyTest自学-认识PyTest

1 PyTest自学-认识PyTest 1.1 PyTest可以用来做什么&#xff1f; PyTest是一个自动化测试框架&#xff0c;支持单元测试和功能测试&#xff0c;有丰富的插件&#xff0c;如&#xff0c;pytest-selemium, pytest-html等。 1.2 安装pytest 使用pip install -U pytest。 1.3 py…