您的位置:首页 > 编程语言 > C语言/C++

《c语言深度剖析》学习笔记2

2012-06-19 09:46 169 查看


第二章:符号

2.1 注释

>编译器会用空格代替原来的注释;

>嵌套注释:return /*/*/0*/**/1;

2.5

>左移和右移的位数不能大于数据类型的宽度,不能小于0;

>有符号数的右移,如果原符号位是0,则左端补0;否则,补0/1.

2.7.2

>c语言里,每一个符号都应该包含尽可能多的字符。(贪心法)也就是说,编译器将程

序分解成符号的方法是,从左到右一个一个字符地读入,如果该字符可能组成一个符号,

那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组

成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串

已不再可能组成一个有意义的符号。

3.预处理:

>C语言中的预处理指令:

预处理名称                            意义
#define                               宏定义
#undef                                撤销已定义的宏名
#include                              使编译程序将另一源程序嵌入到带有#include 的源文件中
#if                                   如果#if后面的常量表达式为真,则编译它与#endif之间的代码,否则跳过这写代码;
#else                                  #else建立另一个选择
#elif                                  #elif命令类似与else if
#endif                                 #endif标识一个#if快的结束
#ifdef                                条件编译的一种方法
#ifndef                               条件编译的一种方法
#line                                 改变当前行数和文件名称  #line number["filename"]    (方括号内可省略)
#error                                编译程序时,只要遇到#error就会生成一个编译错误提示信息,并停止编译
#pragma                               实现时定义的命令,它允许向编译程序传送各种指令



第三章:预处理

3.1 宏定义

>const修饰的只读变量不能用来作为定义数组的维数,也不能放在case后。

>反斜杠作为接续符时,在其后面不能再有任何字符。

>用 define 宏定义注释符号:

#define BSC //
#define BMC /*
#define EMC */
BSC my single-line comment
BMC my multi-line comment EMC
书上的意思是说:

因为注释先于预处理指令被处理,当这两行被展开成//...或

/*...*/时,注释已处理完毕,此时再出现//...或/*...*/自然错误。

也就是:编译器之处理一次注释符号,一次处理完之后,如果再有注释符号,编译器就不认识了。

我认为:BSC是可以展开成 // 的;BMC也可以展开成 /* ;但 EMC 应该是被注释掉了(因为先处理注释,后处理宏定义),EMC无法展开成 */ .

测试了一下:

#include <stdlib.h>

#define BMC /*
#include "f2.h"
#define EMC */

int main()
{
f2();
BMC EMC
return 0;
}

//f2.h
#include <stdio.h>

void f2()
{
printf("Hello f2()\n");
}
编译结果:

#gcc 2_5.c
2_5.c: 在函数‘main’中:
2_5.c:10:7: 错误: ‘EMC’未声明(在此函数内第一次使用)
2_5.c:10:7: 附注: 每个未声明的标识符在其出现的函数内只报告一次
2_5.c:11:3: 错误: expected ‘;’ before ‘return’

说明编译器先将 "/*" 和 "*/"之间的内容替换为空格,然后将BMC替换为 "/*",EMC未声明...
但是为什么不报错 f2 呢?

>用宏定义表达式时,不要吝啬括号!

>宏函数被调用时,是以实参代换形参,而不是"值传值"。

>#define EMPTY //这样的宏定义是可行的,但它的用途是什么呢?

>宏定义中空格的使用:

#define SUM (x) ((x) + (x))
编译器认为这是定义了一个宏:SUM, 其代表的是(x) ((x) + (x)).

所以,当程序中出现SUM(3)时,宏展开为(x) ((x) + (x))(3);//自然会出错:x未定义

在使用宏函数的时候,宏函数名与其后的括号之间可以有括号的。SUM(3) 和 SUM (3)的效果是一样的。

3.3 文件包含

>文件包含实际上是宏替换的延伸。

#include <filename>    //到系统规定的路径中去获得这个文件
#include "filename"    //预处理先在当前目录中查找此文件,若没有找到,再按照系统指定的路径信息,搜索该文件。
#include 是将已经存在的文件的内容嵌入到当前文件中

3.5 #line预处理

用在编译器的编写中。

3.6 #pragma

#pragma 的作用是设定编译器的状态或者指示编译器完成一些特定的动作。

#pragma message("消息文本") 当编译器遇到这条指令时,就在编译输出窗口将消息文本打印出来.

#pragma code_seg(["section-name"[,"section-class"]]) 能够设置程序中函数代码存放的代码段

#pragma once 只要在头文件的最开始加入这条指令就能够保证头文件被编译一次.

#pragma hdrstop 表示与编译头文件到此为止,后面的头文件不进行预编译.

#pragma warning 设置警告信息

>#pragma pack 内存对齐问题

#include <stdio.h>

struct TestStruct1
{
char c1;
short s;  //sizeof(short) = 2
char c2;
int i;
};

int main()
{
struct TestStruct1 a;
printf("c1:%p, s:%p, c2:%p, i:%p.\n",
(unsigned int)(void *) &a.c1 - (unsigned int)(void *) &a,
(unsigned int)(void *) &a.s - (unsigned int)(void *) &a,
(unsigned int)(void *) &a.c2 - (unsigned int)(void *) &a,
(unsigned int)(void *) &a.i - (unsigned int)(void *) &a
);

return 0;
}

//output:
c1:(nil), s:0x2, c2:0x4, i:0x8.

>为什么会有内存对齐?

(1字节 = 8位, 1字 = 2字节, 1双字 = 2字, 1四字 = 2双字)

字、双字和四字在自然边界(字:偶数地址; 双字:被4整除的地址; 四字:被8整除的地址)上不需要内存对齐。

为了提高陈徐的性能,数据结构尽可能在自然边界上对齐,因为:为了访问未对齐的内存,处理器需要做两次内存的访问;然而,对齐内存的访问仅需要一次访问.

>#pragma pack(n). 编译器将按照n个字节对齐.

(但是,对齐规则是:每个成员按其类型的对齐参数(通常是类型大小) 和 指定参数(n) 中较小的一个对齐; 结构的长度必须为所用过的所有对齐参数的整数倍(不够补零)).

>#pragma pack(),编译器将取消自定义字节对齐方式.

#pragma pack(8)

struct TestStruct4
{
char a;
long b; //sizeof(long) = 4
};

struct TestStruct5
{
char c;
struct TestStruct4 d;
long long e; //sizeof(long long) = 8
};

#pragma pack()

TestStruct4中,a占1个字节,默认按照1字节对齐,而指定对齐参数是8,取最小值:1----a按照1字节对齐。
同理,b按照4字节对齐。sizeof(TestStruct4) = 8.
TestStruct5中,c按照1字节对齐;d是一个结构体,大小为8字节,对于结构体来说,默认对齐方式是它的所有成员使用的对齐参数中最大的一个,所以d按4字节对齐;
e按照8字节对齐。这样,TestStruct5一共占用24个字节.在GCC下,得出的结果是20(20并不是8的倍数)......(参考:/article/7753712.html)
数组的对齐方式按照数组元素类型的默认对齐方式。
3.7 #运算符

#define SQR(x) printf("The square of x is %d.\n", ((x) * (x)));
#define SQR(x) printf("The square of "#x" is %d.\n", ((x) * (x)));


3.8 ##运算符
把两个语言符号组合成单个语言符号.

#define XNAME(n) x##n


XNAME(8)将会展开成 x8
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: