<<C语言深度剖析>>学习笔记之四:预运算符

来源:互联网 发布:php artisan 报错 编辑:程序博客网 时间:2024/06/06 12:23

预处理:

    1.C语言中的预编译指令罗列如下:

    #define

    #undef

    #include

    #if

    #else

    #elif

    #endif

    #ifdef

    #ifndef

    #line    改变当前行数和文件名称.命令的基本形式如:#line number["filename"]

    #error 编译程序时,只要遇到#error就会生成一个编译错误提示消息,并停止编译

    #pragma 为实现时定义的命令,它允许向编译程序传送各种指令.例如,编译程序可能有一种选择,它支持对程序执行的跟踪.

        可用#pragma语句指定一个跟踪选择.


    2.ANSI标准C定义的几个宏:

    __LINE__  表示正在编译的文件的行号;

    __FILE__   表示正在编译的文件的名字;

    __DATE__ 表示编译时刻的日期字符串,如:"25 Dec 2007"

    __TIME__   表示编译时刻的时间字符串,如:"12:30:55"

    __STDC__ 判断该文件是不是定义成标准C程序


    3.#define

    3.1数值宏常量

    利用宏常量可以实现程序的重用性、可读性.

    重用性,如:

    #define PI (3.141592653)

    在以后代码中尽可能使用PI来替代3.141592653.这样一旦修改PI的精度只需要改一下PI的宏定义值就可以了.

    可读性,如:

    #define ERROR_POWEROFF (-1)

    如果进行重定义在某种情况下出错返回-1别人根本不知道是什么意思,因为很多出错情况下都会返回-1.


    3.2定义表达式

        定义表达式时一定不要吝啬"()".否则很有可能出错了.

        例如:

        #define SQR(x)    x * x

        如果x的值是10,SQR(x)被替换为10 * 10是没有问题的.如果x是一个表达式,比如是10 + 1.那么SQR(x)被展开后

是10+1*10+1,这不是我们预期的结果.

        因此,上述宏定义为:

        #define SQR(x)    ((x) * (x))


 

    3.3.#undef用来撤销宏定义,用法如下:

#define PI 3.141592653...//code#undef PI//下面的代码就不能用PI了.

 

    4.条件编译

        条件编译的功能使得我们可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件.

这对程序的移植、调试和兼容是很有作用的.有下面三种形式:

        形式一:

#ifdef 标识符    code1#else    code2#endif     

        形式二:

#ifndef 标识符    code1#else    code2#endif

        形式三:

#if 常量表达式    code1#else   code2#endif


    5.文件包含

    文件包含是预处理的一个重要功能,它可用来多个源文件连接成一个源文件编译进一个目标文件.C语言提供#include

命令来实现文件包含操作.有两种格式:

    格式一:

#include<filename>

    这表示预处理要到系统规定的路径中去获得这个文件(即C编译系统所提供的并存放在指定的子目录下的头文件).找到

文件后,用文件内容替换该语句.

    格式二:

#include "filename"

    表示预处理应当在当前目录中查找文件名为filename的文件,若没有找到,则按系统指定的路径信息,搜索标准路径.找到

文件后,用文件内容替换该语句.另外要注意的是,这种格式还支持相对路径.

 

    6.#error预处理

    #error预处理命令的作用是,编译程序时,只要遇到#error就会生成一个编译错误提示消息,并停止编译.语法格式为:

#error error-message

    error-message不用双引号包围.遇到#error指令时,错误信息被显示.[很少用到]

 

    7.#line预处理

    #line的作用是改变当前行数和文件名称.语法如下:

#line number["filename"]

    其中[]内的文件名可以省略.

    例如:

    #line 30 a.h
    其中,a.h可以省略不定.这条指令可以改变当前的行号和文件名.例如上面的这条预处理指令就可以改变当前的行号

为30,文件名为a.h.[很少用到]

   

    8.#pragma预处理

    它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.语法格式如下:

#pragma para

    para常用参数有message、code_seg.如下:

#ifdef _X86#Pragma message("_X86 macro activated!")#endif

    当我们定义了_X86这个宏后,应用程序在编译时就会在编译输出窗口里显示"X86 macro activated!"

#pragma code_seg(["section-name"[,"section-class"]])

    它能够设备程序中函数代码存放的代码段.

#pragma comment(...)

    该指令将一个注释记录放入一个对象文件或可执行文件中.常用的lib关键字,可以帮我们连篇一个库文件.如:

#pragma comment(lib,"user32.lib")

    该指令用来将user32.lib库文件加入到本工程中.

    linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者开发环境中设置的链接选项,

你可以指定/include选项来强制包含某个对象.如:

    #pragma comment(linker,"/include:__mySymbol")

#pragma pack

    改变编译器字节的对齐方式.如:

使用指令#pragma pack(n),编译器将按照n个字节对齐.使用指令#pragma pack(),编译器将取消自定义字节对齐方式.在#pragma pack(n)和#pragma pack()之间的代码按n个字节对齐.但是成员对齐的原则是每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n)中较小的一个对齐.并且结构的长度必须是用过的所有对齐参数的整数倍,不够就补空字节.

下面给出一个#prama pack示例,顺便分析一下内存对齐.

gcc version 4.2.4

#include <stdio.h>#include <stdlib.h>#pragma pack(8)typedef long long __int64;typedef struct __TestStruck1{    char a;    long b;}TestStruck1,*pTestStruck1;typedef struct __TestStruck2{    char c;    TestStruck1 d;    __int64 e;}TestStruck2,*pTestStruck2;#pragma pack()int main(int argc,char **argv){    __int64 i;    printf("Size Of long-long-variable = %d\n",sizeof(i));    printf("Size Of TestStruck1 = %d\n",sizeof(TestStruck1));    printf("Size Of TestStruck2 = %d\n",sizeof(TestStruck2));    return 0;}
输出结果:

root@seven-laptop:~/learn/C_Program# ./pack Size Of long-long-variable = 8Size Of TestStruck1 = 8Size Of TestStruck2 = 20root@seven-laptop:~/learn/C_Program# 

VC6.0

#include <stdio.h>#include <stdlib.h>#pragma pack(8)typedef struct __TestStruck1{    char a;    long b;    }TestStruck1,*pTestStruck1;typedef struct __TestStruck2{    char c;    TestStruck1 d;    __int64 e;    }TestStruck2,*pTestStruck2;#pragma pack()int main(int argc,char **argv){    __int64 lTestVal;    printf("Size Of long-variable = %d\n",sizeof(lTestVal));    printf("Size Of TestStruck1 = %d\n",sizeof(TestStruck1));    printf("Size Of TestStruck2 = %d\n",sizeof(TestStruck2));    return 0;    }

输出结果:

Size Of long-variable = 8Size Of TestStruck1 = 8Size Of TestStruck2 = 24Press any key to continue

 

关于内存对齐需要记住下面几点即可:

    1).每个变量按各自的数据类型对齐,比如char可以是内存的任意地址,short只能是存放在2或2的倍数的地址上,int只能存放在4或4的倍数的地址上等;

    2).比较复杂的数据类型(如结构体包含结构体)是以它最长的成员的对齐方式.如上述的TestStruck1被包含在TestStruck2里面,TestStruck1以其最长

        成员long b作为对齐方式(即4个字节)来存在于TestStruck1里面;

    3).对齐的长度必须是成员中最大的对齐参数的整数倍,如上述TestStruck2成员最大的是__int64,即8个字节.因此是24.GCC,你到底怎么啦?


9.#运算符

    “#”可以把语言符转换为字符串.简单地说,就是对它所引用的宏变量通过替换后在其左右各加上一个双引号.下面给出两个对比示例:

    示例一:

#include <stdio.h>#include <stdlib.h>#define SQR(x)  printf("The Square Of x Is %d\n",((x) * (x)));int main(int argc,char **argv){    SQR(8);    return 0;}
输出结果:

The Square Of x Is 64

    示例二:

#include <stdio.h>#include <stdlib.h>#define SQR(x)  printf("The Square Of "#x" Is %d\n",((x) * (x)));int main(int argc,char **argv){    SQR(8);    return 0;}
输出结果:

The Square Of 8 Is 64

两个示例中,x都是宏变量.不同的是示例一把x当作普通文本处理,而示例二把x当作字符串处理.这就是#预运算的作用.


10.##预算符

    ##预算符就是一个拼接器,把##前后两部分给拼接起来.如:

    #define    XNAME(n)    x##n

    XNAME(8)被展开后:

    x8