等等等等等等
欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。
C语言标准定义的32个关键字
- 1. 数据类型关键字(12个)
- (1) 声明和定义的区别
- (2) 数据类型关键字
- 2. 控制语句关键字(12个)
- 3. 存储类关键字(5个)
- 4. 其他关键字(3个)
专栏:《精通C语言》
1. 数据类型关键字(12个)
C语言中的数据类型主要有下面几种。实际上,数据类型可以理解为固定大小内存块的别名,给变量指定类型就是告诉编译器给该变量分配多大的内存空间,而变量相当于是内存块的门牌号。
(1) 声明和定义的区别
定义可以看作是声明的一个特例,并非所有的声明都是定义。可以通过是否分配内存来区分定义和声明,定义会建立存储空间,而声名不会建立存储空间。
int function()
{//定义int val; //定义一个变量val,此时会给val分配内存,由数据类型int决定分配多大内存,int为4字节。val = 10; //可以为val赋值。//声明extern int val_2; //声明变量val_2,不会建立内存。//val_2 = 10; //error: 声明不会建立内存,没有内存空间所以无法赋值。return 0;
}
- 定义:定义是指创建一个对象并为这个对象分配一块内存,同时将变量名和这个内存块进行绑定。但是,同一个变量在同一作用域只能定义一次,如果多次定义的话,编译器会提示重定义错误。
- 声明:
- 告诉编译器,某个名称已经被预定了,其他对象/内存块不能再使用这个名称。
- 告诉编译器,某个名称已经绑定好内存块了,该对象是在其他位置定义的,这里用到本名称时不要报错。
(2) 数据类型关键字
char:声明字符型变量。
char类型用于存储一个单一字符,即1字节存储单元。在给char类型变量赋值时需要把值用英文半角单引号’'引起来,存储时并非真正把该字符放到存储空间,而是把该字符对应的ASCII码存放到存储单元中。(也可以把char类型看作是1字节整形)。
ASCII对照表如下
ASCII值 | 控制字符 | ASCII值 | 字符 | ASCII值 | 字符 | ASCII值 | 字符 |
---|---|---|---|---|---|---|---|
0 | NUT | 32 | (space) | 64 | @ | 96 | 、 |
1 | SOH | 33 | ! | 65 | A | 97 | a |
2 | STX | 34 | " | 66 | B | 98 | b |
3 | ETX | 35 | # | 67 | C | 99 | c |
4 | EOT | 36 | $ | 68 | D | 100 | d |
5 | ENQ | 37 | % | 69 | E | 101 | e |
6 | ACK | 38 | & | 70 | F | 102 | f |
7 | BEL | 39 | , | 71 | G | 103 | g |
8 | BS | 40 | ( | 72 | H | 104 | h |
9 | HT | 41 | ) | 73 | I | 105 | i |
10 | LF | 42 | * | 74 | J | 106 | j |
11 | VT | 43 | + | 75 | K | 107 | k |
12 | FF | 44 | , | 76 | L | 108 | l |
13 | CR | 45 | - | 77 | M | 109 | m |
14 | SO | 46 | . | 78 | N | 110 | n |
15 | SI | 47 | / | 79 | O | 111 | o |
16 | DLE | 48 | 0 | 80 | P | 112 | p |
17 | DCI | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 | 50 | 2 | 82 | R | 114 | r |
19 | DC3 | 51 | 3 | 83 | S | 115 | s |
20 | DC4 | 52 | 4 | 84 | T | 116 | t |
21 | NAK | 53 | 5 | 85 | U | 117 | u |
22 | SYN | 54 | 6 | 86 | V | 118 | v |
23 | TB | 55 | 7 | 87 | W | 119 | w |
24 | CAN | 56 | 8 | 88 | X | 120 | x |
25 | EM | 57 | 9 | 89 | Y | 121 | y |
26 | SUB | 58 | : | 90 | Z | 122 | z |
27 | ESC | 59 | ; | 91 | [ | 123 | { |
28 | FS | 60 | < | 92 | / | 124 | | |
29 | GS | 61 | = | 93 | ] | 125 | } |
30 | RS | 62 | > | 94 | ^ | 126 | ` |
31 | US | 63 | ? | 95 | _ | 127 | DEL |
在上面的ASCII码表中,ASCII值0-31表示非打印控制字符,用于控制打印机等外围设备;32-126为打印字符,这些字符在键盘上都可以找到;127表示del命令。
转义字符
转义字符 | 含义 | ASCII码值(十进制) |
---|---|---|
\a | 警报 | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\ | 代表一个反斜线字符"" | 092 |
’ | 代表一个单引号(撇号)字符 | 039 |
" | 代表一个双引号字符 | 034 |
? | 代表一个问号 | 063 |
\0 | 数字0 | 000 |
\ddd | 8进制转义字符,d范围0~7 | 3位8进制 |
\xhh | 16进制转义字符,h范围09,af,A~F | 3位16进制 |
int:声明整型变量。
在C语言标准中并没有明确规定整型数据的长度,整型数据在内存中所占的字节数与操作系统有关系。(一般为4字节)
打印格式 | 含义 |
---|---|
%d | 输出一个有符号的10进制int类型 |
%o | 输出8进制的int类型 |
%x | 输出16进制的int类型,字母以小写输出 |
%X | 输出16进制的int类型,字母以大写写输出 |
%u | 输出一个10进制的无符号数 |
short:声明短整型变量。
长度一般不长于int型数据。(一般为2字节)
long:声明长整型变量。
长度一般不短于int型数据。(Windows为4字节;Linux为4字节(32位),8字节(64位)。)
打印格式 | 含义 |
---|---|
%hd | 输出short类型 |
%d | 输出int类型 |
%l | 输出long类型 |
%ll | 输出long long类型 |
%hu | 输出unsigned short类型 |
%u | 输出unsigned int类型 |
%lu | 输出unsigned long类型 |
%llu | 输出unsigned long long类型 |
float:声明单精度浮点型变量。
浮点型变量也叫做实型变量,用于存储小数数值。float单精度浮点型一般占用4字节存储空间,7位有效数字。
double:声明双精度浮点型变量。
double双精度浮点型精度高于float单精度浮点型,占用8字节存储空间,15-16位有效数字。
浮点型变量存储的是小数,并且浮点型变量的存储单元是有限的,这就导致一个小数有效位以外的数字将被舍去,这样便会出现一些误差。尤其是float单精度浮点型,有时候将一个小数赋值给一个float型变量,然后打印该浮点型变量都会出现和原小数不一致这样的情况。一般使用double双精度可以提升精度,并且在C语言中,一个小数后面不加f则被认为是双精度double类型,只有小数后面加f才表示float类型,比如3.14f。
signed:声明有符号类型变量。
缺省时,编译器默认为signed有符号类型。在计算机中,所有的数据都是以01的二进制形式来存储的,对于有符号数来说如何表示一个数值的正负是一个问题,因此便有了原码、反码和补码。
- 原码:二进制数据的最高位用来作为符号位,1表示负数,0表示正数,剩余位来表示这个数据的数值大小(绝对值),也就是说,负数的原码是在其绝对值原码的基础上将最高位变成0。原码的表示简单易懂,正负数区分方便且易于转换,但是在实际用于计算时却不太方便,当两个正数做减法运算,或者两个异号的数相加时,必须先比较两个数的绝对值大小才能进行减法运算,以便于决定最终结果是正号还是负号。所以,原码表示数据时不便于做加减运算。
- 反码:正数的反码与原码相同;负数的反码是在负数的原码基础上,符号位不变,其它位全部取反。反码的存在一般是为了方便计算补码。
- 补码:对正数来说,原码、反码、补码是完全一致的;对负数来说,补码是在其反码的基础上将整个数加1。在计算机系统中,所有的数值一律用补码的形式来存储。补码的存在主要有这几种意义:
- 统一0的编码。不管是用原码还是反码来表示0,都会有两种表示方式,即正0和负0,但是我们知道,0不区分正负。这就导致同一个数值0出现两种表示方式,而使用补码表示时,对于正0,原码为00000000,反码为00000000,补码为00000000;对于负0,原码为10000000,反码为11111111,补码为11111111+1=00000000,其中最高位(第九位)数字1被舍弃。这样,正0和负0的补码就一样了。
- 便于运算。使用补码进行运算时可以将减法转化为加法,对于任何数的加减运算,都直接使用补码进行加法运算即可,并且可以将符号位和其他位统一处理,当两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位直接舍弃。
unsigned:声明无符号类型变量。
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short | 2字节 | -32768 到 32767 (-2^15 ~ 2^15-1) |
int | 4字节 | -2147483648 到 2147483647 (-2^31 ~ 2^31-1) |
long | 4字节 | -2147483648 到 2147483647 (-2^31 ~ 2^31-1) |
unsigned short | 2字节 | 0 到 65535 (0 ~ 2^16-1) |
unsigned int | 4字节 | 0 到 4294967295 (0 ~ 2^32-1) |
unsigned long | 4字节 | 0 到 4294967295 (0 ~ 2^32-1) |
struct:声明结构体变量。
数组是相同类型数据的集合,而结构体可以把不同的数据组合成一个整体。通过结构体,我们可以把大量的不同类型数据,甚至是函数和其他复合类型数据打包为一个整体。在使用struct关键字时,应区分开结构体类型和结构体变量的区别,声明结构体类型并不会分配内存,只有在定义结构体类型的时候才会分配内存。通常struct关键字会和typedef关键字一块使用,通过别名的方式可以在定义结构体变量时不需要再写struct关键字。
struct st
{int a;char b;
}; //声明结构体类型
struct st s_val = {1, 'a'}; //定义结构体变量,分配内存//定义结构体变量时不能省略struct关键字typedef struct st
{int a;char b;
}_st; //给结构体类型取别名为_st
_st val = {1, 'a'}; //可以不写struct
结构体变量所占的存储空间大小是所有结构体成员所占存储空间大小的总和,并且需要考虑内存对齐方式。而且,空结构体(没有任何成员)也是占存储空间的,空结构体占1字节存储空间。
在结构体中可以包含一种称为柔性数组的成员,柔性数组是一个未知大小的数组,它必须是结构体的最后一个成员,并且柔性数组成员的前面必须有一个其他成员。
struct st
{int val;int arr[0]; //int arr[];
};
这个0长度的数组成员arr是不占存储空间的,这个结构体的大小为4字节。有了这个0长度数组我们便可以方便的扩展这个结构体的大小了
struct st *p_st = (struct st *)malloc(sizeof(struct st) + 10 * sizeof(int));
如上,我们使用包含0长度数组的结构体类型定义一个结构体指针,并通过malloc在堆上为其分配一块内存,这块内存的大小为44字节,而结构体类型大小只有4字节,但是我们却可以像访问普通数组一样通过p_st[i]来访问这块内存。也就是说,柔性数组并不是结构体类型的成员,但是通过结构体成员却可以访问我们自定义的柔性数组存储空间。
最后,在C++中,struct结构体和class类的区别,struct成员默认是public属性,而class的成员默认是private属性。
同样,在C语言中也可以实现C++面向对象的效果,使用struct结构可以实现封装,而结构体做结构体成员又可以实现C++中的继承,并且,函数指针做结构体成员可是模仿C++类中的方法。
union:声明联合数据类型。
联合union是一个能在同一个存储空间存储不同类型数据的类型,也就是说,union的所有成员共享同一块存储空间,同一存储空间段可以用来存放几种不同类型的成员,但每一时刻只有一种起作用。联合体所占的存储空间长度为占用存储空间最大的成员的长度,所以也叫做共用体。共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖。并且,共用体变量的地址和它的各成员的地址都是同一地址。
一个union变量只分配一个足够大的存储空间能够存储最大长度的成员,而不会给每一个成员都分配内存,这是union与struct最大的区别。union主要用来达到节省空间的目的,和struct一样,在C++中,union的成员默认属性为public。
看下面的例子
typedef union
{int data;char buf[2];
}u_t;int main()
{u_t* p, u;memset(&u, 0, sizeof(u));p = &u;p->buf[0] = 0x12;p->buf[1] = 0x34;printf("%x\n", p->data);return 0;
}
对union成员的访问也需要考虑大端存储模式和小端存储模式。
enum:声明枚举类型。
通过enum枚举类型可以定义枚举变量,该枚举变量的值只能是枚举类型中列举出来的那些值。
enum 枚举名
{枚举值表
};
枚举值表中的所有可用值是枚举变量可以使用的值,也成为枚举元素。枚举值是常量,在程序中枚举值不能作为左值(不能给枚举值使用赋值语句赋值)。另外,枚举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1,2 …依次递增,我们也可以显示的给枚举元素赋值。
enum day
{mom = 1,tue, //2wed, //3fri = 5,sat, //6sun //7
};
我们知道使用宏定义#define也可以定义常量,但是宏定义常量和枚举常量是有区别的,#define 宏常量是在预编译阶段进行简单替换,而枚举常量则是在编译的时候确定其值。
void:声明空类型指针(void类型指针可以接受任何类型指针的赋值,无需类型转换),声明函数无返回值或无参数等。
void主要的用途是限制函数的返回值或者函数参数。在C语言中,如果一个函数不加返回值类型限定,那么编译器会默认该函数返回整型值,所以,当一个函数没有返回值的时候,一定要声明为void类型。当函数没有参数时,也应该声明为void。实际上,在C++中函数参数为void表示该函数不接受任何参数,如果调用该函数时添加了参数那么会报错;而C语言中,参数为void的函数可以接受任何类型的参数。为了统一,无论C还是C++,只要函数没有参数,都要显式指明参数为void。
void类型指针可以指向任何类型的内存块,但是使用void类型指针的时候要格外注意。在ANSI标准中,不允许对void类型指针进行加减操作,这是因为指针的步长是由指针的类型决定的。比如
int *p = 0xaa;
p++; //指针类型为int,每次加一移动4字节
这里int类型的指针每次自加一会移动4字节,因为int类型的对象占据的存储空间就是4字节。而void类型的指针在移动时你并不知道它指向的存储空间的大小。但是在GNU标准中是允许对void类型指针进行加减操作的。为了统一,我们可以在对void类型指针进行加减操作时强制类型转换,以此来说明指针移动步长。
void *p;
(int *)p++;
对于函数来说,如果函数的参数可以是任意类型指针,那么可以将函数参数声明为void*类型,比如典型的C语言内存操作函数memset和memcpy函数,内存操作函数所操作的对象是一块内存本身,本就不应该关心这块内存是什么类型,只要我们通过函数参数告诉编译器我们要操作的这块内存的大小就行了,这也是C语言内存操作函数的精髓所在,并且也体现了作为一个内存操作API的统一性。比如
int buf[20];
memset(&buf, 0, 20 * (sizeof(int)));
这句代码的意思是把buf这个数组清0,我们只要把buf这块内存的首地址传给memset函数,并将要清0的这块内存的大小通过参数传入就可以了。
最后,void是一种抽象,可以参考C++中的抽象类来理解。抽象类不能实例化,同样我们也不能去定义一个void类型的变量,因为在定义变量时,编译器要为变量分配内存,而void类型本身就是一种抽象,编译器不知道分配多大内存给这个变量。通常,void类型用于定义一个可以指向任何类型内存块的指针。
2. 控制语句关键字(12个)
if:条件语句。
else:条件语句中的否定分支,在if后使用或作为else if分支。
switch:开关语句。
case:开关语句分支。
case后面的值只能是整型或字符型的常量或者常量表达式。当有较多的case选项时,应该尽量把出现概率更大的case选项放在前面,以提升程序的执行效率。
default:开关语句中的其他分支。
for:循环语句。
do:循环语句中的循环体。
while:循环语句中的条件。
break:跳出循环。
continue:跳出本次循环,进入下一次循环。
goto:无条件跳转。
return:返回语句,可带参数。
return用来终止一个函数,并将return后面的值返回给函数的返回值。在函数内部,当执行到return语句的时候就会终止这个函数,并返回值,return语句后面的程序将不会再被执行。
return返回的值不能是存储在栈上的值(局部变量),因为局部变量在这个函数结束的时候被自动销毁,它的生命周期仅限于这个函数内部,所以不能作为return语句的返回值。
3. 存储类关键字(5个)
auto:声明自动变量,缺省时编译器默认为auto。
默认情况下,缺省时所有变量都是auto的。
extern:声明外部变量。
extern表示外部的,通过extern声明的变量或函数表示该变量或者函数是在外部文件定义的,告诉编译器在本文件中遇到该变量或者函数时,去其他文件中寻找变量或函数的定义。
register:声明寄存器变量。
定义寄存器变量,提高效率。register是建议型的指令,而不是命令型的指令,如果CPU有空闲寄存器,那么register就生效,如果没有空闲寄存器,那么register无效。该关键字请求编译器尽量的将变量存放在CPU内部寄存器中,这样在访问变量时不需要再通过内存寻址的方式访问,而是直接在寄存器中访问,大大提升了访问速度。但是CPU内部寄存器是有限的,所以register关键字只能是尽可能的请求编译器把变量存放在寄存器,而不是一定存放在寄存器。因为register关键字用于请求将数据存放在寄存器,所以使用register修饰符来修饰的变量必须是能被CPU寄存器所接受的类型,即register修饰的变量必须是长度小于或等于整形长度的值。同时,因为register修饰的变量可能会存放在寄存器中(也可能存放在内存中),所以不能对register修饰的变量进行取址操作,即不能通过取址操作符&来获取register修饰变量的地址。
static:声明静态变量。
-
修饰变量
static关键字可以修饰全局变量和局部变量,并且他们都会被存放在内存的静态区。
- 静态全局变量:限定变量的作用域为当前文件,即从变量定义之处开始一直到当前文件末尾,当前文件中该变量定义之前也无法使用(除非加extern声明),其他文件中即便是使用extern声明也无法使用。
- 静态局部变量:定义在函数体内部,并且作用域仅限于当前函数,当前文件该函数体外部无法使用。因为static修饰的静态变量存放在内存的静态区,所以函数运行结束这个静态变量也不会被销毁,函数下次被调用时这个变量的值依然存在,也就是我们说的静态局部变量只能被初始化一次,并且有记忆功能,下次调用函数时可以使用上次函数调用结束时静态局部变量的值。需要注意的是,普通的局部变量存放在栈区,函数调用结束变量就会被析构,也就是说普通局部变量的声明周期为定义该变量的函数体内。而静态局部变量存放在静态区,它的生命周期是整个程序执行期间,也就是说定义该静态局部变量的函数执行完毕,并不会析构静态局部变量,而是在当前程序执行完毕才会析构。
-
修饰函数
使用static关键字修饰函数可以将函数变为静态函数,也成为内部函数,静态函数的作用域为当前文件,在该文件之外无法访问。使用静态函数的好处是可以避免不同文件中函数同名引起的错误,但是会导致该文件之外无法调用的问题。
const:声明只读变量(C和C++区别)。
在C语言中,const定义的并不是真正的常量,而是具有只读属性的变量,其本质还是变量,只不过不可修改(实际上在C语言中是可以通过指针等其他方式间接修改的);而在C++中,const定义的是真正的常量,C++中是通过符号表一一对应的方式实现的。通过下面的例子也可以证明
const int NUM = 10;
char buf[NUM];
上面代码在C语言中编译不通过,但是在C++中编译通过。我们知道,定义数组时要指定数组大小,以便于编译器分配内存。在C语言中编译不通过也就证明了const定义的依然是变量,而不是常量。
编译器通常不会为const只读变量分配存储空间,而是将它们保存在符号表中,这使得它们成为一个编译期间的值,没有读写内存的操作,大大提高了效率。另外需要注意const与宏#define的区别
#define NUM 1 //宏定义一个常量
const int VAL = 2; //还没有将VAL放入内存中
int a = VAL; //此时为VAL分配内存,后面不再分配内存
int b = NUM; //预编译期间进行宏替换,分配内存
int c = VAL; //不会分配内存
int d = NUM; //宏替换,还会分配内存
从汇编的角度来看,const定义的只读变量只是给出了内存地址,而#define给出的是立即数。所以,在程序运行过程中,const定义的只读变量只有一份拷贝(全局只读变量存放在静态区,而不是堆栈),而#define定义的常量在内存中有多份拷贝。#define在预编译的时候进行宏替换,而const只读变量是在编译时确定它的值。另外,#define定义的常量没有类型,而const修饰的只读变量是有类型的。const 修饰的只读变量不能用来作为定义数组的维数,
也不能放在case 关键字后面。
最后,当const修饰指针时,放在不同位置所代表的含义也不同。
const int *p; //const修饰指针指向的内存,//指针本身可变,指针指向的内存不可修改
int const *p; //const修饰指针指向的内存,//指针本身可变,指针指向的内存不可修改int * const p; //const修饰指针本身,//指针指向不可修改,指针指向的内存可以修改const int const *p; //指针本身和指针指向的内存都不可修改
4. 其他关键字(3个)
sizeof:计算一个对象所占的字节数。
sizeof在使用时虽然会加括号,但是他并不是函数,而是一个关键字。实际上,通过sizeof计算一个变量所占的内存大小时可以省略括号,sizeof(val)和sizeof val都可以,但是在计算数据类型的大小时必须加括号sizeof(int),否则的话会和类型扩展混淆,比如unsigned int就是扩展为无符号整型变量。因为sizeof不是函数,所以在使用时不需要包含任何头文件,但是sizeof是有返回值的,范围值类型为size_t,在32位操作系统下是unsigned int类型。
在计算一个字符串变量的大小时要区分sizeof与strlen的区别,strlen是一个函数,用于计算字符串的长度,所以不包含字符串最后的’\n’,而sizeof是计算变量所占内存大小,包括字符串结束符’\n’。
typedef:取别名。
typedef可以为一个数据类型定义一个新的名字,但是不能创建一个新的类型。与#define不同,typedef仅限于为数据类型取别名,而不能为表达式或具体的值取别名。#define发生在预处理阶段,typedef发生在编译阶段。
volatile:防止编译器优化,说明变量在程序执行中可被隐含地改变。
volatile是易变的意思,它修饰的变量表示该变量的值可能被某些因素所修改,比如操作系统、硬件外设或其他线程等等。volatile关键字修饰的变量,编译器不会对改变量进行优化访问。
当我们读取一个普通变量的值时,编译器为了加快访问速度,一般会在缓存中读取该变量的值,而不是直接去寄存器取值。但是,有时候寄存器的值并不是通过程序去修改的,比如嵌入式开发中常用开发板进行开发,很多时候寄存器的值会被芯片的外设所修改。这时候,虽然我们程序中并没有去修改寄存器的值,但是寄存器值却因为外界因素而发生了改变。当我们去访问这种变量的时候,如果不加volatile关键字,编译器默认会在缓存中取值,而此时缓存中的值是一个旧值,变量的真实值已经发生了改变。所以,加volatile关键字就是为了告诉编译器,不要对访问进行优化,每次都应该去变量的地址处去访问变量值,以此来确保每次取到的都是变量的最新值。
下面通过例子说明
int val = 1;
int a = val;
int b = val;
在上面的代码中,变量val没有被用作左值(也就是说在程序中变量val的值没有被显式改变),这时候编译器就会认为变量val的值没有发生过改变,并会对val的访问做优化处理。当给变量a赋值时,编译器取到val的值之后赋给a,并且这个值会被放到缓存中。当给b赋值时,因为编译器认为val的值没有发生改变,所以会直接在缓存中取val的值,而不会去val变量的地址处取值,这样大大提高了访问速度。这么做的前提是,两次访问val的语句之间没有将val当左值的语句(即修改val值的语句)。
如果说将val变量修饰为volatile变量,那就不同了。
volatile int val = 1;
int a = val;
int b = val;
此时,编译器认为val的值是随时可能发生改变的,不管程序中有没有将val当作左值的语句,每次访问val变量都会区val变量的地址处去访问。也就是说,在给a赋值时,编译器将会在val地址处取值,当给b赋值时,编译器依然会去val变量的地址处取值。
一般来说,对寄存器变量、端口数据变量、多线程共享数据变量使用volatile修饰可以保证对变量真实值的稳定访问。
相关文章:
等等等等等等
欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。 …...
JAVA集合篇--深入理解ConcurrentHashMap图解版
一、前言 在Java并发编程中,线程安全的Map实现一直是一个重要话题。虽然我们可以使用Collections.synchronizedMap()或者HashTable来获得线程安全的Map,但它们的性能在高并发场景下往往不尽人意。ConcurrentHashMap作为Java并发包中的重要组件࿰…...
Python嵌套循环
一、前言 在 Python 编程中,嵌套循环(Nested Loops) 是指在一个循环的内部再嵌套另一个循环。这种结构常用于处理多维数据结构(如二维数组、矩阵)、遍历组合数据、图形绘制等场景。 虽然嵌套循环在逻辑上更复杂&…...
linux编译安装nginx
1.到官网(nginx)下载nginx压缩包: 2.以(nginx-1.24.0.tar.gz)为例: 1.上传压缩包至linux服务器: rz 2.解压压缩包nginx-1.24.0.tar.gz: tar -zxvf nginx-1.24.0.tar.gz 3.在安装Nginx之前,需…...
算法-动态规划-钢条切割问题
钢条切割问题是一个经典的动态规划问题,旨在通过切割钢条获得最大收益。以下是详细解释和解决方案: 问题描述 给定长度为 n 的钢条和价格表 p,其中 p[i] 表示长度为 i 的钢条的价格(i 1, 2, ..., n)。目标ÿ…...
Java八股文——系统场景设计
如何设计一个秒杀场景? 面试官您好,设计一个秒杀系统,是对一个工程师综合技术能力的巨大考验。它的核心挑战在于,如何在极短的时间内,应对超高的并发请求,同时保证数据(尤其是库存)…...
如何在FastAPI中玩转GitHub认证,让用户一键登录?
title: 如何在FastAPI中玩转GitHub认证,让用户一键登录? date: 2025/06/22 09:11:47 updated: 2025/06/22 09:11:47 author: cmdragon excerpt: GitHub第三方认证集成通过OAuth2.0授权码流程实现,包含用户跳转GitHub认证、获取授权码、交换访问令牌及调用API获取用户信息四…...
[RPA] 影刀RPA实用技巧
1.给数字添加千分位分隔符 将变量variable的数值(2025.437)添加千分位分隔符,使其变为2,025.437 流程搭建: 关键指令: 2.删除网页元素 将bilibili官网的"动态"图标进行删除 流程搭建: 关键指令: 呈现效果…...
RA4M2开发IOT(7)----RA4M2驱动涂鸦CBU模组
RA4M2开发IOT.7--RA4M2驱动涂鸦CBU模组 概述视频教学样品申请硬件准备参考程序初始化 LSM6DSV16X 传感器初始化单双击识别主程序接口RA4M2接口生成UARTUART属性配置R_SCI_UART_Open()函数原型回调函数user_uart_callback0 ()变量定义更新敲击状态DP同步长按进入配网涂鸦协议解析…...
华为公布《鸿蒙编程语言白皮书》V1.0 版:解读适用场景
6 月 22 日消息,华为现已在其开发者网站上架《鸿蒙编程语言白皮书》V1.0 版本,主要围绕鸿蒙 HarmonyOS 整体框架、适用场景、演进策略、未来愿景四大角度进行阐述,文档访问地址(https://developer.huawei.com/consumer/cn/doc/gui…...
多源异构数据接入与实时分析:衡石科技的技术突破
在数字化转型的浪潮中,企业每天产生的数据量呈指数级增长。这些数据来自CRM系统、IoT设备、日志文件、社交媒体、交易平台等众多源头,格式各异、结构混乱、流速不一。传统的数据处理方式如同在无数孤立的岛屿间划着小船传递信息,效率低下且无…...
多设备Obsidian笔记同步:WebDAV与内网穿透技术高效实现教程
文章目录 前言1. Windows开启Webdav服务2. 客户端测试3. 安装Cpolar内网穿透实现公网访问Webdav4. 同步PC端笔记至WebDav4.1 首先需要在IIS中添加md的格式4.2 在Obsidian中安装第三方插件 5. 同步手机端笔记至WebDav 前言 各位好!在数字化浪潮席卷的当下࿰…...
Linux->进程概念(精讲)
引入:本文会讲到的东西有哪些? 注:要讲就讲清楚,所以从0到懂,目录在右侧 一:冯诺依曼体系结构 1:人物介绍 冯诺依曼是一个伟大的人,他提出了一个体系结构,被命名冯诺依…...
【舞蹈】PC-Dance:姿势可控的音乐驱动舞蹈合成
PC-Dance:姿势可控的音乐驱动舞蹈合成 自监督节奏对齐学习音乐到舞蹈的对齐嵌入-PC-Syn 中,依然怒了一种用于 自适应运动图构建(AMGC)的高效方案,可以基于图的优化效率并保持动作的多样性。 舞蹈合成 整体情况 我们的系统主要由音乐到舞蹈对齐嵌 入网络(M2D-Align)和姿势…...
uni-app项目实战笔记22--图片预览和切换
需求描述: 1、图片预览时,通常需要知道,当前预览的是第几张,总共有多少张图片; 2、当用户左右滑动切换预览图片时,当前预览索引需要随着进行切换。 下面简单介绍下实现过程: 1、在图片列表页…...
[特殊字符] AIGC工具深度实战:GPT与通义灵码如何彻底重构企业开发流程
🔍 第一模块:理念颠覆——为什么AIGC不是“玩具”而是“效能倍增器”? ▍企业开发的核心痛点图谱(2025版) 研发效能瓶颈:需求膨胀与交付时限矛盾持续尖锐,传统敏捷方法论已触天花板…...
华为OD机考-用户调度问题-DP(JAVA 2025B卷)
import java.util.Scanner;public class UserScheduling {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt(); // 用户个数int[][] costs new int[n][3]; // 存储每个用户使用A/B/C策略的系统消耗for (int i 0; i …...
【论文阅读 | CVPR 2024 |Fusion-Mamba :用于跨模态目标检测】
论文阅读 | CVPR 2024 |Fusion-Mamba :用于跨模态目标检测 1.摘要&&引言2.方法2.1 预备知识2.2 Fusion-Mamba2.2.1 架构特征提取与多模态融合(FMB模块)FMB的应用与输出2.2.2 关键组件3.2.2.1 SSCS 模块:浅层跨模态特征交互…...
Python 数据分析与可视化 Day 4 - Pandas 数据筛选与排序操作
🎯 今日目标 掌握 Pandas 中 groupby() 的使用方式学会使用 agg() 方法进行多个聚合掌握 pivot_table() 构建透视表结合分组与排序进行更深入的分析 🧮 一、基本分组统计(groupby) ✅ 分组 单列聚合 df.groupby("性别&qu…...
基于Vue.js的图书管理系统前端界面设计
一、系统前端界面设计要求与效果 (一)系统功能结构图 设计一个基于Vue.js的图书管理系统前端界面。要充分体现Vue的核心特性和应用场景,同时结合信息管理专业的知识。要求系统分为仪表盘、图书管理、借阅管理和用户管理四个主要模块&#x…...
FPGA故障注入测试软件使用指南
有数字芯片之母别称的FPGA,是国内在半导体行业率先取得重大突破的细分赛道,正迎来技术和市场形成共振的黄金发展期。 国内拥有最多的应用设计工程师与新兴从业人员,但到目前为止,还没有一款位流级别的专用EDA软件,服务用户日常应用开发所需的调试验证工作。 第一大厂商赛…...
Oracle 数据库查询:单表查询
作者:IvanCodes 日期:2025年6月22日 专栏:Oracle教程 在 Oracle 数据库操作中,查询数据是最频繁、最核心的操作之一。单表查询,即仅从一个表中检索信息,是所有复杂查询的基础。本笔记将系统梳理单表查询的关…...
【DDD】——带你领略领域驱动设计的独特魅力
🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大三学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL࿰…...
阿里云CentOS系统搭建全攻略:开启云端技术之旅
前期准备:开启云端征程前的必备事项 在当今数字化时代,云计算已成为企业和开发者构建应用和服务的重要基础设施。阿里云作为全球领先的云计算服务提供商,提供了丰富的云计算产品和服务,其中 CentOS 系统在阿里云上的应用非常广泛…...
Flink图之间流转解析:从逻辑构建到物理执行的深度剖析
在Flink强大的数据处理体系中,Table Connectors实现了与外部结构化数据的高效交互,而Flink作业从代码到实际执行的背后,是各类图结构之间的流转与转换。这些图结构承载着作业的逻辑定义、任务划分与资源调度等关键信息,其流转过程…...
详解Redis数据库和缓存不一致的情况及解决方案
数据库与缓存不一致是分布式系统中常见问题,本质是数据在缓存层和存储层出现版本差异。 一、并发写操作导致不一致(最常见) 场景描述 线程A更新数据库 → 线程B更新数据库 → 线程B更新缓存 → 线程A更新缓存 结果:缓存中存储的…...
【CSS】CSS3媒体查询全攻略
媒体查询教程 媒体查询(Media Queries)是CSS3中引入的强大功能,允许内容根据设备特性(如屏幕尺寸、分辨率、方向等)进行自适应调整。以下是媒体查询的详细教程: 基本语法 media mediatype and (media feature) {/* CSS规则 */ }常用媒体类型 all - 所…...
深入理解Spring的ResponseBodyAdvice接口
什么是ResponseBodyAdvice? ResponseBodyAdvice是Spring框架4.2版本引入的一个非常有用的接口,它允许我们在控制器方法执行后、响应体写入前对响应进行统一处理。这个接口为开发者提供了对返回数据进行统一拦截和修改的能力,是Spring MVC响应处理流程中…...
C++法则5: 在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。
C法则5: 在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。 在 C 中,法则5指的是:当函数参数是非引用类型(即按值传递)时,传递给函数的实参会进行拷贝初始化(copy initializ…...
Python 使用 Requests 模块进行爬虫
目录 一、请求数据二、获取并解析数据四、保存数据1. 保存为 CSV 文件2. 保存为 Excel 文件打开网页图片并将其插入到 Excel 文件中 五、加密参数逆向分析1. 定位加密位置2. 断点调试分析3. 复制相关 js 加密代码,在本地进行调试(难)4. 获取 …...
day039-nginx配置补充
文章目录 0. 老男孩思想-如何提升能力?1. nginx登录认证功能1.1 创建密码文件1.2 修改子配置文件1.3 重启服务 2. nginx处理请求流程3. 配置默认站点4. location 命令5. 案例1-搭建大型直播购物网站5.1 配置本地hosts解析5.2 编写子配置文件5.3 创建相关目录/文件并…...
K8s入门指南:架构解析浓缩版与服务间调用实战演示
目录 前言一、k8s概念理解1、k8s整体架构(1) Master 主节点(2) Node 工作节点(3) Etcd 键值存储数据库 2、Pod被视为最小的部署单元3、k8s的五种控制器类型(1)…...
如何用AI开发完整的小程序<10>—总结
通过之前9节的学习。 如何用Ai制作一款简单小程序的内容就已经都介绍完了。 总结起来就以下几点: 1、搭建开发制作环境 2、创建页面(需要手动) 3、在页面上制作UI效果(让Ai搞,自己懂了后可以自己调) 4…...
Javaweb - 3 CSS
CSS 层叠样式表(Cascading Style Sheets),能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力。 简单来说,HTML 搭建一个毛坯房,C…...
【算法】【优选算法】优先级队列
目录 一、1046.最后一块石头的重量二、703. 数据流中的第 K 大元素三、692. 前 K 个⾼频单词四、295. 数据流的中位数 一、1046.最后一块石头的重量 题目链接:1046.最后一块石头的重量 题目描述: 题目解析: 题意就是让我们拿出提供的数组…...
PaddleOCR + Flask 构建 Web OCR 服务实战
1、前言 随着图像识别技术的发展,OCR(光学字符识别)已经成为很多应用场景中的基础能力。PaddleOCR 是百度开源的一个高性能 OCR 工具库,支持中英文、多语言、轻量级部署等特性。 而 Flask 是一个轻量级的 Python Web 框架,非常适合快速构建 RESTful API 或小型 Web 应用…...
openapi-generator-maven-plugin自动生成HTTP远程调用客户端
Java开发中调用http接口的时候,有很多可选的技术方案,比如:HttpURLConnection、RestTemplate、WebClient、Feign、Retrofit、Okhttp等,今天我们来看一个更优的技术方案OpenAPI Generator(http://openapi-generator.tech/) OpenAP…...
ms-swift 部分命令行参数说明
参考链接 命令行参数 — swift 3.6.0.dev0 文档 Qwen Chat num_train_epochs 训练的epoch数,默认为3 假设你有 1000 条训练样本,并且设置了: num_train_epochs 3 这意味着: 模型会完整地遍历这 1000 条数据 3 次。每一次…...
【学习笔记】深入理解Java虚拟机学习笔记——第10章 前端编译与优化
第10章 前端编译与优化 10.1 概述 1>前端编译器:Javac命令。 【.java文件->.class文件】 2>即时编译器:Hotspot.C1.C2 【.class文件->机器码】 3>提前编译器:JDK的Jaotc等【.java->机器码】 10.2 Javac 编译器 10.2.1 …...
删除node并且重装然后重装vue
参考第一篇文章 node.js卸载与安装超详细教程_node卸载重装-CSDN博客 第二篇文章安装vue Vue安装与配置教程(非常详细)_安装vue-CSDN博客...
Flink源码阅读环境准备全攻略:搭建高效探索的基石
想要深入探索Flink的底层原理,搭建一套完整且适配的源码阅读环境是必经之路。这不仅能让我们更清晰地剖析代码逻辑,还能在调试过程中精准定位关键环节。接下来,结合有道云笔记内容,从开发工具安装、源码获取导入到调试配置&#x…...
【破局痛点,赋能未来】领码 SPARK:铸就企业业务永续进化的智慧引擎—— 深度剖析持续演进之道,引领数字化新范式
摘要 在瞬息万变的数字时代,企业对业务连续性、敏捷创新及高效运营的需求日益迫切。领码 SPARK 融合平台,秉持“持续演进”这一核心理念,以 iPaaS 与 aPaaS 为双擎驱动,深度融合元数据驱动、智能端口调度、自动化灰度切换、AI 智…...
Flink SQL Connector Kafka 核心参数全解析与实战指南
Flink SQL Connector Kafka 是连接Flink SQL与Kafka的核心组件,通过将Kafka主题抽象为表结构,允许用户使用标准SQL语句完成数据读写操作。本文基于Apache Flink官方文档(2.0版本),系统梳理从表定义、参数配置到实战调优…...
Linux 服务器运维:磁盘管理与网络配置
🤵♂️ 个人主页:布说在见 ✍🏻作者简介: 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏…...
PyTorch 入门学习笔记
目录 1 张量 1)张量的初始化和属性 2)张量操作 3)使用 NumPy 进行桥接 2 torch.autograd 1)背景 2)在 PyTorch 中的使用 3)Autograd 的微分机制 4)计算图原理 3 神经网络 1ÿ…...
9大策略深度解析MySQL多表JOIN性能优化
一、多表JOIN的现实挑战 在实际开发中,MySQL多表JOIN场景主要源于两类场景: • 历史遗留系统:老代码中未严格遵循范式设计的SQL语句• 数据库迁移:从Oracle迁移至MySQL时保留的复杂关联查询 这类操作潜藏多重风险: …...
CSS 逐帧动画
CSS 逐帧动画实现指南 逐帧动画(frame-by-frame animation)是一种通过快速连续显示一系列静态图像来创造运动效果的技术。以下是使用CSS实现逐帧动画的几种方法。 1. 使用 steps() 计时函数 这是实现逐帧动画最常用的方法,通过animation-timing-function的steps(…...
UE5 游戏模板 —— ThirdPersonGame
UE5 游戏模板 —— ThirdPersonGame 前言一、初始化旋转控制参数1.参数一2.参数二3.参数三4.参数四 二、输入系统总结 前言 有了前面的铺垫,第三人称模板简直是手到擒来了,我们只需要注意一些初始化的变量是做什么的即可,因为UE的Character …...
java中关于异步转同步的一些解决方案的对比与思考。【spring mvc堵塞式】
文章目录 1、Spring MVC堵塞式编程中的技术方案a) 最简单的方案,使用 DeferredResult 代码如下,代码解读:最终控制台输出如下。用户收到的结果 b) 上点难度,使用redis监听事件,根据事件的不同返回不同的数据…...
【数据结构与算法】数据结构核心概念系统梳理
第一章 绪论:基础概念体系 🚩算法:问题求解步骤的描述。 🚩非递归的算法效率更高。 1.1 逻辑结构 vs 存储结构 维度逻辑结构存储结构(物理结构)定义数据元素之间的逻辑关系数据结构在计算机中的实现方式分类线性/树形/图/集合顺序/链式/索引/散列独立性独立于存储结构…...