
耕耘 :C、C、嵌入式技术领域我的个人主页❄️个人专栏《C语言专栏》 《嵌入式专栏》✨**不要等待机会而要创造机会**✨博主简介:✨✨一位热爱生活的阳光大男孩.✨✨前言本文深入解析C语言中sizeof与strlen的本质区别结合数组、指针等经典笔试题剖析其在内存计算与字符串处理中的应用。同时系统讲解预定义符号、#define常量与宏的使用规范揭示宏参数副作用、运算符优先级及##、#等高级用法并对比宏与函数的优劣强调命名约定与#undef、命令行定义等实用技巧助力读者掌握C语言底层精髓。文章目录前言1. 预定义符号2. #define 定义常量3. #define定义宏4. 带有副作⽤的宏参数5. 宏替换的规则6. 宏函数的对⽐7. #和##7.1 #运算符7.28. 命名约定9. #undef10. 命令⾏定义11. 条件编译结语1. 预定义符号C语⾔设置了⼀些预定义符号可以直接使⽤预定义符号也是在预处理期间处理的。__FILE__//进⾏编译的源⽂件__LINE__//⽂件当前的⾏号__DATE__//⽂件被编译的⽇期__TIME__//⽂件被编译的时间__STDC__//如果编译器遵循ANSI C其值为1否则未定义举个例子intmain(){printf(file:%s line:%d\n,__FILE__,__LINE__);//当前源文件的路径和文件当前的行号return0;}2. #define 定义常量基本语法#definenamestuff举个例子#defineMAX1000#defineregregister//为 register这个关键字创建⼀个简短的名字// register用于建议编译器将变量存储在处理器的寄存器中以提高访问速度#definedo_foreverfor(;;)//⽤更形象的符号来替换⼀种实现#defineCASEbreak;case//在写case语句的时候⾃动把 break写上。// 如果定义的 stuff过⻓可以分成⼏⾏写除了最后⼀⾏外每⾏的后⾯都加⼀个反斜杠(续⾏符)。#defineDEBUG_PRINTprintf(file:%s\tline:%d\t \ date:%s\ttime:%s\n,\__FILE__,__LINE__,\__DATE__,__TIME__)思考在define定义标识符的时候要不要在最后加上 ; ?答案建议不要例如#defineMAX1000;if(condition)maxMAX;//这里的max的值为 1000elsemax0;如果是加了分号的情况等替换后if和else之间就是2条语句⽽没有⼤括号的时候if后边只能有⼀条语句。这⾥会出现语法错误3. #define定义宏#define 机制包括了⼀个规定允许把参数替换到⽂本中这种实现通常称为宏macro或定义宏define macro。下⾯是宏的申明⽅式#definename(parament-list)stuff其中的 parament-list 是⼀个由逗号隔开的符号表它们可能出现在stuff中。注意参数列表的左括号必须与name紧邻如果两者之间有任何空⽩存在参数列表就会被解释为stuff的⼀部分。举例#defineSQUARE(x)x*x//加法宏这个宏接收⼀个参数 x .如果在上述声明之后你把 SQUARE( 5 ); 置于程序中预处理器就会⽤下⾯这个表达式替换上⾯的表达式 5 * 5问题代码#defineSQUARE(x)x*xinta5;printf(%d\n,SQUARE(a1));//替换后的表达式是a 1 * a 1为了更加规范书写,建议在宏定义表达式两边都要加上括号。例如#defineDOUBLE(x)((x)(x))提⽰所以⽤于对数值表达式进⾏求值的宏定义都应该⽤这种⽅式加上括号避免在使⽤宏时由于参数中的操作符和邻近操作符之间不可预料的相互作⽤。4. 带有副作⽤的宏参数当宏参数在宏的定义中出现超过⼀次的时候如果参数带有副作⽤那么你在使⽤这个宏的时候就可能出现危险导致不可预测的后果。副作⽤就是表达式求值的时候出现的永久性效果。例如x1;//不带副作⽤x;//带有副作⽤MAX宏可以证明具有副作⽤的参数所引起的问题。#defineMAX(a,b)((a)(b)?(a):(b))...x5;y8;zMAX(x,y);printf(x%d y%d z%d\n,x,y,z);//输出的结果是什么预处理器处理之后的结果是什么z((x)(y)?(x):(y));所以输出的结果是x6 y10 z95. 宏替换的规则在程序中扩展#define定义符号和宏时需要涉及⼏个步骤。1.在调⽤宏时⾸先对参数进⾏检查看看是否包含任何由#define定义的符号。如果是它们⾸先被替换。2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏参数名被他们的值所替换。3. 最后再次对结果⽂件进⾏扫描看看它是否包含任何由#define定义的符号。如果是就重复上述处理过程。注意1.宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏不能出现递归。2. 当预处理器搜索#define定义的符号的时候字符串常量的内容并不被搜索。6. 宏函数的对⽐宏通常被应⽤于执⾏简单的运算。⽐如在两个数中找出较⼤的⼀个时写成下⾯的宏更有优势⼀些。#defineMAX(a,b)((a)(b)?(a):(b))那为什么不⽤函数来完成这个任务原因有⼆1.⽤于调⽤函数和从函数返回的代码可能⽐实际执⾏这个⼩型计算⼯作所需要的时间更多。所以宏⽐函数在程序的规模和速度⽅⾯更胜⼀筹。2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使⽤。反之这个宏怎可以适⽤于整形、⻓整型、浮点型等可以⽤于 来⽐较的类型。宏的参数是类型⽆关的。和函数相⽐宏的劣势1.每次使⽤宏的时候⼀份宏定义的代码将插⼊到程序中。除⾮宏⽐较短否则可能⼤幅度增加程序的⻓度。3. 宏是没法调试的。4. 宏由于类型⽆关也就不够严谨。5. 宏可能会带来运算符优先级的问题导致程容易出现错宏有时候可以做函数做不到的事情。⽐如宏的参数可以出现类型但是函数做不到。#defineMALLOC(num,type)\(type)malloc(numsizeof(type))...//使⽤MALLOC(10,int);//类型作为参数//预处理器替换之后(int*)malloc(10sizeof(int));7. #和##7.1 #运算符#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。#运算符所执⾏的操作可以理解为”字符串化“。当我们有⼀个变量 int a 10; 的时候我们想打印出 the value of a is 10 .就可以写#definePRINT(n)printf(the value of #n is %d,n);//这里的n就可以随便替换了intmain(){inta10;PRINT(a)return0;}7.2##运算符## 可以把位于它两边的符号合成⼀个符号它允许宏定义从分离的⽂本⽚段创建标识符。 ## 被称为记号粘合例如//宏定义#defineGENERIC_MAX(type)\type type##_max(type x,type y)\{\return(xy?x:y);\}GENERIC_MAX(int)//替换到宏体内后int##_max ⽣成了新的符号 int_max做函数名GENERIC_MAX(float)//替换到宏体内后float##_max ⽣成了新的符号 float_max做函数名intmain(){//调⽤函数intmint_max(2,3);printf(%d\n,m);floatfmfloat_max(3.5f,4.5f);printf(%f\n,fm);return0;}8. 命名约定⼀般来讲函数和宏的使⽤语法很相似。所以语⾔本⾝没法帮我们区分⼆者。那我们平时的⼀个习惯是把宏名全部⼤写函数名不要全部⼤写9. #undef这条指令⽤于移除⼀个宏定义。#undefNAME//如果现存的⼀个名字需要被重新定义那么它的旧名字⾸先要被移除。例如#defineMAX100intmain(){#undefMAX//这里取消了这个宏所以后面的MAX不可以用了inta10;aMAX;//这里不可以用printf(%d\n,a);return0;}10. 命令⾏定义许多C 的编译器提供了⼀种能⼒允许在命令⾏中定义符号。⽤于启动编译过程。例如当我们根据同⼀个源⽂件要编译出⼀个程序的不同版本的时候这个特性有点⽤处。假定某个程序中声明了⼀个某个⻓度的数组如果机器内存有限我们需要⼀个很⼩的数组但是另外⼀个机器内存⼤些我们需要⼀个数组能够⼤些。#includestdio.hintmain(){intarray[ARRAY_SIZE];inti0;for(i0;iARRAY_SIZE;i){array[i]i;}for(i0;iARRAY_SIZE;i){printf(%d ,array[i]);}printf(\n);return0;}编译指令//linux 环境演⽰gcc-D ARRAY_SIZE10programe.c11. 条件编译在编译⼀个程序的时候我们如果要将⼀条语句⼀组语句编译或者放弃是很⽅便的。因为我们有条件编译指令。⽐如说调试性的代码删除可惜保留⼜碍事所以我们可以选择性的编译。#includestdio.h#define__DEBUG__intmain(){inti0;intarr[10]{0};for(i0;i10;i){arr[i]i;#ifdef__DEBUG__//__DEBUG__ 为真就执行下一条语句printf(%d\n,arr[i]);//为了观察数组是否赋值成功。#endif//__DEBUG__}return0;}常⻅的条件编译指令1.#if常量表达式//...#endif//常量表达式由预处理器求值。如#define__DEBUG__1#if__DEBUG__//..#endif2.多个分⽀的条件编译#if常量表达式//...#elif常量表达式//...#else//...#endif3.判断是否被定义#ifdefined(symbol)#ifdefsymbol#if!defined(symbol)#ifndefsymbol4.嵌套指令#ifdefined(OS_UNIX)#ifdefOPTION1unix_version_option1();#endif#ifdefOPTION2unix_version_option2();#endif#elifdefined(OS_MSDOS)#ifdefOPTION2msdos_version_option2();#endif#endif结语“别走你的三连还在我这儿寄存着呢点赞是门票投币是VIP收藏是复活甲下次更新记得来取哦完。