当前位置: 首页 > news >正文

个人网站备案做论坛网站开发需要的准备

个人网站备案做论坛,网站开发需要的准备,2022好用值得推荐的搜索引擎,财务公司经营范围有哪些​ 六、C语言宏定义与预处理、函数和函数库 6.1 编译工具链 源码.c —— (预处理)——预处理过的.i文件——(编译)——汇编文件.S——(汇编)——目标文件.o-(链接)——elf可执行程序 预处理用预处理器#xff0c;编译用编译器#xff0c;…​ 六、C语言宏定义与预处理、函数和函数库 6.1 编译工具链 源码.c —— (预处理)——预处理过的.i文件——(编译)——汇编文件.S——(汇编)——目标文件.o-(链接)——elf可执行程序 预处理用预处理器编译用编译器汇编用汇编器链接用链接器这几个工具再加上其他一些额外的会用到的可用工具合起来叫编译工具链(gcc就是一个编译工具链)。 gcc中各选项的使用方法 -E只预处理不编译执行头文件的扩展、宏替换、条件编译筛选、去掉注释等生成.i文件-S只编译不汇编将C/C语言程序翻译成汇编语言生成.S文件-c只汇编不链接将汇编语言翻译成机器指令生成.o文件-o链接将目标文件和库文件进行链接得到可执行文件生成elf可执行文件 预处理的意义 编译器本身的主要目的是编译源代码将C的源代码转化成.S的汇编代码。编译器聚焦核心功能后就剥离出了一些非核心的功能到预处理器去了。预处理器帮编译器做一些编译前的杂事。如 gcc编译时可以给一些参数来做一些设置譬如gcc xx.c -o xx可以指定可执行程序的名称譬如gcc xx.c -c -o xx.o可以指定只编译不连接仅生成.o目标文件。gcc -E xx.c -o xx.i可以实现只预处理不编译。一般情况下没必要只预处理不编译但有时候这种技巧可以用来帮助我们研究预处理过程帮助debug程序。链接器链接的时候是把目标文件二进制通过有序的排列组合起来如star.s、main.c、led.c这三个源文件分别被编译成三个目标文件每个目标文件有很多函数集合。链接的时候会根据运行思路把这些杂乱的函数给排列组合起来不是把目标文件简单的排列组合。可执行程序含有很多符号信息符号表里面的符号与一个个地址相对应如函数名max对应一个地址但在执行时有套程序会把这些符号信息给过滤掉得到纯净的二进制代码最后把他们加载到内存中去。debug版本就是有符号信息而Release版本就是纯净版本的。strip工具strip是把可执行程序中的符号信息给拿掉以节省空间。objcopy工具可将可执行程序转变成可烧录的bin文件。 6.2 预处理 6.2.1 #define的用法 #define 是一个预处理指令这个预处理执行可以定义宏。与所有预处理指令一样预处理指令#define用#符号作为行的开头。预处理指令从#开始到其后第一个换行符为止。也就是说指令的长度限于一行代码。如果想把指令扩展到几个物理行可使用反斜线后紧跟换行符的方法实现该出的换行符代表按下回车键在源代码文件中新起一行所产生的字符而不是符号 \n 代表的字符。在预处理开始钱系统会删除反斜线和换行符的组合从而达到把指令扩展到几个物理行的效果。可以使用标准C注释方法在#define行中进行注释。 //使用反斜线回车 #define OW hello\ world! /*注意第二行要左对齐*/每一个#define行由三部分组成 第一部分指令#deine自身。第二部分所选择的缩略语这些缩略语称为宏分为对象宏和函数宏。宏的名字中不允许有空格而且必须遵循C变量命名规则只能使用字母、数字和下划线_第一个字符不能为数字。习惯上宏名用大写字母表示以便于与变量区别。但也允许用小写字母。第三部分#define行的其余部分称为替换列表或主体。 注意结尾没有分号* 下面来看一个例子 #include stdio.h #define OW 2 * 2 #define OW 2 * 2 //#undef OW 需要先取消宏定义 #define OW 2*2 int main (void) { printf (%d\n, OW); return 0; } 输出结果 define.c:5:0: 警告 “OW”重定义 [默认启用] define.c:4:0: 附注 这是先前定义的位置相同定义意味着主体具有相同顺序的语言符号。因此下面两个定义相同(两者都有三个相同的语言符号而且额外的空格不是主体的一部分)#define OW 2 * 2 #define OW 2 * 2下面的定义则被认为是不同的(只有一个而非三个语言符号因此与前面两个定义不同)#define OW 2*2可以使用#undef指令重新定义宏。 6.2.1.1 宏所代表的数字可以在编译命令中指定使用-D选项 /* 宏演示 */ #include stdio.h int main() { int num0; int arr[SIZE]{}; //使用gcc -D可以宏定义这个数字 for(num 0;num SIZE - 1;num){ arr[num]num; printf(%d ,arr[num]); } printf(\n); return 0;}/*gcc -DSIZE4 define.c输出结果0 1 2 3*/6.2.1.2 函数宏 通过使用参数可以创建外形和作用都与函数相似的类函数宏。宏的参数也用圆括号括起来。类函数宏的定义中用圆括号括起来一个或多个参数随后这些参数出现在替换部分。 #include stdio.h #define SQUARE(X) X*X #define PR(X) printf (The result is %d\n, X) int main (void) { int x 4; int z; printf (x %d\n, x); z SQUARE(x); printf (Evaluating SQUARE(x): ); PR(z); z SQUARE(2); printf (Evaluating SQUARE(2): ); PR(z); printf (Evaluating 100/SQUARE(2): ); PR(100/SQUARE(2)); z SQUARE(x2); printf (Evaluating SQUARE(x2): ); PR(z); printf (x is %d\n, x); z SQUARE(x); printf (Eavluating SQUARE(x): ); PR(SQUARE (x)); printf (After incrementing, x is %x\n, x); return 0;}/*输出结果x 4Evaluating SQUARE(x): The result is 16Evaluating SQUARE(2): The result is 4Evaluating 100/SQUARE(2): The result is 100Evaluating SQUARE(x2): The result is 14x is 4Eavluating SQUARE(x): The result is 36After incrementing, x is 6*/SQUARE(x2) 输出结果是14而不是想要的66 36。这是因为预处理器不进行计算而只进行字符串替换。在出现x的地方预处理都用字符串 x2进行替换。xx 变为 x2*x2 根据运算符优先级则结果为 14100/SQUARE(2)输出结果是 100而不是想要的 25。因为根据优先级规则表达式是从左到右求值的100/2*2 100。要处理前面两个示例中的情况需要如下定义 #define SQUARE(x) ((x) * (x))。从中得到的经验是使用必须的足够多的圆括号来保证以正确的顺序进行运行和结合。SQUARE(x) 根据编译器的不同会出现两种不同的结果。解决这个问题的最简单的方法是避免在宏的参数中使用x。一般来说在宏中不要使用增量或减量运算符。 参看C 语言再学习 – 运算符与表达式 6.2.1.3 利用宏参数创建字符串#运算符 在类函数宏的替换部分中#符号用作一个预处理运算符它可以把语言符号转化为字符串。例如如果x是一个宏参量那么#x可以把参数名转化为相应的字符串。该过程称为字符串化。 #include stdio.h #define PSQR(x) printf(The square of #x is %d\n, ((x)*(x))) int main (void) { int y 2; PSQR (y); PSQR (2 4); return 0; } /* 输出结果 The square of y is 4 The square of 2 4 is 36 */#include stdio.h #include string.h #define VEG(n) #nint main() { char str[20]; strcpy(str,VEG(num)); //num printf(%s\n,str); //拷贝 return 0; } /* 输出结果 num */6.2.1.4 预处理器的粘合剂##运算符 和#运算符一样##运算符可以用于类函数宏的替换部分。另外##还可用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。 #include stdio.h #define XNAME(n) x##n #define PRINT_XN(n) printf (x#n %d\n, x##n) int main (void) { int XNAME (1) 14; //变为 int x1 14; int XNAME (2) 20; //变为 int x2 20; PRINT_XN (1); //变为 printf (x1 %d\n, x1); PRINT_XN (2); //变为 printf (x2 %d\n, x2); return 0; } /* 输出结果 x1 14 x2 20 */6.2.1.5 宏用于简单函数 #include stdio.h #define MAX(x,y) ((x)(y) ? (x) : (y)) /*比较大小*/ #define ABS(x) ((x) 0 ? -(x) : (x)) /*绝对值*/ #define ISSIGN(x) ((x) || (x) - ? 1 : 0) /*正负号*/ int main() { printf (较大的为 %d\n, MAX(5,3)); printf (绝对值为 %d\n, ABS (-2)); printf (正负号为 %d\n, ISSIGN ()); return 0; } /* 输出结果 较大的为 5 绝对值为 2 正负号为 1 */下面是需要注意的几点 宏的名字中不能有空格但是在替代字符串中可以使用空格。ANSI C 允许在参数列表中使用空格。用圆括号括住每个参数并括住宏的整体定义。用大写字母表示宏函数名便于与变量区分。有些编译器限制宏只能定义一行。即使你的编译器没有这个限制也应遵守这个限制。宏的一个优点是它不检查其中的变量类型这是因为宏处理字符型字符串而不是实际值。 面试用预处理指令#define 声明一个常数用以表明1年中有多少秒忽略闰年问题 #define SEC (60*60*24*365)UL考察内容 1、懂得预处理器将为你计算常量表达式的值因此可直接写出你是如何计算一年中有多少秒而不是计算出实际的值这样更清晰而没有代价。2、意识到这个表达式将使一个16 位机的整形数溢出因此要用到长整形符号 L 告诉编译器这个常数是长整形数。 面试写一个“标准”宏MIN 这个宏输入两个参数并返回较小的一个 #define MIN(A,B) ((A) (B) ? (A) : (B))考察内容 1、三目表达式的使用2、使用必须的足够多的圆括号来保证以正确的顺序进行运行和结合3、进一步讨论在宏中不要使用增量或减量运算符 参看宏名必须用大写字母吗? 研究C语言中用宏定义define表示数据类型和用typedef定义数据类型有什么区别 宏定义只是简单的字符串代换是在预处理完成的而typedef是在编译时处理的它不是作简单的代换而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。#define P1 int * typedef (int *) P2从形式上看这两者相似但在实际使用中却不相同 P1 a, b; 在宏代换后变成 int *a, b; 表示 a 是指向整型的指针变量而 b 是整型变量。P2 a, b; 表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。 总结typedef和#define的不同之处 1、与#define不同typedef 给出的符号名称仅限于对类型而不是对值。2、typedef 的解释由编译器而不是是处理器执行。3、虽然它的范围有限但在其受限范围内typedef 比 #define 更灵活。 6.2.1.6 用于定义字符串尤其是路径 A)定义路径#define ENG_PATH_1 E:\English\listen_to_this\listen_to_this_3 B)定义字符串#define ENG_PATH_2 E:\English\\listen_to_this\\listen_to_this_3 6.2.2 #undef 指令 取消定义一个给定的 #define 例如有如下宏定义#define LIMIT 40则指令#undef LIMIT会取消该定义。现在就可以重新定义LIMIT以使它有一个新的值。即使开始没有定义LIMIT取消LIMIT的定义也是合法的。如果想使用一个特定名字但又不能确定前面是否已经使用了该名字为安全起见就可以取消该名字的定义。注意#define 宏的作用域从定义点开始直到用 #undef 指令取消宏为止或直到文件尾为止由二者中最先满足的那个结束宏的作用域。还应注意如果用头文件引入宏那么#define 在文件中的位置依赖 #define 指令的位置。 #include stdio.h #define X 3 #define Y X*3 #undef X #define X 2 int main (void) { printf (Y %d\n, Y); printf (X %d\n, X); return 0; }/* 输出结果 Y 6 X 2 */#include stdio.h #define X 3 #define Y X*3 #define X 2 //不可重复定义 int main (void) {int z Y; printf (Y %d\n, z); printf (X %d\n, X); return 0; }/* 输出结果 test.c:4:0: 警告 “X”重定义 [默认启用] test.c:2:0: 附注 这是先前定义的位置 */6.2.3 文件包含#include 预处理器发现#include指令后就会寻找后跟的文件名并把这个文件的内容包含但当前文件中。被包含文件中的文件将替换源代码文件中的#include指令就像你把被包含文件中的全部内容键入到源文件中的这个特定位置一样。#include 指令有两种使用形式 文件名放在尖括号中#include filename.h 在UNIX系统中尖括号告诉预处理器在一个或多个标准系统目录中寻找文件。/usr/include 、kernel/include 文件名放在双引号中#include filename.h 在UNIX系统中双引号告诉预处理器现在当前目录或文件名中指定的其他目录中寻找文件然后在标准位置寻找文件。 扩展C语言再学习 – 常用头文件和函数转 Lniux的文件系统中有一个大分组它包含了文件系统中所有文件这个大的分组用一个专门的目录表示这个目录叫做根目录根目录可以使用“/”表示。 路径可以用来表示文件或者文件夹所在的位置路径是从一个文件夹开始走到另一个文件夹或者文件位置中间的这条路。把这条路经过的所有文件夹名称按顺序书写出来的结果就可以表示这条路。 路径分为绝对路径和相对路径 : 绝对路径起点必须是根目录如 /abc/def 所有绝对路径一定是以“/”作为开头的 相对路径可以把任何一个目录作为起点如…/…/abc/def 相对路径编写时不应该包含起点位置 。相对目录中“…”表示上层目录用“.”表示当前目录。 终端窗口里的当前目录是所有相对路径的起点当前目录的位置是可以修改的。 pwd 命令可以用来查看当前目录的位置 cd 命令可以用来修改当前目录位置 ls 命令可以用来查看一个目录的内容 6.2.4 条件编译 参看条件编译#ifdef的妙用详解_透彻 #if表示如果… #ifdef 表示如果定义… #ifndef表示如果没有定义… #else表示否则…与#ifdef、#ifndef搭配使用。 #elif表示否则如果…与#if、#ifdef、#ifndef搭配使用。没有#elseif #endif表示结束判断与#if、#ifdef、#ifndef搭配使用 注意#if 和 if 区别 #if主要用于编译期间的检查和判断if主要用于程序运行期间的检查和判断 (1)最常见的形式: #ifdef 标识符 程序段1 #else 程序段2 #endif 当标识符已经被定义过一般用#define命令定义则对程序段1进行编译否则编译程序段2。#else部分也可以没有即#ifdef 标识符程序段1 #endif这里的“程序段”可以是语句组也可以是命令行。这种条件编译可以提高C源程序的通用性。如果一个C源程序在不同计算机系统上运行而不同的计算机又有一定的差异。例如我们有一个数据类型在Windows平台中应该使用long类型表示而在其他平台应该使用float表示这样往往需要对源程序做必要的修改这就降低了程序的通用性。可以用以下的条件编译#ifdef WINDOWS #define MYTYPE long #else #define MYTYPE float #endif 如果在Windows上编译程序则可以在程序的开始加上#define WINDOWS这样则编译#define MYTYPE long如果在这组条件编译命令之前曾出现#define WINDOWS 0则预编译后程序中的MYTYPE都用float代替。 这样源程序可以不必任何修改就可以用于不同类型的计算机系统。当然以上介绍的只是一种简单的情况可以根据此思路设计出其他的条件编译。 例如在调试程序时常常希望输出一些所需的信息而在调试完成后不再输出这些信息。可以在源程序中插入以下的条件编译段#ifdef DEBUG print (device_open(%p)\n, file); #endif 如果在它的前面有#define DEBUG则在程序运行时输出file指针的值以便调试分析。调试完成后只需将这个define命令行删除即可。有人可能觉得不用条件编译也可以达到此目的即在调试时加一批printf语句调试后再将prntf语句全部删除。的确这是可以的。但是当调试时加的printf语句比较多时修改的工作量是很大的。用条件编译则不必一一删除printf语句。只需删除前面的一条#define DEBUG 命令即可这时所有的用DEBUG 作标识符的条件编译段都使其中的printf语句不起作用起到统一控制的作用如同一个“开关”一样。 (2)有时也采用下面的形式 #ifndef 标识符 程序段1 #else 程序段2 #endif 只是第一行与第一种形式不同将#ifdef改为#ifndef。它的作用是若标识符未被定义则编译程序段1否则编译程序段2。这种形式与第一种形式的作用相反。 一般地当某文件包含几个头文件而且每个头文件都可能定义了相同的宏使用#ifndef可以防止该宏重复定义。 /*test.h*/ #ifndef SIZE #define SIZE 100 #endif#ifndef 指令通常用于防止多次包含同一文件也就是说头文件可采用类似下面几行的设置 //头文件卫士 #ifndef THINGS_H_ #define THINGS_H_ #endif(3) #if 后面跟一个表达式而不是一个简单的标识符 #if 表达式 程序段1 #else 程序段2 #endif 当指定的表达式为真非零时就编译程序段1否则编译程序段2。可以事先给定一定条件使程序在不同的条件下执行不同的功能。例如 #include stdio.h #define LETTER 1 int main (void) { #if LETTER printf (111\n); #else printf (222\n); #endif return 0; } /* 输出结果 111 */这种形式也可以用作注释用#if 1 和 #if 0 #include stdio.h int main (void) { #if 0 printf (111\n); #endif printf (222\n); return 0; } /* 输出结果 222 */(4)最后一种形式 #if 标识符 程序段 #elif 程序段1 #elif 程序段2 ... #else 程序段n #endif#if...#elif任意多次...#else...#endif以上结构可以从任意逻辑表达式选择一组编译这种结构可以根据任意逻辑表达式进行选择。/* 条件编译演示 */ #include stdio.h #define SANint main() { #if defined(YI) //布尔值 printf(1\n); #elif defined(ER) //布尔值 printf(2\n); #elif defined(SAN) printf(3\n); #else printf(4\n); #endif return 0; } /* 输出结果 3 */这里define是一个预处理运算符。如果 define 的参数已用#define定义过那么define返回1否则返回 0 。这种方法的优点在于它可以和#elif一起使用。 应用示例 假设我们已在程序首部定义#define DEBUG #define TEST利用#ifdef 和 #endif 将程序功能模块包括进去以向某用户提供该功能 //在程序首部定义#define HNLD #ifdef HNLD includen166_hn.c #endif如果不许向别的用户提供该功能则在编译之前将首部的HNLD加下划线即可。 在每一个子程序前加上标记以便追踪程序的运行 #ifdef DEBUG printf( Now is in hunan !); #endif避开硬件的限制。有时一些具体应用环境的硬件不一样但限于条件本地缺乏这种设备于是绕过硬件直接写出预期结果。具体做法是 #ifndef TEST idial(); //程序调试运行时绕过此语句 #else i0; #endif //调试通过后再屏蔽TEST的定义并重新编译即可发给用户使用了。确保使用的标识符在其他任何地方都没有定义过 通常编译器提供商采用下述方法解决这个问题用文件名做标识符并在文件名中使用大写字母、用下划线代替文件名中的句点字符、用下划线可能使用两条下划线做前缀和后缀。例如检查头文件read.h可以发现许多类似的语句 #ifndef __READ_H__ //防止被重复定义 #define __READ_H__ extern int num0; #endif参看C语言再学习 – 标识符 6.2.5 扩展extern “C” 通过 extern C 可以要求 C 编译器按照 C方式处理函数接口即不做换名当然也就无法重载。 C 调 C在 C 的头文件如下设置 extern C int add (int x, int y); extern C {int add (int x, int y);int sub (int x, int y); }//示例 add.h #ifndef _ADD_H #define _ADD_H#ifdef __cplusplus extern C { #endifint add (int ,int );#ifdef __cplusplus } #endif#endifC 调 C在C 的主函数如下设置 extern C { #include chead.h }//示例 main.cpp #include iostream using namespace std;extern C { #include 05sub.h }int main (void) { int x456,y123; cout x y sub(x, y) endl; return 0; }6.2.6 预定义宏 __DATE__进行预处理的日期“mm dd yyyy”形式的字符串文字 __FILE__代表当前源代码文件名的字符串 __BASE_FILE__获取正在编译的源文件名 __LINE__代表当前源代码文件中的行号 __TIME__源文件编译时间格式为“hh: mm: ss” __STDC__设置为 1时表示该实现遵循 C标准 __STDC_HOSTED__为本机环境设置为 1否则设为 0 __STDC_VERSION__为C99时设置为199901LC11为201112LC18、C17为201710L __FUNCTION__或者 __func__获取所在的函数名预定义标识符而非预定义宏 //part.c #include stdio.h int main (void) { printf (The file is %s\n, __FILE__); printf (The base_file is %s\n, __BASE_FILE__); printf (The line is %d\n, __LINE__); printf (The function is %s\n, __FUNCTION__); printf (The func is %s\n, __func__); printf (The date is %s\n, __DATE__); printf (The time is %s\n, __TIME__); return 0; }/*输出结果The file is part.cThe base_file is part.cThe line is 6The function is mainThe func is mainThe date is Nov 22 2016The time is 15:46:30*/6.2.7 常用的新指令 #line 整数n 表示修改代码的行数/指定行号 插入到程序中表示从行号n开始执行修改下一行的行号为n #error 字符串 表示产生一个错误信息 #warning 字符串 表示产生一个警告信息 //#line 预处理指令的使用 #include stdio.h #line 200 int main(void) { printf(The line is %d\n,__LINE__); return 0; } /* 输出结果 The line is 203 *///#error和#warning的使用 #include stdio.h #define VERSION 4 #define VERSION 2 #define VERSION 3#if(VERSION 3) #error 版本过低 #elif(VERSION 3) #warning 版本过高 #endif int main(void){ printf(程序正常运行\n); return 0; } /* 输出结果 警告 #warning 版本过高 //错误 #error 版本过低 //程序正常运行 */6.2.8 #pragma #pragma GCC dependency 文件名 表示当前文件依赖于指定的文件如果当前文件的最后一次修改的时间早于依赖的文件则产生警告信息 #include stdio.h #pragma GCC dependency 01print.c //当前程序依赖于01print.c文件 int main(void){ printf(Good Good Study,Day Day Up!\n); return 0; } /* 输出结果 致命错误 01print.c没有那个文件或目录编译中断。 */#pragma GCC poison 标示符 表示将后面的标示符设置成毒药一旦使用标示符则产生错误或警告信息 //毒药的设置 #include stdio.h //#define GOTO goto //将goto设置为毒药 #pragma GCC poison goto int main(void){ //GOTO ok; goto ok; printf(main函数开始\n); ok: printf(main函数结束\n); return 0; } /* 输出结果 错误 试图使用有毒的“goto” */#pragma pack(n) 表示按照整数n倍进行补齐和对齐 //设置结构体的对齐和补齐方式 #include stdio.h //设置结构体按照2的整数倍进行对齐补齐 #pragma pack(2) //8 //#pragma pack(1) //6 //#pragma pack(3) //error //char short int long int long long int main(void){ struct S{ char c1; int i; char c2; }; printf(sizeof(struct S) %d\n,sizeof(struct S)); //12 return 0; } /* 输出结果 sizeof(struct S) 8 */#pragma message(“string”) message 参数 message 参数是我最喜欢的一个参数它能够在编译信息输出窗口中输出相应的信息这对于源代码信息的控制是非常重要的。其使用方法为 #pragma message(“消息文本”)当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候我们自己有可能都会忘记有没有正确的设置这些宏此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86 这个宏可以用下面的方法. #define _X86 #ifdef _X86 #pragma message (_X86 macro activated!) #endif /* 输出结果 附注 #pragma message_X86 macro activated! */#pragma code_seg 另一个使用得比较多的 pragma 参数是 code_seg它能够设置程序中函数代码存放的代码段格式如#pragma code_seg( [section-name[,section-class] ] ) 当我们开发驱动程序的时候就会使用到它。 #pragma once 比较常用只要在头文件的最开始加入这条指令就能够保证头文件被编译一次这条指令实际上在Visual C6.0 中就已经有了但是考虑到兼容性并没有太多的使用它。 #pragma hdrstop 表示预编译头文件到此为止后面的头文件不进行预编译。 BCB 可以预编译头文件以加快链接的速度但如果所有头文件都进行预编译又可能占太多磁盘空间所以使用这个选项排除一些头文件。有时单元之间有依赖关系比如单元 A 依赖单元 B所以单元 B 要先于单元 A 编译。你可以用#pragma startup 指定编译优先级如果使用了#pragma package(smart_init) BCB就会根据优先级的大小先后编译。 #pragma resource *.dfm表示把*.dfm 文件中的资源加入工程。 *.dfm 中包括窗体外观的定义。 #pragma warning #pragma warning( disable : 4507 34; once : 4385; error : 164 ) 等价于#pragma warning(disable:4507 34) // 不显示 4507 和 34 号警告信息 #pragma warning(once:4385) // 4385 号警告信息仅报告一次 #pragma warning(error:164) // 把 164 号警告信息作为一个错误。 同时这个 pragma warning 也支持如下格式#pragma warning( push [ ,n ] ) //n 代表一个警告等级(1-4) #pragma warning( pop ) #pragma warning( push ) //保存所有警告信息的现有的警告状态。 #pragma warning( push, n) //保存所有警告信息的现有的警告状态并且把全局警告等级设定为 n。 #pragma warning( pop ) //向栈中弹出最后一个警告信息在入栈和出栈之间所作的一切改动取消。例如 #pragma warning( push ) #pragma warning( disable : 4705 ) #pragma warning( disable : 4706 ) #pragma warning( disable : 4707 ) //....... #pragma warning( pop ) 在这段代码的最后重新保存所有的警告信息(包括 4705 4706 和 4707)。 #pragma comment() 将一个注释放入一个对象文件或可执行文件中。常用的 lib 关键字可以帮我们连入一个库文件。 比如#pragma comment(lib, user32.lib) 将 user32.lib 库文件加入到本工程中。linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中设置的链接选项,可以指定/include 选项来强制包含某个对象,例如: #pragma comment(linker, /include:__mySymbol) 6.3 string.h和math.h 字符串处理函数库 在ubuntu系统中在/usr/include中字符串函数有 memcpy(dest, src)内存字符串复制它直接将源空间内容复制到目标内存空间所以使用它的前提是确定src和dst不会overlap重复执行效率高。memmove(dest, src)内存字符串复制它先将源空间内容复制到一个临时内存空间然后再复制到目标空间。memset()strncmp()memcmp()strdup()memchr()strndup()strcpy()strchr()strncpy()strstr()strcat()strtok()strncat()strcmp() 数学函数库 在编译时需要加-lm告诉链接器到libm.so中去查找用到的函数。在链接时可以用-lxxx来指示链接器去到libxxx.so中去查找这个函数。 6.4 自制链接库 6.4.1 制作静态链接库 静态链接库就是商业公司将自己的函数库源代码经过只编译不连接形成.o的目标文件然后用ar工具将.o文件归档成.a的归档文件.a的归档文件又叫静态链接库文件。商业公司通过发布.a库文件和.h头文件来提供静态库给客户使用客户拿到.a和.h文件后通过.h头文件得知库中的库函数的原型然后在自己的.c文件中直接调用这些库文件在连接的时候链接器会去.a文件中拿出被调用的那个函数的编译后的.o二进制代码段链接进去形成最终的可执行程序。 使用gcc -c只编译不连接生成.o文件使用ar工具进行打包成.a归档文件。 库名不能随便乱起一般是lib库名后缀名是.a表示是一个归档文件制作完静态库后发布时需要发布.a文件和.h文件。 6.4.2 使用静态链接库 把.a和.h都放在自己引用的文件夹下在.c文件中包含库的.h此时就可以引用静态库里面的函数啦。 备注 命名.a文件时前缀一定要加lib如libzf.a链接属性-l小L后跟库名不要lib和.a属性-L表示库的路径属性-I表示库的头文件路径例如gcc cdw.c -o cdw -lzf -L./include -I(大i)./include 6.4.3 自制动态链接库 动态链接库本身不将库函数的代码段链接入可执行程序只是做个标记。当应用程序在内存中执行时运行时环境发现它调用了一个动态库中的库函数时会去加载这个动态库到内存中然后以后不管有多少个应用程序去调用这个库中的函数都会跳转到第一次加载的地方去执行不会重复加载。也就是在运行时会把库函数代码放入内存中然后多个程序要用到库函数时就从这段内存去找而静态链接对于多程序就是重复使用库函数比较占内存。 动态链接库的后缀名是.so对应windows系统中的.dll静态库的扩展名是.a。gcc aston.c -o aston.o -c -fPIC-fPIC表示设置位置无关码。gcc -o libaston.so aston.o -shared-shared表示使用共享库发布库时发布libxxx.so和xxx.h。 6.4.4 使用动态链接库 gcc cdw.c -o cdw -lmax.so -L./动态链接库路径 -I./动态链接库头文件路径 上述命令虽然可以编译成功但是在执行时会报错。原因是采用动态链接时只是在可执行文件中做了一个标记标记是使用了哪个函数库的哪个函数但并没有将库函数加载到源文件中所以可执行文件很小。但在执行时需要立即从系统里面找到使用到的函数库然后加载到内存中在linux系统中会先从环境变量的路径然后再从系统默认的路径/usr/lib中寻找所以我们可以用两种办法解决运行的问题 一是将动态库libmax.so复制到/usr/lib下面但是如果以后所有的库都这样放的话会越来越臃肿导致运行速度变慢系统会一个一个查找 二是新添加一个环境变量export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/mnt/hgfs/share/include然后将库libmax.so复制到这个路径下面就可以了。 6.4.5 知识拓展 头文件用双引号括起来表示该头文件从自定义目录下找。如果没加路径属性默认当前路径找如果在其他文件夹下必须用-I(大i)路径。用尖括号括起来表示该头文件从编译器的默认库目录下找或者是自定义的库目录下找但是要明确其路径。nm ./include/libmax.a可以查看max库中有哪些.o文件各自含有哪些函数。使用ldd命令判断可执行文件是否能运行成功ldd cdw linux-gate.so.1(0xb77a8000) libmax.sonotfound #notfound意思就是没有找到对应的函数库 libc.so.6/lib/i386-linux-gnu/libc.so.6(0xb75e2000) /lib/ld-linux.so.2(0xb77a9000)gcc中编译链接程序默认是使用动态库的要想静态链接需要显式用-static来强制静态链接。库函数的使用需要注意4点 包含相应的头文件调用库函数时注意函数原型有些库函数链接时需要额外用-lxxx来指定链接如果是动态库要注意-L指定动态库的地址。 ​
http://www.w-s-a.com/news/53349/

相关文章:

  • 电子商城网站如何建设上海公司车牌价格
  • 丽江网站设计公司专业公司网站设计企业
  • iis怎么建设网站特色产品推广方案
  • 道路建设网站专题品牌网站建设特色大蝌蚪
  • 网站开发组合 所有组合如何做com的网站
  • 电商网站怎么做的Wordpress 报表的插件
  • 纹理网站推荐买了两台服务器可以做网站吗
  • 机关公文写作网站南宁互联网推广
  • 五指山网站开发价格免费申请网站域名
  • 帝国音乐网站怎么做数据表电脑优化软件
  • 做国外网站收款怎么收建筑人才招聘网站
  • 毕设做桌面端还是网站sns社交网站 建设
  • 建设一个网站需要注意哪些内容wordpress 进销存
  • 沈阳市建设局网站sem优化师是什么意思
  • 餐饮vi设计公司网站排名优化方法讲解
  • 无线昆明官方网站可以做书的网站
  • 信誉最好的20个网投网站凡科网站建设之后怎么删除
  • 天津网站开发技术广州网站优化公司排名
  • 养老做增减的网站医院网站怎么做优化排名
  • 企业网站的推广方法有哪些上海猎头公司前十名
  • 电商网站建设建议免费下载app
  • 网站搭建设计是什么意思百度地图放到网站上
  • 东莞网站建设市场分析淘宝网站框架
  • 新网站多久被百度收录网站空间单位
  • 2017常用的网站昆明网站代理
  • 成都海鸥手表网站安阳网站建设策划
  • 做好的网站怎么发布做网站应该做哪方面的
  • 可以找厂家的网站品牌创意型网站开发
  • 有没有做牛羊角的网站电商网站报价
  • 网站建设行业咨讯文章网站兼容模式怎么设置