有书共读22《C primer Plus》16章
- 明示常量#define预处理字符:由3部分组成,第1部分是#define指令本身;第2部分是选定的缩写,也成为宏;第3部分(指令的其余部分)称为替换列表或替换体。
eg:#define PX printf(“*x is %d.\n”,x) 其中#define是指令本身;PX为宏;printf(“*x is %d.\n”,x)是替换体。
预处理器不做计算、不求值、只替换字符序列。
2、#define OW “consistency is the last refuge of the unimagina\
tive.-oscar while”我们在上一行的结尾加一个反斜杠字符使该行扩展至下一行,注意第2 行要与第一行左对齐。
3、重定义常量:只有新定义与旧定义完全相同才允许重定义。注:如果需要重定义宏,使用#undef指令。
4、预处理器粘合剂:##运算符。与#运算符类似,##运算符可用于类函数宏的替换部分,还可以用于对象宏的替换部分。eg: #define XNAME(n) x ## n
5、变参宏:…和_ _VA_ARGS_ _
通过把宏参数列表中最后的参数写成省略号(即,3个点…)
eg:#define PR(…) printf(__VA_ARGS__)
6、宏和函数的选择:
1)记住宏名中不允许有空格,但是在替换字符串中可以有空格。
2)用圆括号把宏的参数和整个替换体括起来,这样能确保被括起来的部分在表达式中正确。
3)用大写字母表示宏函数的名称
4)如果打算使用宏来加快程序的运行速度,那么首先要确定使用宏和使用函数是否会导致较大的差异。在程序中只使用一次的宏无法明显减少程序的运行时间。在嵌套循环中使用宏更有助于提高效率。
7、文件包含:#include
eg:#include <stdio.h>和#include “mystuff.h”
在UNIX系统中,尖括号告诉预处理器在标准系统目录中查找该文件。双引号告诉预处理器首先在当前目录中(或该文件名中指定的其他目录)查找该文件。
注:可执行代码通常在源代码文件中,而不是在头文件中。
8、#undef指令:用于“取消”已定义的#define指令。
eg:#define LIMIT 400 #undef LIMIT
将移除上面的定义。
9、处理器在识别标识符时,遵循与C相同的规则:标识符可以由大写字母、小写字母、数字和下划线字符组成,且首字符不能是数字。
10、条件编译:
1)#ifdef #else和#endif指令
eg:#ifdef MAVIS
#include “horse.h”//如果已经用#define定义了MAVIS,则执行下面的指令;
#define TSTABLES 5
#else
#include “cow.h”//如果没有用#define定义了MAVIS,则执行下面的指令;
#define TSTABLES 15
#endif
注:#ifdef #else很想C的if else。两者的区别是:预处理器不识别用于标记块的花括号({ }),因此它使用#ifdef(如果需要)和#else(必须存在)来标记指令块。
2)#ifndef:与#ifdef指令的用法类似,也可以和#else #endif一起使用,但是他们的逻辑相反。#ifndef指令判断后面的标识符是否是未定义的,常用于定义之前的未定义的常量。
3)#if和#elif指令:和C语言中的if很像,#if后面跟整型常量表达式,如果表达式为非零,则表达式为真。可以按照if else的形式使用#elif。
eg:#if SYS==1 #include“ibmpc.h” #elif SYS==2 #include“vax.h” #elif SYS==3 #include“mac.h” #else #include“general.h” #endif
11、预定义宏:
__DATE__:当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。
__TIME__:当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。
__FILE__:这会包含当前文件名,一个字符串常量。
__LINE__:这会包含当前行号,一个十进制常量。
__STDC__:当编译器以 ANSI 标准编译时,则定义为 1。
12、内联函数:(inline function):把函数变成内联函数建议尽可能快的调用该函数,其具体效果由实现定义。因此把函数变成内联函数,编译器可能会用内联代码替换函数调用,并(或)执行一些其他的优化,但是也可能不起作用。创建内联函数的定义有多种方法。最简单的方法是使用函数说明符inline和存储类别说明符static。eg:inline static void eatline()
13:数学库:包含许多有用的数学函数,math.h头文件提供这些函数的原型。
1)double acos(double x) 返回以弧度表示的 x 的反余弦。
2)double asin(double x) 返回以弧度表示的 x 的反正弦。
3)double atan(double x) 返回以弧度表示的 x 的反正切。
4)double atan2(double y, double x) 返回以弧度表示的 y/x 的反正切。y 和 x 的值的符号决定了正确的象限。
5)double cos(double x) 返回弧度角 x 的余弦。
6)double cosh(double x) 返回 x 的双曲余弦。
7)double sin(double x) 返回弧度角 x 的正弦。
8)double sinh(double x) 返回 x 的双曲正弦。
9)double tanh(double x) 返回 x 的双曲正切。
10)double exp(double x) 返回x的指数函数的值( )
11)double frexp(double x, int *exponent) 把浮点数 x 分解成尾数和指数。返回值是尾数,并将指数存入 exponent 中。所得的值是 x = mantissa * 2 ^ exponent。
12)double ldexp(double x, int exponent) 返回 x 乘以 2 的 exponent 次幂。
13)double log(double x) 返回 x 的自然对数(基数为 e 的对数)。
14)double log10(double x) 返回 x 的常用对数(基数为 10 的对数)。
15)double modf(double x, double *integer) 返回值为小数部分(小数点后的部分),并设置 integer 为整数部分。
16)double pow(double x, double y) 返回 x 的 y 次幂。
17)double sqrt(double x) 返回 x 的平方根。
18)double ceil(double x) 返回大于或等于 x 的最小的整数值。
19)double fabs(double x) 返回 x 的绝对值。
20)double floor(double x) 返回小于或等于 x 的最大的整数值。
21)double fmod(double x, double y) 返回 x 除以 y 的余数。
14、可变参数:stdarg.h和变宏参功能类似。
1)提供一个使用省略号的函数原型;
2)在函数定义中创建一个va_list类型的原型;
3)用宏把该变量初始化为一个参数列表;
4)用宏访问参数列表;
5)用宏完成清理工作。
15、编程练习:16-5
#include <stdio.h> #include <stdlib.h> #include <time.h> void random_pick(int ar[], int arsize, int picks); #define SPOTS 51 #define PICKS 6 int main(void) { int lotto[SPOTS]; int i; char ch; for (i = 0; i < SPOTS; i++) lotto[i] = i + 1; do { random_pick(lotto, SPOTS, PICKS); printf ("Again? <y/n> "); ch = getchar(); while (getchar() != '\n') continue; } while (ch == 'y' || ch == 'Y'); puts ("Done"); return 0; } void random_pick(int ar[], int arsize, int picks) { int i, index, temp; srand((unsigned int) time(0)); if (picks > arsize) { fputs("Number of picks > array size\n", stderr); fputs("Setting picks = array size\n", stderr); picks = arsize; } for (i = 0; i < picks; i++) { index = rand() % (arsize - 1); /* pick a random element */ temp = ar[index]; printf ("%2d ", temp); /* display it */ if (i % 20 == 19) putchar('\n'); ar[index] = ar[arsize - 1]; /* swap it with last element */ ar[arsize - 1] = temp; arsize--; /* exclude end from search */ } if (i % 20 != 0) putchar('\n'); }
运行结果:
16、编程练习16-7
#include <stdio.h> #include <stdlib.h> #include <stdarg.h> void show_array(const double ar[], int n); double * new_d_array(int n, ...); int main() { double * p1; double * p2; p1 = new_d_array(5, 1.2, 2.3, 3.4, 4.5, 5.6); p2 = new_d_array(4, 100.0, 20.00, 8.08, -1890.0); show_array(p1, 5); show_array(p2, 4); free(p1); free(p2); return 0; } void show_array(const double ar[], int n) { int i; for (i = 0; i < n; i++) printf("%g ", ar[i]); putchar('\n'); } double * new_d_array(int n, ...) { va_list ap; int i; double * pt; va_start(ap, n); pt = (double *) malloc(n * sizeof(double)); for (i = 0; i< n; i++) pt[i] = va_arg(ap, double); va_end(ap); return pt; }
运行结果: