潮州市工程建设网站seo技术培训广东
2026/5/13 4:39:06 网站建设 项目流程
潮州市工程建设网站,seo技术培训广东,什么是行业网站?,打开百度app头文件什么东西应该放在头文件里面呢#xff1f;答案是#xff1a; 1) 普通函数声明 2) 宏定义 3) 结构体、共用体模板定义 4) 枚举常量列表 5) static 函数和 inline 函数定义 6) 其他头文件 对上面的几项略作说明#xff1a;第一#xff0c;普通函数的定义不能放在头文件…头文件什么东西应该放在头文件里面呢答案是1) 普通函数声明2) 宏定义3) 结构体、共用体模板定义4) 枚举常量列表5) static 函数和 inline 函数定义6) 其他头文件对上面的几项略作说明第一普通函数的定义不能放在头文件里面因为普通函数缺省是所有文件可见的那样假如一个头文件被几个.c 源文件包含了那么当他们一起编译的时候就会出现函数重定义的错误。第二static 型的函数是可以放在头文件里面的因为这些函数被任何一个.c 源文件包含了也不会跟别的文件冲突实际上 static 型函数一般都放在头文件里面。第三inline 函数缺省就是 static 型函数因此也一般被放在头文件里。第四由于头文件还可以嵌套包含别的头文件为了防止头文件被重复包含头文件的书写格式是有一定要求的比如一个名字为 head.h 的头文件他一般是这么写的vincentubuntu:~/ch02/2.6$ cat head.h -n1 #ifndef _HEAD_H_ // 如果没有定义此宏2 #define _HEAD_H_ // 则马上定义此宏34 ... 5 ... // 头文件正文6 ... 78 #endif上述代码中的条件编译语句和宏定义能确保该头文件不会被重复包含因为如果存在重复包含那么在第二次判断宏 _HEAD_H_ 是否被定义的时候就会失败。顺带说一句这里的宏名一般就是将头文件名写成大写字母前后加下划线即可目的是让宏名唯一。宏macro在 C 语言中宏具有不可替代的地位宏分为两种一种是不带参的另一种是带参宏。不带参数的宏非常简单比如#define PI 3.14这样在以后的程序代码中凡是要用到圆周率 3.14 的地方都可以使用 PI 来表示这样一来有两个好处一是程序代码使用英文单词替代数字使得程序更具可读性。二是修改起来方便想象一下程序中用到了 100 此圆周率后来发现精度要变成 3.141592654如果我们没有定义宏的话则需要修改 100 处代码有了宏之后则只需修改一处即可。宏定义不一定要给她一个值空值也行比如上面提到的防止头文件重复包含的那里例子定义的 _HEAD_H_ 就是空的#define _HEAD_H_ 这样宏_HEAD_H_就被定义了是空值。而登顶 C 语言语法最高难度的非带参宏莫属这结论绝非空穴来风。在 Linux 源码中顶尖黑客们对 C 语言的宏也情有独钟撇去复杂性不说宏确实有其优越性宏不需要像函数那样来回切换宏替换发生在编译的预处理阶段因而省去了函数切换的时间花销这个原理跟 inline 函数是一样的。另外由于宏是直接将其参数进行替换中间没有实参形参的计算因此传递的是参数的名字而不是参数的类型和值这是和函数最大的不同。来看一个使用带参宏的简单范例vincentubuntu:~/ch02/2.6$ cat macro.c -n1 #include stdio.h23 #define MAX(a, b) ab ? a : b // 定义一个求最大值的带参宏45 void show(int a, int b, int m)6 {7 printf(a%d, b%d, m%d\n, 8 a, b, m);9 }1011 int main(void)12 {13 int a100, b200;14 int m0;1516 show(a, b, m);17 m MAX(a, b); // 使用 MAX( )求出最大值赋给 m18 show(a, b, m);1920 return 0;21 }vincentubuntu:~$ ./macroa100, b200, m0a100, b200, m200注意上述代码的第 17 行站在使用者的角度看宏的使用除了大写字母之外跟函数调用感觉是一样的。而实际上编译该文件的时候 MAX( )将会被其定义语句替代我们可以使用 -E 选项来查看宏替换之后的程序的真实面孔vincentubuntu:~/ch02/2.6$ gcc macro.c -o macro.i -Evincentubuntu:~/ch02/2.6$ cat macro.i -n …………852853854 void show(int a, int b, int m)855 {856 printf(a%d, b%d, m%d\n, 857 a, b, m);858 }859860 int main(void)861 {862 int a100, b200;863 int m0;864865 show(a, b, m);866 m ab ? a : b; // 注意此处宏已经被替换867 show(a, b, m);868869 return 0;870 }世界上永远没有绝对免费的午餐这种“直截了当”的宏替换是把双刃剑她在节省了函数切换时间的同时也隐藏着逻辑陷阱比如我们改造一下上述代码变成vincentubuntu:~/ch02/2.6$ cat macro.c -n ……1516 show(a, b, m);17 m MAX(a|-1, b); // 求出表达式 a|-1 和 b 的最大值18 show(a, b, m);1920 return 0;21 }vincentubuntu:~/ch02/2.6$ ./macroa100, b200, m0a100, b200, m-1我们修改了一下 MAX( )参数发现执行的结果跟预料的不相符。本来任何数与-1进行位或运算都将得到-1因为-1 的二进制补码是全 1即 0xFFFFFFFF任何数位或这个值都将变成全 1这样一来 b 当然要比 a|-1 要大但是可惜结果却打印了-1 而不是200这是因为宏替换后的代码是这样的vincentubuntu:~/ch02/2.6$ cat macro.i -n ……863 int m0;864865 show(a, b, m);866 m a|-1b ? a|-1 : b; // 注意此处宏已经被替换867 show(a, b, m);868869 return 0;870 }可以看到之所以出现逻辑谬误是因为宏替换之后出现了运算符优先级的问题。如果我们直接编写代码上述代码的第 866 行应该写成m (a|-1) (a|-1) : b;这样才能保证运算时不受运算符优先级的困扰也就是说我们在定义宏的时候由于参数并不会像函数的实参那样经过计算再传递给形参而是简单粗暴地直接替换因此宏的“参数”其实是一个表达式而不是一个数据既然是表达式比如 a|-1就必须用括号括起来防止由于优先级的问题而导致计算结果谬误我们的代码改成vincentubuntu:~/ch02/2.6$ cat macro.c -n1 #include stdio.h23 #define MAX(a, b) ((a)(b) ? (a) : (b)) // 将该括起来的地方都括起来45 void show(int a, int b, int m)6 {7 printf(a%d, b%d, m%d\n, 8 a, b, m);9 }1011 int main(void)12 {13 int a100, b200;14 int m0;1516 show(a, b, m);17 m MAX(a|-1, b);18 show(a, b, m);1920 return 0;21 }vincentubuntu:~/ch02/2.6$ ./macroa100, b200, m0a100, b200, m200注意第 3 行的宏定义我们将能括起来的地方都括起来了再看程序的执行结果果然输出了正确的答案。但是还有潜在的隐患比如我们传递的宏参数如果带有自增自减运算符尽管已经加了括号但计算的结果可能还是会出现谬误vincentubuntu:~/ch02/2.6$ cat macro.c -n1 #include stdio.h23 #define MAX(a, b) ((a)(b) ? (a) : (b))45 void show(int a, int b, int m)6 {7 printf(a%d, b%d, m%d\n, 8 a, b, m);9 }1011 int main(void)12 {13 int a100, b200;14 int m0;1516 show(a, b, m);17 m MAX(a, b); // 我们期待的结果b 在自增 1 之后应该是 20118 show(a, b, m);1920 return 0;21 }vincentubuntu:~/ch02/2.6$ ./macroa100, b200, m0a100, b202, m201从程序执行结果看变量 b 的值发生了奇怪的变化本来应该自增 1 的答案却变成了 202。这是宏参数直接替换的结果我们来看看预处理之后的代码马上就有分晓vincentubuntu:~/ch02/2.6$ cat macro.i -n ……864865 show(a, b, m);866 m ((a)(b) ? (a) : (b));867 show(a, b, m);868869 return 0;870 }注意到代码中宏被替换之后b出现了两次因此如果条件表达式的第一个语句结果为假的话 b 就会被自增两次这是造成 b 的值不符合预期结果的原因。要解决这个问题就必须保证宏参数在宏定义中只出现一次怎么做到请看代码vincentubuntu:~/ch02/2.6$ cat macro.c -n1 #include stdio.h23 #define MAX(a, b) \4 ({ \5 int _a a; \6 int _b b; \提示7 ((_a)(_b) ? (_a) : (_b)); \8 })9……vincentubuntu:~/ch02/2.6$ ./macroa100, b200, m0a100, b201, m200在这个版本的宏定义中改进了不少东西1在宏的内部定义了两个变量_a 和_b 来替换 a 和 b防止 a 和 b 出现多次。2由于宏定义出现了多条语句因此必须用{……}将他们括起来形成一个复合语句。3根据 C 语言语法复合语句不能出现在表达式当中而我们的宏的调用不能有这个限制因而在花括号的外边再加一对儿圆括号变成类似({……})这样的东东这东西叫语句表达式是 GNU 的扩展语法。4每一条语句的后面都必须有一个反斜杠来结束哪怕是空行。这是预处理指令的规定任何一条预处理指令只能写在一个逻辑行之中如果这个逻辑行有多个物理行则每个物理行必须用反斜杠来结束。该范例中的宏使用了所谓的语句表达式的东西来将具有多条语句的复杂宏囊括起来实际上在 Linux 内核中除了这种方式之外还有一种称之为 do……while(0)的方式都可以用来实现这种复杂宏的编写拿一个现实的例子比如 Linux-2.6.35/fs/aio.c 中的一段代码203 ……204 #define put_aio_ring_event(event, km) do { \205 struct io_event *__event (event); \206 (void)__event; \207 kunmap_atomic(((unsigned long)__event PAGE_MASK), km); \208 } while(0)209 ……上述代码中205-207 三行代码被 do……while(0)包含目的就是让这三行代码组成一个整体而且只执行一遍。最后果然打印出了正确的答案。但是客户的需求是无止境的他还会要求我们的宏能同时处理整型和浮点型。因此我们不能在宏定义里面写死为整型而要让程序自动获得宏参数的类型代码进一步变成vincentubuntu:~/ch02/2.6$ cat macro.c -n1 #include stdio.h23 #define MAX(a, b) \4 ({ \5 typeof(a) _a a; \6 typeof(b) _b b; \7 ((_a)(_b) ? (_a) : (_b)); \8 })9……注意到第 5、6 行的关键字 typeof这个关键字也是 GNU 的扩展语法可以用他来取得一个数据的类型这样一来不管传递给宏的是整型还是浮点型数据宏都可以自动处理。但是可是可但是客户的需求是变态的他会接着提到如果我将两个不同类型但兼容的数据丢给 MAX( )比如一个 3.14 和一个 100他应该也要得出正确的结果哦而另一方面毕竟他们类型不同嘛所以最好在编译的时候要给我些提示哦面对这个坑爹的要求顽强如仙人掌的我们毫不屈服一口气将代码进一步改成这样vincentubuntu:~/ch02/2.6$ cat macro.c -n1 #include stdio.h23 #define MAX(a, b) \4 ({ \5 typeof(a) _a a; \6 typeof(b) _b b; \7 (void)(_a _b); \8 ((_a)(_b) ? (_a) : (_b)); \9 })1011 void show(int a, float b, float m)12 {13 printf(a%d, b%.2f, m%.2f\n, 14 a, b, m);15 }1617 int main(void)18 {19 int a 100;20 float b 3.14;2122 float m0;2324 show(a, b, m);25 m MAX(a, b);26 show(a, b, m);2728 return 0;29 }vincentubuntu:~/ch02/2.6$ gcc macro.c -o macromacro.c: In function ‘main’:macro.c:25:6: warning: comparison of distinct pointer types lacks a cast[enabled by default]vincentubuntu:~/ch02/2.6$ ./macroa100, b3.14, m0.00a100, b4.14, m100.00首先注意到执行结果是正确的其次编译的时候也确实有因为数据类型不同而给出的警告他们这两方面无耻的需求居然也被我们满足了。注意到代码的宏定义部分我们所做的更改是加了第 7 行那条语句使用一个判断来强迫编译器对比两个变量_a 和_b 的地址的类型不能直接比较_a 和_b 的值因为即使浮点和整型不同但仍是兼容的由此来触发由于类型不同而产生的警告。前面的强类型转换(void)目的是要让编译器认为后面的比较语句是有作用的从而不会因为误以为没有实际作用而报出其他我们不需要的警告。实际上这个 MAX( )宏定义代码出自 Linux 内核源码是世界上最聪明绝顶的 Linux黑客写的这些代码与其说为了实现一些苛刻的要求不如说更像一个展现 C 语言运用的舞台将一门语言的各个方面运用得淋漓尽致。IT 大拿用技术秀智商既使得 Linux 变得越来越强悍甚至变态也给这些为开源事业做出卓越贡献的黑客与之相匹配的荣誉青史留名在源码中有详细的源码贡献者名单。这样的荣誉是非常重要的因为世界上并没有一家公司或者个人给这些志愿者们因为贡献 Linux 源码而支付薪水。条件编译#ifdef MACROsome statements#endif含义是如果定义了宏 MACRO则编译 some statements否则不编译。注意条件编译都必须使用#endif 作为结尾。vincentubuntu:~/ch02/2.6$ cat thread_pool.c -n1 #include thread_pool.h 23 void handler(void *arg)4 {5 pthread_mutex_unlock((pthread_mutex_t *)arg);6 }78 void *routine(void *arg)9 {10 #ifdef DEBUG // 此处调试信息若需要则定义 DEBUG否则关闭11 printf([%u] is started.\n, 12 (unsigned)pthread_self());13 #endif1415 thread_pool *pool (thread_pool *)arg;16 struct task *p;17……在以上的例子中如果我们需要第 11 行和第 12 行的调试信息则在编译的时候添加-DDEBUG 选项即可否则这两行信息将被舍弃。第二种形式#ifndef MACROsome statements#endif含义是如果没定义宏 MACRO则编译 some statements否则不编译。这种形式的逻辑跟第一种形式刚好相反最常用的地方是在头文件中使得头文件的内容不会被重复包含。第三种形式#if expressionsome statements#endif含义是如果表达式 expression 的值为真则编译 some statements否则不编译。注意这里的 expression 如果是一个宏则必须是一个整数或者一个整型表达式比如vincentubuntu:~/ch02/2.6$ cat skbuff.h -n | more ……158 #if (BITS_PER_LONG 32) || (PAGE_SIZE 65536)159 __u32 page_offset;160 __u32 size;161 #else162 __u16 page_offset;163 __u16 size;164 #endif165 };166……这段阶段的代码片段来自 Linux 源码的 inlucde/linux/skbuff.h注意到第 158行使用了条件编译其中的 BITS_PER_LONG 和 PAGE_SIZE 是两个值为整数的宏。另外还注意到第 161 行跟 C 语言的 if-else 结构相似条件编译语句也可以使用二路分支语句#else但是要注意前面有井号#。当声明变得复杂的时候我们希望能简化他使得代码看起来更加简洁。关键字 typedef可以帮到忙他的作用就是给一种类型取一个别名请看范例vincentubuntu:~/ch02/2.6$ cat typedef.c -n1 #include stdio.h2 #include stdlib.h3 #include unistd.h4 #include string.h5 #include strings.h67 int main(void)8 {9 // 给 int 取个别名叫 INTEGER使用它们定义整型 a 是等价的10 typedef int INTEGER;11 INTEGER a;12 int a;1314 // 给 char *取个别名叫 String使用它们定义字符指针 b 是等价的15 typedef char *String;16 String b;17 char * b;1819 // 给具有 5 个元素的数组类型 int [5]取个别名叫 ARRAY20 typedef int ARRAY[5];21 ARRAY c; // 使用它们定义具有 5 个 int 型元素的数组 c 是等价的22 int c[5];2324 // 给函数指针类型 void (*)(int)取个别名叫 FUNC_POINTER25 typedef void (*FUNC_PTR)(int);26 FUNC_PTR pf ; // // 使用它们定义函数指针 pf 是等价的27 void (*pf)(int);2829 return 0;30 }再来看一个使用 typedef 简化声明的实际范例我们先来查看 man 帮助文档中库函数signal 的定义vincentubuntu:~$ man 3posix signal | head -n 15SYNOPSIS#include signal.hvoid (*signal(int sig, void (*func)(int)))(int); ……在这里signal 函数的定义被写在一条语句上看起来复杂得很“可怕”仔细看其实发现该函数的第二个参数和其返回值类型是一样的即函数指针void (*)(int)再来看 man 帮助文档中系统调用函数 signal 的定义vincentubuntu:~$ man 2 signal | head -n 15SYNOPSIS#include signal.htypedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler); ……此处将这种函数指针类型 void (*)(int) 用 typedef 抽取出来定义了一个别名叫sighandler_t因此看起来就没那么面目可憎了。由上图可知他们的类型可以使用 typedef 来统一名称称之为 sighandler_t再使用这个别名来定义这个函数即可。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询