c语言学习笔记(13)pragma详解,#和##运算符及编译指示字总结

摘要:总结了#error,#line的意义和用法,#和##运算符的用法,分析了pragma在控制内存对齐机制时候的用法,总结了struct在内存中的对齐方式,最后用一个面试题加深理解。


一、#error和#warning

    #error用于生成一个编译错误消息,并停止编译,该指示字用于指示程序员自己定义的错误信息。

    #warning用于生成警告信息,但是不会停止编译。

    用法:

    #error message

    message不需要用双引号。


二、#line

    #line用于强制指定新的行号和编译文件名,并对源程序的代码重新编号

    用法:

    #line number filename(可以不加)

    该指示字的本质是重新定义_LINE_和_FILE_

    这个有什么作用呢?比如我们在编译的时候,某个部分出错了,因为一个庞大的代码是很多人一起写的,可以使用这个在自己写的代码部分定义为第一行,文件名称改为自己的名称,这样编译出错的时候,就直接显示谁写的代码第几行出错了,然后谁拿回去修改就可以了。

 

三、#运算符

    #运算符用于在预编译期将宏参数转换为字符串,例如:

    #defineCONVERS(X)  #X

    我们在使用:

    printf(“%s\n“,CONVERS(while));

    printf(“%s\n“,CONVERS(if));

    的时候,输出的就是while和if,因为#已经把他们转换成了字符串的形式,也就是”while”和”if”。

四、##运算符

    ##运算符用于在预编译期粘连两个符号,例如:

    #defineNAME(n) name##n

    我们在main函数里面,int NAME(1),实际上就是int name1,因为##符号把他们粘连在一起了。

    ##运算符还可以用来定义结构类型,起到大大简化代码的作用。

 

五、pragma详解

1.pragma简介

    下面将会给出pragma的解释,说实话,看不太懂,有点抽象,用自己的话说,pragma是一种编译指示字,有什么用处呢?配合下面的message使用可以把编译的消息输出到我们的窗口,用来对编译版本进行控制,用法就是pragma message因为有时候一个版本要编译很久,如果编译错了,浪费时间,在编译开始的时候就进行输出,用于控制。

    #pragma是编译器指示字,用于指示编译器完成一些特定的动

    #pragma所定义的很多指示字是编译器和操作系统特有的

    #pragma在不同的编译器间是不可移植的

    预处理器将忽略它不认识的#pragma指令

    两个不同的编译器可能以两种不同的方式解释同一条#pragma指令

    一般用法:

    #pragma parameter

    注:不同的parameter参数语法和意义各不相同


2.#pragmamessage

    message参数在大多数的编译器中都有相似的实现
    message参数在编译时输出消息到编译输出窗口中
    message可用于代码的版本控制


3.#pragma pack

    这个好像很重要,可以控制内存对齐的大小。

    那么什么是内存对齐?我们在移植u-boot或者arm裸机的启动代码里面经常看到要设置字节对齐方式,一般是四字节,解释起来就是不同类型的数据在内存中按照一定的规则排列;而
不是顺序的一个接一个的排放,这就是对齐。这句话再解释一下什么意思呢?比如我们申请一个char型数据,在申请一个short类型的,再申请一个int类型的,他们并不是按照从0开始,放在第一,第二,第四个内存位置的,而是按照一定的对齐方式,他们之间存在间隔。

    为什么需要内存对齐呢?CPU对内存的读取不是连续的,而是分成块读取的,块的大小只能是1、2、4、8、16字节当读取操作的数据未对齐,则需要两次总线周期来访问内存,因此性能会大打折扣,某些硬件平台只能从规定的地址处取某些特定类型的数据,否则抛出硬件异常。

    #pragma pack能够改变编译器的默认对齐方式。

    例程如下:

#include<stdio.h>
 
int a;
 
#pragma pack(2)
struct TEST1
{
char c1;
short s;
char c2;
int i;
   
};
#pragma pack()
 
#pragma pack(4)
 
struct TEST2
{
char c1;
char c2;
short s;
int i;
   
};
#pragma pack()
 
int main(void)
{
    printf("test1size is:%d\n",sizeof(struct TEST1));
    printf("test2size is:%d\n",sizeof(struct TEST2));  
    printf("asize is:%d\n",sizeof(a));
    return0;
    }
运行之后的输出结果:
#test1size is:10
#test2size is:8
#asize is:4

    可以看到通过不同的pragma pack指定的对齐方式,结构体的大小是不一样的,我们也可以把两个pack里面的值都改为4,可以看到输出结果如下:

    #test1size is:12

    #test2size is:8

    #a sizeis:4

    同样的对齐方式,同样的结构体成员,只是顺序不同,打印出来的大小也不同,为什么呢?下面会解释。


4.struct占用内存大小的机制

    第一个成员起始于0偏移处。

    每个成员按其类型大小和指定对齐参数n中较小的一个进行对齐。

    偏移地址和成员占用大小均需对齐,偏移地址必须是刚才选出来的小的那个数的整数倍,这个很重要。

    结构体成员的对齐参数为其所有成员使用的对齐参数的最大值,也就是说加入我们在别的地方使用了整个结构体的大小,那么不是这个结构体所有成员的对其参数的和,而是取那个最大值。

    结构体总长度必须为所有对齐参数的整数倍

    微软的面试题,例程:

<span style="font-size:18px;">#include <stdio.h>
 
#pragma pack(8)
 
struct S1
{
   short a;
   long b;
};
 
struct S2
{
   char c;
   struct S1 d;
   double e;
};
 
#pragma pack()
 
int main()
{
   struct S2 s2;
   
   printf("%d\n", sizeof(struct S1));
   printf("%d\n", sizeof(struct S2));
   printf("%d\n", (int)&(s2.d) - (int)&(s2.c));
 
   return 0;
}</span>

    最后输出的结果在linux下运行的结果是8,20,4因为gcc不支持8字节对其,支持4字节对齐方式。在vc下编译运行的结果就是8,24,4.具体过程分析参照上面的总结,和下面的这幅图:

   

    这篇帖子就总结到这里,如有不正确的地方还请指出,大家共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值