您的位置:首页 > 其它

C和指针读书笔记

2013-12-07 23:41 85 查看
C和指针读书笔记
第1章 快速上手
1、注释不能嵌套,也就是说,第1个/*符号和第1个*/符号之间的内容都被看作是注释,不管里面还有多少个/*符号。
2、stdlib.h定义了EXIT_SUCCESS和EXIT_FAILURE符号。
3、假如一个程序的源代码由几个源文件所组成,那么使用该函数的源文件都必须写明该函数的原型。把原型放在头文件中并使用#include指令包含它们,可以避免由于同一个声明的多份拷贝而导致的维护性问题。
4、使用所有格式码(除了%c之外)时,输入值之前的空白都会被跳过,值后面的空白表示该值的结束。因此,用%s格式码输入字符串时,中间不能包含空白。
DEMO:
#include<stdio.h>

int main() {
int quantity;
int n = scanf("%d ",&quantity);
printf("%d : %d\n", n, quantity);
return 0;
}
在控制台下需要输入两次。
第2章 基本概念
5、/*或*/如果出现在字符串字面值内部,就不再起注释定界符的作用。

第3章 数据
6、只有当程序所使用的char类型变量的值位于signed char和unsigned char的交集中,这个程序才是可移植的。
7、如果一个多字节字符常量的前面有一个L,那么它就是宽字符常量。
8、在程序中使用字符串常量会生成一个“指向字符的常量指针”。当一个字符串常量出现于一个表达式中时,表达式所使用的值就是这些字符所存储的地址,而不是这些字符本身。
9、char *message = “Hello World!”;这条语句把message声明为一个指向字符的指针,并用字符串常量中第1个字符的地址对该指针进行初始化。这个声明与:char *message; message = “Hello World!”相同。
10、typedef声明的写法和普通的声明基本相同,只是把typedef这个关键字出现在声明的前面。在定义更为复杂的类型名字时,如函数指针或指向数组的指针,使用typedef更为合适。
11、变长数组
(1)变长数组必须在程序块的范围内定义,不能在文件范围内定义变长数组;

(2)变长数组不能用static或者extern修饰;

(3)变长数组不能作为结构体或者联合的成员,只能以独立的数组形式存在;

(4)变长数组的作用域为块的范围,对应地,变长数组的生存时间为当函数执行流退出变长数组所在块的时候。
由于变长数组的长度在程序编译时未知,因此变长数组的内存空间实际上是在栈中分配的。
DEMO:
#include<stdio.h>

#defineMAX_ELEMENTS 10

int main() {
int max_elements;
scanf("%d", &max_elements);
int a[MAX_ELEMENTS];
int b[max_elements];
return 0;
}
12、位于一对花括号之间的所有语句称为一个代码块。任何在代码块的开始位置声明的标识符都具有代码块作用域,表示它们可以被这个代码块中的所有语句访问。如果内层代码块有一个标识符的名字与外层代码块的一个标识符同名,内层的那个标识符就将隐藏外层的标识符——外层的那个标识符无法在内层代码块中通过名字访问。
13、任何在所有代码块之外声明的标识符都具有文件作用域,它表示这些标识符从它们的声明之处直到它所在的源文件结尾处都是可以访问的。在文件中定义的函数名也具有文件作用域,因为函数名本身并不属于任何代码块。在头文件中编写并通过#include指令包含到其他文件中的声明就好像它们是直接写在那些文件中一样。它们的作用域并不局限于头文件的文件尾。
14、原型作用域只适用于在函数原型中声明的参数名,如果出现参数名,可以随意取任何名字,不必与函数定义中的行参名匹配。
15、链接属性一共有3种——external(外部)、internal(内部)和none(无)。没有链接属性的标识符总是被当作单独的个体,也就是说该标识符的多个声明被当作独立不同的实体。属于internal链接属性的标识符在同一个源文件内的所有声明中都指向同一个实体,但位于不同源文件的多个声明则分属不同的实体。最后,属于external链接属性的标识符不论声明多少次,位于几个源文件都表示同一个实体。
DEMO:
typedef char*a;
int b;
int c(int d){
int e;
int f(int g);

}
在缺省情况下,标识符b、c和f的链接属性为external,其余标识符的链接属性则为none。f的链接属性之所以是external是因为它是个函数名。
16、关键字extern和static用于在声明中修改标识符的链接属性。如果某个声明在正常情况下具有external链接属性,在它前面加上static关键字可以使它的链接属性变为internal。static只对缺省链接属性为external的声明才有改变链接属性的效果。extern为一个标识符指定external属性,这样就可以访问在其他任何位置定义的这个实体。
17、当用于具有文件作用域的声明时,extern关键字是可选的。然而,如果在一个地方定义变量,并在使用这个变量的其他源文件的声明中添加external关键字,可以使读者更容易理解你的意图。
18、当extern关键字用于源文件中一个标识符的第1次声明时,它指定该标识符具有external链接属性。但是,如果它用于该标识符的第2次或以后的声明时,它并不会更改由第1次声明所指定的链接属性。
DEMO:
testa.c
#include<stdio.h>

static int i= 1;
int j;

int main() {
extern int i;
printf("i = %d , j = %d\n", i, j);
}

testb.c
int i = 10;
int j = 10;

19、变量的缺省存储类型取决于它的声明位置。凡是在任何代码块之外声明的变量总是存储于静态内存中,也就是不属于堆栈的内存,这类变量称为静态(static)变量。对于这类变量,你无法为它们指定其他存储类型。静态变量在程序运行之前创建,在程序的整个执行期间始终存在。修改变量的存储类型并不表示修改该变量的作用域,它仍然只能在该代码块内部按名字访问。函数的形式参数不能声明为静态,因为实参总是在堆栈中传递给函数,用于支持递归。
20、属于文件作用域的声明在缺省情况下为external链接属性。
21、作为函数,在缺省情况下具有external链接属性,所以其他源文件只要在文件上存在其原型,就可以调用。
DEMO:
testa.c
#include<stdio.h>

int main() {
void f();
f();
}

testb.c
#include<stdio.h>
void f() {
printf("Hello\n");
}

22、具有external链接属性的实体在其他语言的术语里称为全局(global)实体,所有源文件中的所有函数均可以访问它。只要变量并非声明于代码块或函数定义内部,它在缺省情况下的链接属性即为external。如果一个变量声明于代码块内部,在它前面添加extern关键字将使它所引用的是全局变量而非局部变量。

第4章 语句
23、单独用一行来表示一条空语句是比较好的做法。
24、switch(expression){
statement-list

其中expression的结果必须是整形值。贯穿于语句列表之间的是一个或多个case标签,case标签并不把语句列表划分为几个部分,它们只是确定语句列表的进入点。default子句可以写在任何一个case标签可以出现的位置。

第5章 操作符和表达式
25、当sizeof的操作数是个数组名时,它返回该数组的长度,以字节为单位。
26、判断表达式的长度并不需要对表达式进行求值,所以sizeof(a = b + 1)并没有向a赋任何值。
27、有一个技巧,偶尔可能会看到:
while( x < 10)
b += x,x+= 1;
在这个例子中,逗号操作符把两条赋值语句整合成一条语句,从而避免了在它们的两端加上花括号。
28、a = b + 25;
a是个左值,因为它标识了一个可以存储结果值的地点,b+25是个右值,因为它指定了一个值。字面值常量都不是左值。
29、复杂表达式的求值顺序是由3个因素决定的:操作符的优先级、操作符的结合性以及操作符是否控制执行的顺序。
30、左值意味着一个位置,而右值意味着一个值。所以,在使用右值的地方也可以使用左值,但是在需要左值的地方不能使用右值。

第6章 指针
31、间接访问操作符和后缀++操作符的组合常常令人误解(*cp++)。后缀++操作符的优先级高于*操作符。事实上,这里涉及3个步骤:(1)++操作符产生cp的一份拷贝,(2)然后++操作符增加cp的值,(3)最后,在cp的拷贝上执行间接访问操作。
32、如果p1指向array[i]而p2指向array[j],那么p2-p1的值就是j-i的值。
33、标准允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针进行比较,但不允许与指向数组第1个元素之前的那个内存位置的指针进行比较。
34、任何指针之间都可以进行比较,测试它们相等或是不相等。

第7章 函数
35、使用函数原型最方便(且最安全)的方法是把原型置于一个单独的文件,当其他源文件需要这个函数的原型时,就使用#include指令包含该文件。
36、可变参数列表是通过宏来实现的,这些宏定义于stdarg.h头文件,它是标准库的一部分。这个头文件声明了一个类型va_list和三个宏——va_start、va_arg和va_end。
va_start的第1个参数是va_list变量的名字,第2个参数是省略号前最后一个有名字的参数。初始化过程把va_arg变量设置为指向可变参数部分的第一个参数。
为了访问参数,需要使用va_arg,这个宏接受两个参数:va_list变量和参数列表中下一个参数的类型。va_arg返回这个参数的值,并使var_arg指向下一个可变参数。
最后,当访问完毕最后一个可变参数之后,需要调用va_end。

第8章 数组
37、在C中,在几乎所有使用数组名的表达式中,数组名的值是一个指针常量,也就是数组第1个元素的地址。它的类型取决于数组元素的类型:如果它们是int类型,那么数组名的类型就是“指向int的常量指针”;如果它们是其他类型,那么数组名的类型就是“指向其他类型的常量指针”。注意这个值是指针常量,而不是指针变量。不能修改常量的值。
38、只有在两种场合下,数组名并不用指针常量来表示——就是当数组名作为sizeof操作符或单目操作符&的操作数时。sizeof返回整个数组的长度,而不是指向数组的指针的长度。取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针。
39、2[array] = *(array + 2) = array[2]。
40、当你根据某个固定数目的增量在一个数组中移动时,使用指针变量将比使用下标产生效率更高的代码。当这个增量时1并且机器具有地址自动增量模型时,这点表现得更为突出。
41、声明为寄存器变量得指针通常比位于静态内存和堆栈中得指针效率更高。
42、如果你可以通过测试一些已经初始化并经过调整的内容来判断循环是否应该终止,那么你就不需要使用一个单独的计数器。
43、那些必须在运行时求值的表达式较之诸如&array[SIZE]或array+SIZE这样的常量表达式往往代价更高。
44、char message[] = “Hello”;尽管这看上去像是一个字符串常量,实际上不是。它只是初始化列表的另一种写法。
45、int matrix[3][10]; int *pi = &matrix[0][0]; int *pi = matrix[0];增加这个指针的值使它指向下一个整型元素。
46、int (*p)[] = matrix; p仍然是一个指向整型数组的指针,但数组的长度却不见了。当某个整数与这种类型的指针执行指针运算时,它的值将根据空数组的长度进行调整(也就是说,与零相乘)。
47、void func(int (*mat)[10])与void func(int mat[][10])函数原型等价。这里的关键在于编译器必须知道第2个及以后各维的长度才能对各下标进行求值,因此原型中必须声明这些维的长度。
48、下面的声明取自某个源文件:
int a[10];
int *b = a;
但在另一个不同的源文件中,却发现了这样的代码:
extern int *a;
extern int b[];
int x, y;

x = a[3];
y = b[3];
请解释一下,当两条赋值语句执行时会发生什么。
a
b

第9章 字符串、字符和字节
49、注意strlen返回一个类型维size_t的值,这个类型是在头文件stddef.h中定义的,它是一个无符号整数类型。
if(strlen(x) – strlen(y) >= 0) …
该条语句的结果将永远是真。strlen的结果是个无符号数,所以操作符左边的表达式也将是无符号数,而无符号数绝不可能是负的。
50、strcpy和strcat都返回它们第1个参数的一份拷贝,就是一个指向目标字符数组的指针。
51、初学者常常会编写下面这样的表达式:if(strcmp(a, b))
他以为如果两个字符串相等,它的结果将是真。但是,这个结果正好相反。
52、strpbrk函数查找任何一组字符第1次在字符串中出现的位置。这个函数返回一个指向str中第1个匹配group中任何一个字符的字符位置。
53、当strtok函数执行任务时,它将会修改它所处理的字符串。如果strtok函数的第1个参数不是NULL,函数将找到字符串的第1个标记。strtok同时将保存它在字符串中的位置。如果strtok函数的第1个参数是NULL,函数就在同一个字符串中从这个被保存的位置开始像前面一样查找下一个标记。如果字符串内不存在更多的标记,strtok函数就返回一个NULL指针。
54、直接测试或操纵字符将会降低程序的可移植性。if(ch >= ‘A’ && ch <= ‘Z’) 替换为if(isupper(ch))。
55、不可再入是指函数在连续几次调用中,即使它们的参数相同,其结果也可能不同。

第10章 结构和联合
56、即使结构的成员列表完全相同,它们的声明也会被编译器当作截然不同的类型。
57、下标引用和点操作符具有相同的优先级,它们的结合性都是从左向右。
58、警惕下面这个陷阱:
typedef struct {
int a;
SELF_REF*b;
int c;
} SELF_REF;
类型名直到声明的末尾才定义,所以在结构声明的内部它尚未定义。
59、不完整声明:它声明一个作为结构标签的标识符。然后,我们可以把这个标签用在不需要直到这个结构的长度的声明中,如声明指向这个结构的指针。接下来的声明把这个标签与成员列表联系在一起。
60、位段成员必须声明为int,signed int或unsigned int类型。其次,在成员名的后面是一个冒号和一个整数,这个整数指定该位段所占用的位的数目。
61、在一个成员长度不同的联合里,如果这些成员的长度相差悬殊,当存储长度较短的成员时,浪费的空间是相当可观的。在这种情况下,更好的方法是在联合中存储指向不同成员的指针而不是直接存储成员本身。所有指针的长度都是相同的,这样就解决了内存浪费的问题。
62、联合变量可以被初始化,但这个初始值必须是联合第1个成员的类型,而且它必须位于一对花括号里面。
DEMO:
#include<stdio.h>

struct {
int a:2;
} x;

int main() {
x.a = 1;
x.a += 1;
printf("%d\n", x.a);
}

63、malloc和calloc之间的主要区别是后者在返回指向内存的指针之前把它初始化为0。calloc的参数包括所需元素的数量和每个元素的字节数。realloc函数用于修改一个原先已经分配的内存块的大小。

第12章 使用结构和指针
64、优化链表插入函数:链表中的每个节点都有一个指向它的指针。对于第1个节点,这个指针是根指针;对于其他节点,这个指针是前一个节点的link字段。重点在于每个节点都有一个指针指向它。至于该指针是不是位于一个节点的内部则无关紧要。
/*
** Look for the right place.
*/
current = *linkp;
while(current != NULL && current->value< value) {
linkp =¤t->link;
current =*linkp;
}
循环的最后一条语句和循环之前的那条语句相同,这就促使我们对它进行简化,方法是把current的赋值嵌入到while表达式中。
while((current = *linkp) != NULL && current->value <value)
linkp =¤t->link;
65、在一个双链表中,每个节点都包含两个指针——指向前一个节点的指针和指向后一个节点的指针。存在两个根指针,一个指向链表的第1个节点,另一个指向最后一个节点。
我们可能想把两个根指针分开声明为两个变量。但这样一来,我们必须把两个指针都传递给插入函数。为根指针声明一个完整的节点更为方便,只是它的值字段绝不会被使用。
那么,怎样省略双链表中根节点的值字段呢?
如果根节点是动态分配内存的,我们可以通过只为节点的一部分分配内存来达到目的。
Node *root;
root = malloc(sizeof(Node) – sizeof(ValueType));
一种更安全的方法是声明一个只包含指针的结构。
struct DLL_NODE;

struct DLL_POINTERS {
structDLL_NODE *fwd;
structDLL_NODE *bwd;
};

struct DLL_NODE {
struct DLL_POINTERSpointers;
ValueTypevalue;
};

第13章 高级指针话题
66、int f()[];
f是一个函数,它的返回值是一个整型数组。这个声明是非法的——函数只能返回标量值,不能返回数组。
int f[]();
现在,f似乎是一个数组,它的元素类型是返回值为整型的函数。这个声明也是非法的,因为数组元素必须具有相同的长度,但不同的函数显然可能具有不同的长度。

67、int f(int); int (*pf)(int) = &f;
初始化表达式中的&操作符是可选的,因为函数名被使用时总是由编译器把它转换为函数指针。
68、int ans; ans = f(25);
该语句简单地使用名字调用函数f,函数名f首先被转换为一个函数指针,该指针指定函数在内存中的位置。然后,函数调用操作符调用该函数,执行开始于这个地址的代码。
69、用户把一个函数指针作为参数传递给其他函数,后者将“回调”用户的函数,这样的函数被称为回调函数。
70、当一个字符串常量出现于表达式中时,它的值是个指针常量。编译器把这些指定字符的一份拷贝存储在内存的某个位置,并存储一个指向第1个字符的指针。”xyz” + 1的结果是个指针,指向字符串的第2个字符:y。同样,*”xyz”的值为x。
71、以十六进制打印结果值:
putchar(“0123456789ABCDEF”[value%16]);
同样,余数将是一个0~15的值。但这次它使用下标从字符串常量中选择一个字符进行打印。这个方法定义了一个字符串,使字母和数字相邻,从而避免了复杂性。余数将从字符串中选择一个正确的数字。

第14章 预处理器
72、预处理符号
__FILE__:进行编译的源文件名
__LINE__:文件当前行的行号
__DATE__:文件被编译的日期
__TIME__:文件被编译的时间
73、相邻的字符串常量被自动连接为一个字符串。
74、#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏。下面是宏的声明方式:
#define name(parameter-list) stuff
其中,parameter- list是一个由逗号分隔的符号列表,它们可能出现在stuff中。参数列表的左括号必须与name紧邻。如果两者之间有任何空白存在,参数列表就会解释为stuff的一部分。
75、宏参数和#define定义可以包含其他#define定义的符号。但是,宏不可以出现递归。
76、使用预处理器可以把一个宏参数转换为一个字符串。#argument这种结构被预处理器翻译为”argument”。
77、宏是与类型无关的。
78、#define MALLOC(n, type) ((type *)malloc((n) * sizeof(type)))
这个宏的第2个参数是一种类型,它无法作为函数参数进行传递。例如,pi = MALLOC(25, int); pi = ((int *)malloc((25)* sizeof(int)));
同样,请注意宏定义并没有用一个分号结尾,分号出现在调用这个宏的语句中。
79、#undef name这条预处理指令用于移除一个宏定义。
80、如果所有的头文件都像下面这样编写:
#ifndef _HEADERNAME_H
#define _HEADERNAME_H 1
/*
** All the stuff that you want in the header file
*/
#endif
那么,多重包含的危险就被消除了。当头文件第1次被包含时,它被正常处理,符号_HEADERNAME_H被定义为1.如果头文件被再次包含,通过条件编译,它的所有内容被忽略。符号_HEADERNAME_H按照被包含文件的文件名进行取名,以避免由于其他头文件使用相同的符号而引起的冲突。
注意前一个例子中的定义也可以写作#define _HEADERNAME_H,效果完全一样。尽管现在它的值是一个空字符串而不是“1”,但这个符号仍然被定义。
81、当程序编译之后,#error指令允许你生成错误信息。
82、#line number “string”
它通知预处理器number是下一行输入的行号。如果给出了可选部分“string”,预处理器就把它作为当前文件的名字。值得注意的是,这条指令将修改__LINE__符号的值,如果加上可选部分,它还将修改__FILE__符号的值。
83、无效指令是一个#符号开头,但后面不跟任何内容的一行。这类指令只是被预处理器简单地删除。

第15章 输入/输出函数
84、一个常见的调试策略是把一些printf函数的调用散布于程序中,确定错误出现的具体位置。但是,这些函数调用的输出结果被写入到缓冲区中,并不立即显示于屏幕上。事实上,如果程序失败,缓冲输出可能不会被实际写入,这就可能使程序员得到关于错误出现位置的不正确结论。这个问题的解决方法就是在每个用于调试的printf函数之后立即调用fflush,如下所示:
printf(“something or other”);
fflush(stdout);
85、在MS系统中,文本文件约定以一个回车符和一个换行符(或称为行反馈符)结尾。但是,UNIX系统只使用一个换行符结尾。
86、stdin、stdout和stderr,都是一个指向FILE结构的指针。
87、如果一个文件打开是用于写入的,如果它原先已经存在,那么它原来的内容就会被删除。如果它原先不存在,那么就创建一个新文件。如果打开用于添加的文件原先并不存在,那么它将被创建。如果它原先已经存在,它原先的内容并不会被删除。无论在哪一种情况下,数据只能从文件的尾部写入。
88、在mode中添加“a+”表示该文件打开用于更新,并且流既允许读也允许写。
89、freopen函数用于打开(或重新打开)一个特定的文件流。这个函数首先试图关闭这个流,然后用指定的文件和模式重新打开这个流。如果打开失败,函数返回一个NULL值。如果打开成功,函数就返回它的第3个参数值。
90、流是用函数fclose关闭的,对于输出流,fclose函数在文件关闭之前刷新缓冲区。如果它执行成功,fclose返回零值,否则返回EOF。
91、fgetc和fputc都是真正的函数,但getc、putc、getchar和putchar都是通过#define指令定义的宏。
92、ungetc把一个先前读入的字符返回到流中,这样它可以在以后被读入。
93、scanf函数家族的格式代码都以一个百分号开头,后面可以是(1)一个可选的星号,(2)一个可选的宽度,(3)一个可选的限定符,(4)格式代码。

94、printf格式代码由一个百分号开头,后面跟(1)零个或多个标志字符,用于修改有些转换的执行方式,(2)一个可选的最小字段宽度,(3)一个可选的精度,(4)一个可选的修改符,(5)转换类型。
如果用于表示字段宽度和/或精度的十进制证书由一个星号代替,那么printf的下一个参数(必须是个整数)就提供宽度和(或)精度。因此,这些值可以通过计算获得而不必预先指定。

95、二进制输出避免了在数值转换为字符串过程中所涉及的开销和精度损失。fread函数用于读取二进制数据,fwrite函数用于写入二进制数据。函数的返回值是实际读取或写入的元素(并非字节)数目。
96、fflush迫使一个输出流的缓冲区内的数据进行物理写入,不管它是不是已经写满。调用该函数保证调试信息实际打印出来,而不是保存在缓冲区中直到以后才打印。
97、ftell函数返回流的当前位置,也就是说,下一个读取或写入将要开始的位置距离文件起始位置的偏移量。在文本流中,这个值表示一个位置,但它并不一定准确地表示当前位置和文件起始位置之间的字符数,因为有些系统将对行末字符进行翻译转换。但是,ftell函数返回的值总是可以用于fseek函数中,作为一个距离文件起始位置的偏移量。
98、在二进制流中,从SEEK_END进行定位可能不被支持,所以应该避免。在文本流中,如果from是SEEK_CUR或SEEK_END,offset必须是零。如果from是SEEK_SET,offset必须是一个从同一个流中以前调用ftell所返回的值。
99、rewind函数将读/写指针设置回指定流的起始位置。它同时清除流的错误提示标志。fgetpos和fsetpos函数分别是ftell和fseek函数的替代方案。
100、如果流当前处于文件尾,feof函数返回真。
101、tmpfile函数创建了一个文件,当文件被关闭或程序终止时这个文件便自动删除。该文件以wb+模式打开,这使它可用于二进制和文本数据。临时文件的名字可以用tmpnam函数创建。

第16章 标准函数库
102、clock函数返回从程序开始执行起处理器所消耗的时间。为了把这个值转换为秒,你应该把它除以常量CLOCKS_PER_SEC。
103、difftime函数计算time1-time2的差,并把结果值转换为秒。注意它返回的是一个double类型的值。
104、当setjmp函数第1次被调用时,它返回0。当setjmp作为longjmp的执行结果再次返回时,它的返回值是longjmp的第2个参数,它必须是个非零值。通过检查它的返回值,程序可以判断是否调用longjmp。
105、当程序被完整地测试完毕后,你可以在编译时通过定义NDEBUG消除所有地断言。可以使用-DNDEBUG编译器命令行选项或者在源文件中头文件assert.h被包含之前增加#define NDEBUG这个定义。

第17章 经典抽象数据类型
106、从二叉搜索树删除节点:解决这个问题的一种策略是不删除这个节点,而是删除它的左子树中值最大的那个节点,并用这个值代替原先应被删除的那个节点的值。
107、当一个循环数组已满时,front和rear值之间的关系和堆栈为空时一样。但是,满和空是两种不同的状态。从概念上说,为什么会出现这种情况?
我们不可能用只能表示n个不同状态的变量来表示n+1个不同的状态。

第18章 运行时环境
108、在UNIX系统中,编译器选项-S使编译器把每个源文件的汇编代码写到一个具有.s后缀的文件中。
109、在耗费时间最多的函数中,有些是库函数。
有些函数之所以耗费了大量的时间是因为它们被调用的次数非常多。
有些函数调用的次数并不多,但每次调用所花费的时间却很长。
你可以对单个函数用汇编语言重新编码,函数越小,重新编码就越容易。
110、mystdarg.h
typedef char *va_list;
#define va_start(arg_ptr, arg) arg_ptr = (char*)&arg + sizeof(arg)
#define va_arg(arg_ptr, type) *((type *)arg_ptr)++
#define va_end(arg_ptr)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: