C语言#pragma使用

C/C++ 专栏收录该内容
49 篇文章 0 订阅

1.#pragma简介

(1)#pragma用于指示编译器完成一些特定的动作
(2)#pragma所定义的很多指示字是编译器特有的
(3)#pragma在不同的编译器间是不可移植的
(4)预处理器将忽略它不认识的#pragma指令
(5)不同的编译器可能以不同的方式解释同一条#pragma指令

一般用法︰#pragma parameter

#pragma message

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

#error和#warning不同,#pragma message仅仅代表一条编译消息,不代表程序错误。
将下列代码在gcc编译器中运行,查看结果

#include <stdio.h>

#if defined(ANDROID20)
    #pragma message("Compile Android SDK 2.0...")
    #define VERSION "Android 2.0"
#elif defined(ANDROID23)
    #pragma message("Compile Android SDK 2.3...")
    #define VERSION "Android 2.3"
#elif defined(ANDROID40)
    #pragma message("Compile Android SDK 4.0...")
    #define VERSION "Android 4.0"
#else
    #error Compile Version is not provided!
#endif

int main()
{
    printf("%s\n", VERSION);

    return 0;
}

可以看见,编译的时候,通过#pragma message("Compile Android SDK 2.0...")语句,我们想要的提示信息已经被打印出来了,在不同的编译器对message这个参数处理的结果不一样,在VC和BCC编译器中运行的结果只是打印出了Compile Android SDK 2.0...,与GCC编译器编译的效果有所差异,但差异不大,
在这里插入图片描述

#pragma once

(1)#pragma once 用于保证头文件只被编译一次
(2)#pragma once是编译器相关的,不一定被支持

提出问题:这个方式和#ifndef 、#define、#endif有什么区别?
解释:
#ifndef 、#define、#endif来处理头文件的时候,嵌套处理还是执行了多次的,而#pragma once告诉预编译器只执行一次即可,所以#pragma once的执行效率会比其更高。
虽然#pragma once 的效率更高,但是不同的预编译器对其的支持度不同,所以在很多工程文件中,还是使用#ifndef 、#define、#endif来避免头文件重复包含用的多一点。

示例代码

#include <stdio.h>
#include "global.h"
#include "global.h"

int main()
{
    printf("g_value = %d\n", g_value);

    return 0;
}

global.h


#pragma once

int g_value = 1;

在gcc进行编译,结果正常运行,原因就在于在头文件中已经使用#pragma once 进行了声明该头文件只被包含一次。
在bcc编译器进行编译,会报错,这款编译器不支持#pragma once ,被忽略了,所以头文件被包含了两次。
所以有一种推荐写法:即将#pragma once写在在中间部分

#ifndef _GLOBAL_H_
#define _GLOBAL_H_
#pragma once 

int g_value = 1;

#endif

pragma pack

保证内存对齐,什么是内存对齐?
-----不同类型的数据在内存中按照一定的规则排列,而不一定是顺序的一个接一个的排列。

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

#pragma pack 用于指定内存对齐方式

看下面的结构体,想想输出的结果是什么呢?

#include <stdio.h>

struct Test1
{
    char  c1;
    short s;
    char  c2;
    int   i; 
};

struct Test2
{
    char  c1;
    char  c2;
    short s;
    int   i;
};

int main()
{
    printf("sizeof(Test1) = %d\n", sizeof(struct Test1));
    printf("sizeof(Test2) = %d\n", sizeof(struct Test2));

    return 0;
}

分别是12和8
在这里插入图片描述
内存空间的存储方式是这样的。
在这里插入图片描述
结构体计算要遵循字节对齐原则,结构体大小结果要为成员中最大字节的整数倍,编译器在默认情况下是是4字节对齐的。参考文章:结构体字节对齐

struct占用的内存大小,重要的几点在下面列出:(务必掌握)
1.第一个成员起始于0偏移处
2.每个成员按其类型大小和pack参数中较小的一个进行对齐
3.偏移地址必须能被对齐参数整除(坑人)
4.结构体成员的大小取其内部长度最大的数据成员作为其大小
5.结构体总长度必须为所有对齐参数的整数倍(这个最重要)

Test1结构体中,

				 对齐参数   偏移地址  大小
char  c1;          1          0     1
short s;           2          2     2
char  c2;          1          4     1
int   i;           4          8     4

对于short s对齐参数是2,偏移地址必须是对齐参数的整数倍,那么偏移位置不能是1了,只能是2。
同理,int i对齐参数是4,偏移地址起始地址不能是5了,因为要满足对齐参数的整数倍,只能是8才行。那么最后一个变量i的偏移地址是8,大小是4,所以8+4=12,且12是所有对齐参数的整数倍,该结构体的大小就为12个字节了。我们的分析与上图的示意图是一致的。

Test2结构体中,

				 对齐参数   偏移地址  大小
char  c1;          1          0     1
char  c2;          1          1     1
short s;           2          2     2
int   i;           4          4     4

最后一个变量i的偏移地址是4,大小是4,所以4+4=8,且8是所有对齐参数的整数倍,该结构体的大小就为8个字节了。

编译器默认4字节对齐的意思就是,下面的代码和没有加#pragma pack(4)、#pragma pack()组合算出来结构体的大小结果是一样的。

#pragma pack(4)
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()

训练一下,
例子1

#include <stdio.h>

#pragma pack(2)
struct Test1
{				//对齐参数   偏移地址  大小
    char  c1;	// 1 	     0       1
    short s;    // 2         2       2
    char  c2;  //  1	     4       1
    int   i;   //  4		 6       4
};
#pragma pack()

#pragma pack(4)
struct Test2
{				//对齐参数   偏移地址  大小						
    char  c1;//   1          0       1
    char  c2;//   1		     1       1
    short s;//    2          2       2
    int   i;//    4          4       4
};
#pragma pack()

int main()
{
    printf("sizeof(Test1) = %d\n", sizeof(struct Test1));
    printf("sizeof(Test2) = %d\n", sizeof(struct Test2));

    return 0;
}
结果是108

例子2

#include <stdio.h>

#pragma pack(8)

struct S1
{				//对齐参数   偏移地址  大小	
    short a;  //  2          0        2
    long b;   //  4          4        4
};

struct S2
{			   //对齐参数   偏移地址  大小	
    char c;//      1        0       1
    struct S1 d;// 4        4       8
    double e;//    8        16      8
};

#pragma pack()

int main()
{
    printf("%d\n", sizeof(struct S1));
    printf("%d\n", sizeof(struct S2));

    return 0;
}

不同编译器处理的结果是不同的,gcc编译器不支持八字节对齐,double的对齐参数只能为4计算,所以得到结构体2的大小为20,但是在BCC编译器大小就为24了。

2.小结

(1)#pragma用于指示编译器完成一些特定的动作
(2)#pragma所定义的很多指示字是编译器特有的
(3)#pragma message 用于自定义编译消息
(4)#pragma once 用于保证头文件只被编译一次
(5)#pragma pack 用于指定内存对齐方式

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 点我我会动 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值