####1. 为了跳过之前的回车,可以使用scanf(“空格%c”,&chr);,其中的空格可以跳过零个或者若干个空白字符,包括换行、换页、行制表符、列制表符、空格

####2. 为了打印出小数点后两位数字,"%.2f"来输出。为了打印出自然书写格式的的浮点数字,可以用%g。

####3. 整型常量:八进制和十六进制没有负数常量。书写为:八进制:0开头,如0177;十六进制:0x开头,如0x7fff。默认属于int型,可以在后边添加L、U表示长整型、无符号型。为了输出负数八进制或十六进制,可以用下列方法:

1
2
3
4
if( i < 0)
	printf("-%x",-i);
else
	printf("%x",i);

####4. 浮点型常量:默认以双精度浮点形式存储。后缀F、L表示单精度浮点型或者long double型。在读取double型时用%lf,输出时%f;读取或者输出long double型时用%Lf。

####5. 转义序列:连续的??要用?来输出问号,否则会认为是三字符序列。八进制转义序列用\33或者\033表示ESC;十六进制转义序列用\x1B。x必须小写。

####6. 如果用char来存储较小的整型时,必须用signed或者unsigned来声明是否带有符号位。(否则会在自动扩展时出现问题,比如大于128的数字变成负数)

####7.

1
2
3
while (getchar() != '\n');/*来跳过一行的输入*/

while (getchar() == ' ');/*来跳过空格的输入*/

scanf()后边如果再使用getchar时要注意,scanf后边输入的\n是会被getchar所读取的,一般是所不希望的。

####8. sizeof()运算符一定要将所求目标用括号选中,否则容易出错。(sizeof运算符的优先等级高于二元运算符)。另一方面,返回值为无符号整型,容易在赋值到整型时溢出。

####9. 同样,-10<(unsigned int) 10的结果是假。

####10. 运算时溢出,即便结果是由更大范围的类型来保存依然会溢出。如:

1
2
3
long int i;
int j = 1000;
i = j * j;//先溢出,再转换类型

####11. 使用数组的时候一定要避免从1开始。能从0开始就一定要从0开始,否则容易产生难以察觉的错误。

####12. 数组下标使用++等带有作用的运算时要小心。如a[i] = b[i++]在不同电脑的实现是不同的,可能会成为b[1] = a[2]。

####13. 数组间的赋值:memcpy(a, b, sizeof(a));它比循环速度快。来自<string.h>。

####14. 函数不能返回数组。

####15. 如果函数返回的类型非常长(如unsigned long int),那么有必要把返回类型单独放在一行

1
2
3
4
5
unsigned long int
average(float a, float b)
{
return ( a + b) / 2;
}

####16. 在有返回值的函数需要丢弃返回值时,可以强制转换来明确表示丢弃,比如(void) printf(”Hi,MMMM……\n“);来表示丢弃返回值。

####17. 在void 引导的函数中可以用return;来随时退出函数。

####18. 每个函数定义前都要有盒型注释,要求给出函数名、函数的目的、讨论每个形式参数的含义、描述返回值和罗列任何的副作用。(如会改变某个参数等)

####19. 函数中用const来保护不需要修改的参数。在指针、数组类型中使用const要注意,如void f(const int *p)表示p所指向的数字不能改变。如果是void f(int * const p)则表示p指向的数字可以改变,但是p不可以改变。可以出现void f(const int * const p)来表示指针和指针指向的数字都不可变。如果不用const,即便是void f(int a[])中a也相当于指针,也可以改变的。

####20. 指针可以当作数组名使用。如*p = a[10],则p[5] 等价于 a[5];

####21. 多维数组名作为指针时要小心。如a[10][10]中a相当于 **a。

####22. Int a[10],i = 5;使用时a[i]和i[a]是一样的,但是千万别混着使用。函数参数中a[]和*a是一样的,但是*a更为通用。

####23. 可以用printf("%p",p);来显示p指针地址。

####24. 字符串里面的转义序列要小心使用。比如八进制的转义序列在三个数字或者第一个非八进制数字处结束,\1234 实际是\123和4。十六进制数的转义序列不限制为3个数字,而是到第一个非16进制数字截至。

####25. 如果字符串太长,不能在一行内放置,可以使用\来延长。\要求之后没有除回车外的字符,下一行要顶头开始。如:

1
2
    printf(“Put a \
disk into drive a\n”);

不过会打乱缩进。还好C语言标准规定,只要两个字符串中间都是空白字符,则将两个字符串合并,如:

1
2
3
    printf("Put a "

    "disk into drive a\n");

####26. C语言允许对字符串加下标,如"abc"[1]。这个特性使得字符串常量也可能改变,如

1
2
char *p = "abc"[1];
*p = 'x';

但是这种特性会对程序造成不可预知的后果(如果编译器对同样的字符串是压缩存储,这将导致其它字符串可能变化)。但是下标还有一个很好的使用方法,如:return "0123456789ABCDEF"[digit];这句话可以简单地将十进制的数字转化为十六进制的字符。

####27. 如果初始化字符串的长度和其本身长度一致时,编译器不会在后边加上'0',也就不会破坏之后的数据。如:Char str[7] = "srpsrp!",ch;中, Ch的值是不确定的,而不是'\0'。这条仅知道即可。

####28. 如果只显示字符串的一部分,则使用%.xs。如:

1
2
char str[10] = "123456789";
printf("%.3s",str);

将显示123。

####29. puts(str);将在输出字符串后强制输出一个换行。

####30. 用scanf输入的字符永远不包括空白字符(空格、回车、换行、换页、换表格等)。gets则到回车才停止,并忽略回车。

####31. scanf天生不是安全的,它可能会溢出数组。使用%ns会更安全,n指读入的最大字符数。

####32. strcpy 的返回值在较大的表达式中比较有用。如strcpy(str2,strcpy(str1,”1234”));

####33. 在空间敏感的程序中使用字符数组,可以使用char *planets[] = {"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto"},这样里面每一个字符串都只有本身的长度,而不是定长的。如果是定长数组(planets[9][81]),那会浪费不少空间,如Mars后边会空很多。

####34. C程序的命令行中始终有一个空指针,即argv[argc]。

####35. printf和scanf的第一个参数是字符串,并不只是字符串常量。因此可以实现下面的代码:

1
2
3
char fmt = "%d\n";
int i = 10;
printf(fmt,i);

因此可以读入fmt,然后再用fmt作为格式化串输入。

####36. #运算符在预处理时可以将宏的参数转换为字符串字面量。比如: #define PRINT_INT(x) printf(#x " = %d\n",x),将使PRINT_INT(var)输出为var = 13\n

####37. ##运算符在预处理时可以将两个记号合成一个记号。如#define MK_ID(n) i##nint MK_ID(1), MK_ID(2)中等同于int i1, i2

####38. 在宏定义中缺少圆括号会导致C语言中最让人讨厌的错误。程序通常仍然可以编译通过,而且宏似乎也可以正常工作,但在某些情况下会出错。

####39. 一些预定义的宏:

1
2
3
4
5
6
7
8
9
__LINE__	被编译的文件的行数

__FILE__	被编译的文件的名字

__DATE__	编译的日期

__TIME__	编译的时间

__STDC__    如果编译器接受标准C,那么为1

####40. defined运算符引用于标识符时,如果标识符是一个定义过的宏返回1,否则返回0。通常与if结合使用,如 #if defined(DEBUG)…#endif。条件编译常用于保护头文件以避免重复包含。

####41. #line指令用来给程序代码编号。如#line n(n介于1和32767),#line n "文件名"。它可以改变__LINE__宏。

####42. 宏的使用必须谨慎小心,建议仅在常量不止一次使用,或者常量可能的使用方式变化的时候使用。

####43. ##运算符不能嵌套使用。比如#define CONCAT(x,y) x##yCONCAT(a,b)时可以用,但是在CONCAT(a,CONCAT(b,c));时将成为aCONCAT(b,c),而不是abc,因为没有名为aCONCAT的宏。

####44. extern int i;提示编译器i是程序中其他位置定义的,因此不为i分配空间。

####45. 为了防止头文件多次包含,用#ifndef#endif把文件的内容括起来,来保护。如:

1
2
3
4
5
6
7
boolean.h
#ifndef	BOOLEAN_H
#define	BOOLEAN_H
#define	TRUE	1
#define	FALSE	0
typedef	int	Bool;
#endif

####46. 划分源文件的规则:

把每组函数的集合放入单独的源文件,在与源文件同名的头文件中放置函数原型(以及使用这些函数所需要的定义和头文件),每个调用源文件的文件都要包括相应的头文件,这样编译器就可以检验原型和定义是否一致。

####47. Struct可以使用赋值运算:part2 = part1。通常利用这个特性来实现数组的赋值:

1
2
3
struct {int a[10];}a1,a2;
...
a1 = a2;

但是不能使用a1 == a2来判断两者是否相等。

####48. 为了能对两个结构使用=赋值,两者不仅仅需要定义一致,还需要结构标记一致或者使用共同的类型。比如

1
2
struct example{int a[10];} a1; 
struct example a2;

或者

1
2
3
typedef struct {int a[10];} example; 
example a1; 
example a2;

(a1 和 a2可以是出现在不同位置的结构,如两个函数中。为了使用=,但是定义位置不同,所以要用上述两种方法解决问题)。

####49.

1
2
 Struct part {int a[10];} a1;
int part;

是合法的,但是为了避免结构体part和整型变量part混淆,尽量不要使用。

####50. 在结构中使用联合可以节省大量空间。比如同一个数组保存三类不同物品的信息时,可以将三类物品的定义整合为联合。为了区别表示其中保存的物品,可以在结构定义一个枚举变量来表明联合中存储的类型。如

1
2
3
4
struct{
enum (INT_KIND,FLOAT_KIND) kind;
    union{int i; float t;} u;
} number;

可以通过number.kind确定存储的是整形还是实型变量,再提取number.u.i或者number.u.t。

####51. 使用malloc来动态分配结构时,不要使用malloc(sizeof(结构指针)),而是malloc(sizeof(struct 名字))

####52. 对于结构体不能使用==的一点补充,就是不可以对结构体按字节来对比。因为结构内部可能有空洞,对于它们而言,内部可以储存不同的数字。因此相同的结构按字节对比的结果可能不同,必须使用按成员的方式对比。

####53. 使用结构联合和枚举类型的指针时,访问子元素时一定要用(*p).element。包括当被指向的单元自增时使用(*p)++,指针自加使用p++。

####54. calloc函数会清除分配的内存,而malloc不会。有时需要用calloc为非空数组分配空间。通过调用1为第一个实际参数的calloc函数,可以为任何类型数据项分配空间:

p = calloc(1,sizeof(struct point));

####55. realloc的指针参数必须是来自malloc,calloc或者realloc生成的指针。如果不是这样,程序可能会行为异常。

####56. realloc的准则:

  1. realloc不会初始化扩展的内存空间。
  2. 如果realloc不能扩展,原指针会变空指针。
  3. 如果realloc函数调用时以空指针作为第一个实际参数,那么行为就像malloc。
  4. realloc函数的第二个参数如果是0,那么行为如同free。 ####57. 一旦realloc函数返回,需要将指向本内存块的所有指针进行更新,因为realloc函数可能把原内存空间移动到了其他地方的内存。

####58. 释放内存块时一定要谨慎小心,避免几个指针都指向同一个内存块,此时很容易有悬空指针。

####59. 如果有struct node *new_node;要分配空间时要使用new_node = malloc(sizeof (struct node));而不是new_node = malloc(sizeof (new_node));,后者只分配了一个指针的空间,而不是一个结构的空间。

####60. int *p(...)int (*p)(....)不同,前者是返回指针的函数,后者是指向函数的指针。需要后者指向某个函数时,简单的使用p= 函数名(当函数名后边没有括号时,编译器将产生指向函数的指针来代替函数。);调用时,可以用(*p)(参数),或者p(参数)

####61. 利用指向函数的指针可以用来产生菜单项。void(*file_cmd[])(void) = {new_cmd,open_cmd,close_cmd,exit_cmd};就是一个菜单函数的封装实例。通过对file_cmd进行下标操作就可以调用相应的函数。

####62. qsort不可以直接调用strcmp进行排序,因此需要下列函数进行辅助:

1
2
3
4
Int compare_strings(const void *p,const void *q)
{
    Return strcmp(*(char **) p,*(char **)q);
}

####63. static有三个用法:

  1. 声明变量和函数当前文件内有效;
  2. 函数内的变量得以保留并在下次调用时使用;
  3. 使变量初始化为0。

####64. 可以用extern int toupper(int);来强制使用函数版本的toupper。

####65. malloc、calloc要和free封装在一个函数内,否则容易在free的时候跳出

####66. assert用在函数检查参数的时候非常方便。如果该函数需要保证参数输入的条件,用assert,这样保证输入参数有问题的时候可以在debug的时候发现,而release版又不会产生性能上的影响。

####67. 函数不能返回auto型的变量,但是可以返回static声明的静态变量。

####68. rigister类型只对块内的变量有效,由于没有地址,因此取地址运算符&是无效的。

####69. 函数的类型只有extern 和static两种,前者加与不加是一样的,后者使得函数只能在本文件内调用。

####70. 对于某个文件内的全局变量,在另一个文件的函数内可以使用extern int i来获得之前的定义的变量。但是此时i在该文件中只有该函数的作用域。

####71. 处理复杂的声明时:始终从内向外读声明;做选择时,始终先[]和()后是*,越先选取的,就越是它的本质。如:int *(*x[10])(void);

  1. x[10],数组;
  2. *,指向函数的指针数组;
  3. void,指向无参数函数的指针数组;
  4. int *,指向返回整型指针的无参数函数的指针数组。

可以利用typedef来定义中间类型,简化理解过程:

  1. typedef int *Fcn(void);
  2. typedef Fcn *Fcn_ptr;
  3. typedef Fcn_ptr Fcn_ptr_array[10];
  4. Fcn_ptr_array x;

注意不能出现以下情况:

  1. int f(int)[];(函数不能返回数组)
  2. int g(int)(int);(函数不能返回函数)
  3. int a[10](int);(函数型数组不存在)

####72. 可以用下列技巧来在一个工程中合理的分配公有私有的函数:

1
2
#define PUBLIC /*empty*/
#define PRIVATE static

这样就可以在声明函数的时候用上述修饰符来声明函数的定义范围了。

####73. i»j的值是i右移j位的结果。如果i是无符号数或非负值,左端补0;i是负值,则由实现定义的。因此最好仅对无符号数移位。

####74. ~0可以得到一个全部数位为1的数字。

####75. &^|优先级低于判等和关系运算符,因此一般使用位运算符都用括号。

####76. 位运算惯用法:

  1. 设置位 i |= 0x0010
  2. 位清0 i &= ~0x0010;
  3. 测试位 if (i & 0x0010)

####77. 结构体中可以在变量声明的时候在后边加冒号和数字来声明一个位域,如struct date { unsigned int day:5 ,month: 4, year:7}.但是使用的时候位域不可以使用&来取地址,因此scanf要先读入到普通变量后再赋值。也可以在位域声明的时候不起名字,起到占位的作用。长度为0的位域则强制下一个位域放在一个存储单元的起始位置(用于对齐)。

####78. 联合常常用来用多种形式显示数字。比如union int_date {unsigned int i; struct date fd;};其中date结构体是代码中已经声明过的,size等同int。这样按照date类型存储后可以通过i得到一个int型的数字。

####79. 指定一个指针的地址:如果计算机的指针和长整型一致,则可以直接赋值:BYTE *p; p = (BYTE*) 0x1000;如果是实模式的dos程序,则使用BYTE far *p; p = MK_FP(segment, offset);

####80. 对于直接从用户输入缓冲得到的字符,需要用volatile 限定符声明中间变量。否则循环中中间变量没有变化过,可能被编译器优化后,使得每次读取中间变量时都是读取第一次的值。用volatile声明后编译器就会强制每次读取p的值时都从内存中重新获取。如:

1
2
3
4
5
6
while (缓冲区未满) 
{
 等待输入;
 buffer[i] = *p;
 if (buffer[i++] == '\n') break;
}

此处的p就需要用volatile 声明。

####81. 系统库里的很多函数都用宏重新封装过,如isprint()函数被重新定义为#define isprint(c)宏。要使用函数版本而不是宏的时候(比如函数指针),可以用#undef 宏名来取消它的宏定义;也可以用(isprint)(c)来强制个别调用时是函数。

####82. 宏offsetof参数是类型、指定成员,会计算起点到指定成员的字节数。如struct s {char a;int b[2]; float c;} offset(struct s,a)的结果为0。

####83. 可以用fflush(fp)来强制让fp指针的状态commit到硬盘。fflush(NULL)可以把所有的文件指针commit。没有错误返回0,有错返回EOF。fp必须处于(1)输出打开(2)为更新打开并且流的最后操作不是读,也就是说,是用来“清理”输出流的。

####84. 使用setvbuf函数必须在stream的任何操作之前使用。

####85. r+、a+、w+在转换读或者写的时候,必须用文件定位函数将读转为写,或者用fflush或文件定位函数将写转为读。

####86. filename = tmpnam(NULL);或者 tmpnam(filename)都可以生成临时的文件名。

####87. Printf类函数完整说明:

标志最小字段宽度精度长度修饰符转换说明符
%#012.5Lg
*标志*:可选项,允许多余一个。所有标志如下:
标志含义
-在字段内左对齐
+以+开头的正符号数
空格空格取代正符号数的+号
#以0开头的八进制数,以0x开头的16进制数。浮点数始终是十进制。不能删除g或者G输出数的尾部0
0(零)用前导0填充宽度。如果是制定了精度的d、i、o、u、x,则忽略前导0
*最小字段宽度*:可选项。如果字符数太少达不到字段宽度最小值,则对字符数量扩充(默认在左侧添加空格)。如果超过字段宽度,则完整的显示。如果宽度是*,则宽度由下一个参数决定。

精度:可选项。精度含义依赖于转换说明符:如果转换说明符是d、i、o、u、x、X,则精度表示最少数字位数,少于时左侧加0;如果是e、E、f,那么精度是小数点后的数位;如果是g、G,则表示最大有效位数;如果是s,则表示最大字符数;如果只有小数点,则精度为0;如果是*,则由下一个参数决定。

长度修饰符:可选项,h说明是short型的,l是long型的,L说明是long double型的。

转换说明符

转换说明符含义
d、i有符号整数的十进制形式
O、u、x、X8进制、10进制、或16进制
f十进制double型
E、e科学计数法的double型
G、g自动选择f还是e
c自动选择f还是e
s字符串输出,达到精度时或者遇到结尾时停止。
pvoid *型的指针
n匹配的实参必须是指向int的指针。可以和h和l搭配。会将输出的字符数量存储到指针指向的地址而不输出。
%字符%
*示例*:(。表示空格)
转换说明对123的结果对-123的结果
%8d。。。。。123。。。。-123
%-8d123。。。。。-123。。。。
%+8d。。。。+123。。。。-123
% 8d。。。。。123。。。。-123
%08d00000123-。。。。123
%-+8d+123。。。。。-123。。。。
%- 8d。123。。。。-123。。。。
%+08d+0000123-0000123
% 08d。0000123-0000123
转换说明对123的结果对123.0的结果
%8o。。。。。173-
%#8o。。。。0173-
%8x。。。。。。7b-
%#8x。。。。0x7b-
%8X。。。。。。7B-
%#8X。。。。0X7B-
%8g-。。。。。123
%#8g-。123。000
%8G-。。。。。123
%#8G-。123。000
转换说明对"bogus"的结果对"buzzword"的结果
%6s。bogusbuzzword
%-6sbogus。buzzword
%.4sbogubuzz
%6.4s。。bogu。。buzz
%-6.4sbogu。。buzz。。
对%.4g产生的结果
1234561.235e+05
12345.61.235e+04
1234.561235
123.456123.5
12.345612.35
1.234561.235
0.1234560.1235
0.01234560.01235
0.001234560.001235
0.0001234560.0001235
0.00001234560.00001235
0.000001234560.000001235
其它
printf("%6.4d",i)等价于printf("%*.4d",6,i);printf("%6.*d",4,i);printf("%*.*d",6,4,i);
printf("%d%n",123,&len);输出123输出后len==3

####88. scanf类完整说明:

scanf类函数在输入失败或者匹配失败的时候会提前返回。返回值是读入并赋值给实参的数据数量。因此可以用while (scanf("%d",&i) == 1){...}来循环读取,直到读取失败为止。

格式化串的组成:

字符*:可选项。*出现意味着赋值屏蔽:读入此项,但是不赋值。*匹配的数据项不计入scanf类函数的返回值。

最大字段宽度:可选项。限制了输入项字符的数量。如果开始时跳过了空白字符,对空白字符不进行统计。

h、l和L:同printf类函数的含义。

转换说明符:

转换说明符含义
d匹配十进制整数
i匹配整数,假定数是十进制的,除非0开头(8进制),或0x开头
o匹配八进制数,实参为unsigned int 指针
u匹配十进制数,实参为unsigned int 指针
X、x匹配十六进制数,实参为unsigned int 指针
E、e、f、g、G匹配float型
s匹配一序列非空字符串,并在末尾加'\0'
[匹配来自扫描集合的非空字符序列,然后在末尾添'\0'。可以是%[集合]或者%[^集合],前者匹配集合内的字符,后者匹配集合外的字符。
c匹配其字段宽度的字符数,如果没有字段宽度,则匹配1个
p匹配printf输出相同的指针值
n将目前为止的读入字符数存入此int指针指向的变量,没有输入会匹配,scanf的返回值也不受影响。
%匹配%

示例:(。表示空格)

scanf的调用输入变量
n = scanf ("%d%d",&i,&j);12。,。34'\n'n:1;i:12;j:?
n = scanf ("%d,%d",&i,&j);12。,。34'\n'n:1;i:12;j:?
n = scanf ("%d。,%d",&i,&j);12。,。34'\n'n:1;i:12;j:34
n = scanf ("%d,。%d",&i,&j);12。,。34'\n'n:1;i:12;j:?
n = scanf ("%*d%d",&i);12。34'\n'n:1;i:34
n = scanf ("%*s%s", str);My。Fair。Lady'\n'n:1;str:"Fair"
n = scanf ("%1d%2d%3d",&i,&j,&k);12345'\n'n:3;i:1;j:23;k:45;
n = scanf ("%2d%2s%2d",&i,str,&k);123456'\n'n:3;i:12;str:"34";k:56;
n = scanf ("%i%i%i",&i,&j,&k);12。012。0x12'\n'n:3;i:12;j:10;k:18;
n = scanf ("%[0123456789]",str);123abc'\n'n:1;str:"123";
n = scanf ("%[0123456789]",str);abc123'\n'n:0;str:?
n = scanf ("%[^0123456789]",str);abc123'\n'n:1;str:"abc";
n = scanf ("%*d%d%n",&i,&j);10。20。30'\n'n:1;i:20;j:5

####89. 如上所述,scanf ("%[^\n]",str);可以轻松得到一行文字。

####90. ungetc(ch,fp);可以轻松回退一次上次的getc操作,只保证第一次ungetc的调用是成功的。注意,getc所保存的应当是int型数据,否则(ch = getc(fp)) != EOF 可能会导致错误的结果。

####91. ftell(fp)函数中fp如果是文本流,则返回的值不一定是字节计数,因此最好不要进行算术运算。

####92. 可以用sprintf和sscanf对字符串进行读写操作。比如sprintf(str,"%d",i);sscanf则实现了对一次输入的多重检测读取,如:

1
2
3
4
5
6
if(sscanf(str,"%d /%d/%d", &month,&day,&year) == 3)
    printf("Month: %d, day: %d, year: %d\n",month,day,year);
else if (sscanf(str,"%d -%d -%d", &month,&day,&year) == 3)
    printf("Month: %d, day: %d, year: %d\n",month,day,year);
else
    printf("Date not in the proper form.\n");

####93. 可以将格式化串和问号表达式一起用,比如printf(num > 1 ? “%d apples.":"%d apple.",num);

####94. 库的宏定义范围:<float.h>浮点型的特性;<limits.h>整值类型的大小;<math.h>数学计算(出错时多数会写入errno变量中),其中包含fmod(5.5,2.2) = 1.1;

####95. size_t strxfrm(char *s1, const char *s2, size_t n);该函数将s2转换后存在s1,返回转换后的字符数量。一般都是用两次:

1
2
3
4
5
size_t len;
char *transformed;
len = strxfrm(NULL, original, 0);
transformed = malloc(len+1);
strxfrm(transformed, original, len);

####96. strpbrk函数用来查找第一个参数中与第二个参数中任意一个字符相匹配的最左边的一个字符;strspn和strcspn会返回一个表示字符串中特定位置的整数(size_t类型),strspn返回第一个不属于给定字符集的字符下标;strcspn函数返回第一个属于给定字符集中的字符下标,如:

1
2
3
4
5
6
size_t len;
char str[] = "Form follows function.";
len = strspn(str, "morF");	/*len=4*/
len = strspn(str," \t\n";	/*len=0*/
len = strcspn(str, "morF");	/*len=0*/
len = strcspn(str, " \t\n");	/*len=4*/

####97. memset可以将数组全初始化为0,但是对于浮点数、结构体等,不是memset可以完全置0;

####98. <assert.h>可以使用#define NODEBUG禁用assert

####99. <errno.h>errno变量用来存储数学函数错误。EDOM表示定义域错误,ERANGE表示值域错误,通常是太大。

####100. <signal.h>void (*signal(int sig, void (*func)(int)))(int);作用是用自定义的func函数替换原先与sig相对性的函数。返回值是原先的函数指针。除非是abort或者raise函数引发的signal,否则不要用库函数,或者试图使用一个静态存储期限的变量。从处理函数返回时程序将从信号发生点继续运行,除了信号SIGABRT(终止程序),和SIGFPE(未定义)。信号处理完毕后,除非处理函数重新安装,否则该信号不会被同一函数再次处理。因此可以在返回之前再用signal函数重新安装。SIGSEGV表示段错误,调试时应该很有用。

####101. 在字符串中小心放置??。如果要放置??,可以用??来代替。否则会被认为是三字符序列。

####102. 任意程序开始时都隐含执行setlocale(LC_ALL,"C");如果运行setlocale(LC_ALL,NULL);则会返回当前区域设置。对于宽字符常量需要用L修饰,如L"abc”。

####103. 用来检测多字符和宽字符:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int mbcheck(const char *s)
{
    int n;
    for (mblen(NULL, 0) ; ; s += n)
    if ((n = mblen(s, MB_CUR_MAX)) <= 0)
    return n;
}

int wccheck(wchar_t *wcs)
{
    char buf[MB_LEN_MAX];
    int n;
    for(wctomb (NULL,0) ; ; ++wcs)
        if ((n = wactomb(buf, *wcs)) <= 0)
            return -1;
        else if (buf[n -1] == '\0')
            return 0;
}

wctomb (NULL,0)这个函数是用来初始化多字符状态的。

mbstowcs和wcstombs可以转换字符串,返回修改字符串的个数。

####104. <stdarg.h>提供的三种宏可以视为三个函数:

1
2
3
void va_start(va_list ap, parmN);
类型 va_arg(va_list ap, 类型);
void va_end(va_list ap);

通过第一个函数设置valist的可变参数是从哪里开始;第二个函数是从valist中返回下一个参数(不必手动增加,每次调用会自增),其类型为指定类型;第三个参数结束参数的调用,此时方可返回。声明函数时使用int max(int n,...)即可。

####105. <stdlib.h>对于相同的种子,srand()将生成相同的随机数列。rand()函数获得随机数列的下一个值。选择1作为种子值和没有指明种子值是一样的。

####106. 可以通过atexit(函数名)来指定退出时自动调用的方法。

####107. getenv函数会返回指定字串在操作系统中某个字符串的意义,比如PATH。该结果储存在静态空间中,稍后函数调用时会改变。system用来执行外部命令,如果命令为空指针,则对命令环境测试,如果命令处理程序有效,则返回非0值。

####108. 搜索和排序:

1
2
3
void *bsearch(const void *key, const void *base,size_t nmemb, size_t size, int (*compar)(const void *, const void *,));

void qsort (void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

####109. /和%与实现有关,因此可以用div和ldiv代替,前者用于整型,后者用于长整型。返回div_t类型,可用quot和rem求商和余数。效率是使用它们而不是/和%的原因,因为可以在一个指令里计算出两个结果。