C语言关键字

📅 发布时间:2026/8/21 21:09:08
C语言关键字 本文将主要介绍一些C语言关键字一、关键字概览关键字含义说明typedef声明一种新类型替换一个已有的类型static改变变量或函数的生命周期或作用域const类型限定符volatile明确变量可能被其他方式改变每次访问必须从内存重新获取#define定义一个宏二、关键字1、typedef用来声明一种新类型来替换一个已有的类型方便移植代码语法typedef 现有类型 新类型别名;//一般写在文件的开头预处理命令的下面用法1基础类型起别名typedefunsignedintu32;//这句话之后u32就代表unsigned intu32 a8;//等价定义unsigned int a8;用法2给结构体、共用体起别名typedefstructstudent{intid;charname[20];}Student;//此时定义的Student等价struct studentStudent zhangsan;用法3给数组起别名格式typedef 原类型 别名[数组大小]typedefintI[5];//此时I就是一种类型代表int [5]typedefintII[3][4];//二维数组I test_arr_1{1,2,3,4,5};for(inti0;i5;i){printf(%d ,test_arr_1[i]);}printf(数组test_arr的长度是%d\n,sizeof(I));II test_arr_2{1,2,3,4,5,6,7,8,9,10,11,12};printf(\n %d\n,test_arr_2[2][2]);运行结果用法4给函数指针类型起别名void(*p)(int,int);//定义了一个函数指针*p这个指针指向不带返回值带两个int类型参数的函数typedefvoid(*s)(int,int);//s就是void(int,int) *s b;//等价void* b(int,int),定义了一个函数指针b这个指针指向不带返回值带两个int类型参数的函数。理解过程p的类型是voidintint*typedef voidintint* s括号往后移typedef void *sintint2、static修饰局部变量可延长生存周期修饰全局变量和函数使其作用域只在本文件中有效。已初始化的static变量存储在data段未初始化的static变量存储在bss段。详解可参考https://blog.csdn.net/qq_44801116/article/details/1632621193、constconst是C语言中类型限定符type qualifier用于在编译器限定一个对象为只读不可修改。它让代码更安全可读性更强有助于编译器优化。const修饰的变量叫常变量其值不能通过变量名修改但是可以通过指针或别名间接修改行为未定义应避免const赋予变量只读属性必须在定义时初始化除了extern声明const修饰普通变量//const和类型是可以互换位置以下两者等效intconsta;constinta;const与指针可有四种组合写法含义const int *p常量指针指针可变指向的数据不可变int const *p同上int* const p指针常量指针不可变指向的数据可变const int* const p指向常量整数的常量指针指针和数据均不可变const优先和类型结合和类型结合时表示该类型的数据为常量第二优先和指针结合说明该指针是一个常量。const int *p的例子intx,y;constint*px;//常量指针从编译层次约束不能通过*p进行修改xx5;//数据本身是可以修改的py;//指针可以更换为其他常量int * const p的例子intx,y;int*constpx;//这个指针只能指向这个常量从编译层次约束自指针初始化起不能更改指针指向的位置。py;//是不可取的*p5;//是可行的4、volatile编译器为了提速会做缓存变量放在RAMCPU会把变量拷贝到CPU高速缓存/寄存器里后续直接读寄存器副本不去真正读内存。volatile的作用就是告诉编译器这个变量不要做寄存器缓存优化每次读写都必须老老实实访问原始内存不许用寄存器里面的副本。1典型场景1硬件寄存器单片机外设寄存器硬件会自动修改内存值ADC采样、IO口状态、中断标志寄存器软件没有任何赋值语句加上volatile之后每次都会重新访问硬件寄存器真是地址。//#defineREG_IO_STATUS*volatileunsignedint*0x40001000Uwhile((REG_IO_STATUS0x01)0){//等待硬件bit00进行相应操作//若不加volatile编译器优化只读取一次寄存器的值放到CPU寄存器陷入死循环}2典型场景2单核单片机中断和主循环共享全局标志volatileintflag0;//中断里面修改flagvoidEXIT_ISRHandler(void){flag1;}intmain(){while(flag0){printf(中断flag0);//只要flag非0这个循环就会退出---但是实际可能其他代码已更改但是此处取的仍是flag0}//do somethingreturn0;}主循环读变量中断函数里面修改flag变量。不加volatile编译器优化把flag载入CPU寄存器主循环永远看不到中断修改后的内存新值死循环。多线程也会遇到类似问题但volatile不能替代互斥锁只阻止编译器优化不解决CPU多核缓存一致性问题。单片机单核环境volatile够用。3典型场景3内存会被外界意外修改例如通过指针强行修改栈上const局部变量给变量加上volatile可以阻止编译器优化每次从内存读取。intmain(){volatileconstinta100;int*p(int*)a;*p200;printf(%d\n,a);//会读取内存打印200不加volatile编译器用常量传播会打印100return0;}使用注意事项volatile不会防止变量被优化删除不是原子操作egcnt是读-修改-写三条指令中断可以插在中间依然会出现数据错乱。不能用来做计数器的线程/中断安全需要关中断或者原子指令linux/Windows多核多线程volatile不能保证线程安全不要随便到处加volatile。5、define-定义一个宏宏一个批量处理的谓称是简单的替换不作为表达式和计算使用宏可以提高代码的移植性和通用性。语法#define 宏名 要替换的内容1不带参数的宏#definePI3.14159262带参数的宏格式#define 宏名(参数1,参数2,…) 替换文本#defineADD(a,b)((a)(b))//宏名和括号之间不能有空格否则会变成不带参数的宏把后面的参数当作替换文本intmain(){intrADD(1,2);printf(%d\n,r);return0;}运行结果宏是纯文本直接替换没有计算没有类型所以每个参数都要套小括号看下面的例子可看到效果#includestdio.h#defineMUL(a,b)(a*b)intmain(){intrMUL(12,34);printf(%d\n,r);return0;}显然上面的结果与预期是不一致的当我们给每一个变量加小括号后宏参数不要传入带自增或者自减有副作用详细参看下面的例子#includestdio.h#includewindows.h#includemath.h#defineMAX(a,b)((a)(b)?(a):(b))intmain(){SetConsoleOutputCP(65001);SetConsoleCP(65001);inta2;intb3;printf(执行前a%db%d\n,a,b);intrMAX(a,b);printf(执行后a%db%d\n,a,b);printf(执行后r%d\n,r);return0;}运行结果上面的宏定义如果通过typeof锁死参数则会发生不一样的结果#defineMAX(a,b)(\{typeof(a)_aa;\typeof(b)_bb;\(_a)(_b)?(_a):(_b);\}\)\代表在此处不换行的意思因为宏定义只替换一行的内容上面内容为一行()表示括号里的内容是一个整体{}大括号里面表示语句块的集合;分号代表语句的结束运行后结果如下说明预处理阶段文本替换没有函数调用开销没有类型检查