### 指针数组 指针数组里的每个存储区都是一个指针类型的存储区。 字符指针数组包含多个字符指针,每个字符指针都可以用来表示一个字符串。 字符指针数组可以用来代表多个相关字符串。 #include <stdio.h> int main(){ char *strs[] = {"abc", "def", "iop", "asd", "xyz"}; int num = 0; for (num =0;num <= 4;num++){ printf("%s\n", strs[num]); } return 0; } 二维字符数组也可以用来记录多个相关字符串。 通常采用字符指针数组表示多个相关字符串。 ### 宏定义 `#define`预处理指令用来定义宏 宏可以用来给数字起名字。 #include <stdio.h> #define PI 3.14 // 宏定义 int main(){ int radius = 0; printf("请输入半径: "); scanf("%d", &radius); printf("周长是:%g\n", 2 * PI * radius); return 0; } 宏定义语句里宏名称写在前面,它代表的数字写在后面。 宏名称通常由大写字母构成。 宏名称里不能包含空格。 用宏给数字起名字的时候,不要使用赋值操作符。 编译器在编译的时候会首先把程序里的宏名称替换成它代表的数字。 ### gcc编译 使用gcc -E可以查看预处理后的结果: gcc -E main.c 可以在编译命令里使用-D的选项指定宏代表的数字。 #include <stdio.h> // #define PI 3.14 int main(){ int radius = 0; printf("请输入半径: "); scanf("%d", &radius); printf("周长是:%g\n", 2 * PI * radius); return 0; } gcc进行编译: gcc -DPI=3.14f main.c 如果程序中某些数字在编写的时候无法确定,只能在编译的时候才知道就可以使用宏名称代表它们,使用-D命令来进行从外面编译宏数据。 ### 宏计算公式 宏也可以给计算公式起名字。 计算公式里包含未知数字,宏的参数用来代表这些未知数字。 ### 宏参数 带参数的宏采用二次替换的方式进行处理。 宏的参数不一定代表数字,所以没有类型名称。 如果宏有多个参数就需要在相邻的参数名称之间用逗号把他们分开。 宏因为不会被分配存储区,所以没有可以使用自己的存储区和函数进行数据传递。 宏没有形式参数也没有用来存放返回值的存储区。 能当数字使用的宏,必须是逻辑表达式(因为宏没有用来存放返回值的存储区)。 #include <stdio.h> #define ABS(n) n >= 0 ? n : 0 - n #define NEG(n) n = n - n int main(){ int num = 0; printf("请输入一个数字:"); scanf("%d", &num); printf("绝对值是: %d\n", ABS(num)); return 0; } 宏的参数直接代表的就是函数的存储区,对宏参数内容的修改直接修改的就是函数存储区的内容。 ### 宏计算的优先级 因为宏没有用来记录返回值的存储区,所以不能保证优先计算宏内部的操作符。 能当做数字使用的宏必须写在一对小括号中间。 正确写法: #include <stdio.h> #define SUB(x, y) ((x) - (y)) int main(){ printf("SUB(x, y) = %d\n", SUB(10, 5)); printf("20 - SUB(x, y) = %d\n", 20 - SUB(10, 5)); return 0; } 宏没有形式参数所以不能保证优先计算参数里面的操作符。 宏的所有能代表数字的参数,必须都写在小括号里面。 #define SUB(x, y) ((x) - (y)) 不要使用自增或者自减的结果作为宏的参数。 #include <stdio.h> #define SQUARE(n) ((n) * (n)) int main(){ int num = 5; printf("SQUARE(n) = %d\n", SQUARE(5)); printf("SQUARE(++num) = %d\n", SQUARE(++num)); return 0; } ### 宏操作符 编写宏的时候可以使用一些特殊的操作符,他们叫做宏操作符。 **# 操作符** `#`是一个宏操作符,它可以把宏的一个参数转换成字符串字面值。 #include <stdio.h> #define STR(n) #n int main(){ STR(2 + 4); // "2 + 4" return 0; } **## 操作符** `##`也是一个宏操作符,它可以把一个代表标识符的参数和其他内容链接成为一个新的标识符。 #include <stdio.h> #define PTR(n) p_##n int main(){ int num = 0; int *PTR(num) = &num; return 0; } ### 条件编译 **#ifdef/$ifndef...#else...#endif** 条件编译可以在编译的时候从几组语句中选择一组编译而忽略其他组。 `#ifdef/$ifndef...#else...#endif`,这种结构可以根据一个宏是否被定义过从两组语句中选择一组编译。 以上两种结构,最开始的预处理指令,需要从两个选择一个,不论选哪个后面都要跟一个宏名称。 如果最前面的预处理执行时`#ifdef`就表示它后面的宏名称被定义过的时候编译前一组语句,否则编译后一组语句。 如果是`#ifndef`则相反。 /* 条件编译演示 */ #include <stdio.h> int main(){ #ifdef YI printf("1\n"); #else printf("2\n"); #endif return 0; } YI是一个宏,当我们在编译的时候没有去编译这个宏,则会打印出2,否则就是1。 #ifndef 反之 **#if...#elif(任意多次)...#else...#endif** `#if...#elif(任意多次)...#else...#endif`,这种结构也可以实现条件编译,它可以根据任意逻辑表达式从多组语句中选择一组编译而忽略其他组。 /* 条件编译演示 */ #include <stdio.h> int main(){ #if defined(JINGPIN) // defined的意思是确认宏是否被定义,定义了则为真,没定义则 为假 printf("120%%\n"); #elif !defined(JINGPIN) && !defined(GONGCHANG) printf("100%%\n"); #else printf("80%%\n"); #endif return 0; }