int b = a << 2; /* b = 0111 1000 =240 */ /* ... 使用PI... */ #undef PI/* 之后PI不再是一个宏 */
复制代码
宏定义的注意事项
l 宏定义只是简单的文本替换,没有类型检查或作用域限制。
l 宏可能会因为参数的运算符优先级导致预期之外的行为,所以使用时要特别小心。
l 宏定义可能会导致代码膨胀,因为每个宏的使用都会导致相同代码的重复插入。
l 避免在宏中使用复杂的表达式或逻辑,因为这会增加代码阅读和维护的难度。
l 使用宏时要谨慎,以避免出现意外的副作用或难以调试的错误。
在这个例子中,FEATURE_A被定义了,所以#ifdef FEATURE_A和#endif之间的代码会被编译。而FEATURE_B没有被定义,所以#ifndef FEATURE_B和#endif之间的代码会被编译。
除了#ifdef,还有其他的条件编译指令:
l #ifndef:如果宏没有定义,则包含代码。
l #if:用于检查宏是否定义以及它的值是否为真(非零)。
l #elif:与#if和#else结合使用,用于检查多个条件。
l #else:如果前面的#if或#ifdef条件不满足,则包含代码。
l #endif:标记条件编译块的结束。
这些指令通常在源代码的顶部使用,以根据特定的配置或平台条件包含或排除代码段。例如,你可能想要在不同的操作系统上使用不同的系统调用,或者在调试和发布版本中包含或排除调试代码。 5.4 extern外部申明
在C语言中,extern关键字用于声明一个变量或函数,而不是定义它。extern告诉编译器,变量的定义或函数的实现在其他地方,可能是在另一个源文件中。这允许程序的不同部分共享同一个变量或函数,而无需在每个文件中都重复定义它。
1,变量外部声明
当你想在一个源文件中使用另一个源文件中定义的变量时,你需要使用extern来声明这个变量。例如,假设你有一个名为variables.c的文件,它定义了一个名为globalVar的全局变量:
printf("Thevalue of globalVar is: %d\n", globalVar);
return 0;
}
复制代码
在这个例子中,main.c中并没有定义myFunction,而是通过extern关键字进行了声明。在编译和链接阶段,链接器会找到myFunction的定义,并将其与main.c中的调用关联起来。
3,注意事项
l extern只能用于声明变量或函数,不能用于定义。定义会分配内存空间,而声明不会。
l 当你在一个源文件中定义了一个变量或函数,并想在另一个源文件中使用它时,你需要在第二个源文件中使用extern进行声明。
l 外部声明必须在使用变量或函数之前进行。
l 在链接阶段,链接器会查找所有extern声明的定义,如果找不到,就会出现链接错误。
l 如果多个源文件定义了同一个extern变量,那么链接器会将其视为错误,因为同一个变量只能有一个定义。但是,多个源文件可以包含同一个extern函数的声明,因为这个函数可能在不同的地方有不同的实现。 5.5 typedef类型别名
typedef用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。例如在编写程序时经常使用到的uint8_t、uint16_t和uint32_t等都是由typedef定义的类型别名,其定义如下:
如上,就定义一个名为p_str的指针变量,并将p_str指针指向了字符串“This is a string!”保存在内存中首地址,对于ESP32来说,此时p_str的值就是一个32位的数,这个数就是一个内存地址,这个内存地址就是上述字符串保存在内存中的首地址。
通过p_str指针就可以访问到字符串“This is a string!”,那具体是如何访问的呢?前面说p_str保存的是一个内存地址,那么就可以通过这个内存地址去内存中读取数据,通过p_str就可以访问地址为p_str的内存数据,(p_str + 1)可以访问下一个内存地址中的数据。
知道了如何访问内存中的数据,但是读取到的数据要如何解析呢?这就有p_str指针的类型决定了。在这个例子中p_str是一个char类型的指针,那么访问p_str就是访问地址为p_str,大小为sizeof(char)(一般为一个字节)的一段内存数据,在这个例子中就可以读取到字符“T”, 读取(p_str+ 1)就是“h”,以此类推。
为了更加直观的演示,我们试着编写如下代码并观察输出结果的变化: