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

第二章:符号

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的倍数)......(参考:http://blog.csdn.net/ticmy/article/details/4503831)

数组的对齐方式按照数组元素类型的默认对齐方式。

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值