C语言学习笔记--#error 、 #line 和 #pragma 的使用

1. #error 的用法

(1)#error 是一种预编译器指示字,用于生成一个编译错误消息

(2)用法:#error message //注意:message 不需要用双引号包围

(3)#error 编译指示字用于自定义程序员特有的编译错误消息。类似的,#warning 用于生成编译警告消息

(4)#error 可用于提示编译条件是否满足。编译过程中的任何错误意味着无法生成最终的可执行程序

2. #line 的用法

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

(2)用法:#line number newFilename //newFilename 可省略

(3)#line 编译指示字的本质是重定义__LINE__和__FILE__

#include <stdio.h>
//作者 A 写的代码
//--------------------------开始--------------------------
//把 line 的下一行定义为第 1 行,文件名为“a.c”
#line 1 "a.c"
//--------------------------结束--------------------------
//作者 B 写的代码
//--------------------------开始--------------------------
//把 line 的下一行定义为第 1 行,文件名为“b.c”
#line 1 "b.c"
//--------------------------结束--------------------------
//作者 C 写的代码
//--------------------------开始--------------------------
#line 1 "MyCode.c"
int main(){
    printf("%s:%d\n",__FILE__,__LINE__);//输出 MyCode.c:4
    #line 1 "Test.c"
    printf("%s:%d\n",__FILE__,__LINE__);//输出 Test.c:1
    return 0;
}
//--------------------------结束--------------------------

3.#pragma 的使用

(1)#pragma 用于指示编译器完成一些特定的动作
(2)#pragma 所定义的很多指示字是编译器特有的,在不同的编译器间是不可移植的

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

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

(3)一般用法:#pragma parameter //注意,不同的 parameter 参数语法和意义不同

3.1.#pragma message——用于自定义编译消息

(1)message 参数在大多数的编译器中都有相似的实现

(2)message 参数在编译时输出消息到编译输出窗口中

(3)message 用于条件编译可提示代码的版本信息

(4)与#error 和#warning 不同,#pragma message 仅仅代表一条编译消息,不代表程序错误

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

int main()
{
    //可用命令行编译:gcc 24-1.c -DANDROID40
    //在 gcc 下输出:
    // 24-1.c:10: note: #pragma message: Complie Android SDK
4.0...
    //在 vc\bcc 下输出: Complie Android SDK 4.0...
    printf("%s\n",VERSION);
    return 0;
}

3.2.#pragma once——用于保证头文件只被编译一次

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

(3)#pragma once 比#ifndef…#define…#endif 效率高,因为后者定义的头文件仍然被处理。前者只要头文件被定义一次,就不会再次被处理。

global.h

#pragma once
int g_nValue = 1;
//说明:因#pragma once 不被所有的编译器支持(如 bcc 不支持),但
//#pragma once 又比#ifndef...#define...#endif 效率高,如果
//为了让支持#pragma once 的编译器有更高的效率有更高的效率,可以采用如下的头文件定义方式/*
#pragma once //当编译器不支持#pragma once 时,会直接忽略这行
ifndef _HEADER_FILE_H_
#define _HEADER_FILE_H_
//source code
#endif
*/

test.c

#include <stdio.h>
#include "global.h"
#include "global.h" //被 include 两次
int main()
{
    printf("g_nValue = %d \n",g_nValue);
    return 0;
}

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

(1)什么是内存对齐

不同类型的数据在内存中按照一定的规则排列而不一定是顺序的一个接一个的排列。

结构体大小的计算

#include <stdio.h>
#pragma pack(2)
struct Test1
{
    char c1; //对齐参数:min(1,2)=1, offset = 0short s; //对齐参数:         min(2,2)=2, offset = 2
    char c2; //对齐参数:min(1,2)=1, offset = 4
    int i; //对齐参数:min(4,2)=2, offset = 6
};
#pragma pack()

#pragma pack(4)
struct Test2
{
    char c1; //对齐参数:min(1,4)=1, offset = 0
    char c2; //对齐参数:min(1,4)=1, offset = 1
    short s; //对齐参数:min(2,4)=2, offset = 2
    int i; //对齐参数:min(4,4)=4, offset = 4
};
#pragma pack()

int main()
{
    printf("sizeof(Test1) = %d\n",sizeof(struct Test1)); //10
    printf("sizeof(Test2) = %d\n",sizeof(struct Test2)); //8
    return 0;
}

(2)为什么需要内存对齐?

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

(3)#pragma pack(n)能够改变编译器的默认对齐方式(默认是按 4 字节对齐)

①struc 占用的内存大小

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

B.每个成员以 min(sizeof(成员的类型),n)的对齐参数进行对齐。即偏移地址必须 能被对齐参数整除,在复合结构体中,某个成员(结构体类型)的对齐参数为其内部长度最          大的数据成员的对齐参数作为这个成员的对齐参数。

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

#include <stdio.h>
#define OFFSET(st,member) ((int)&((struct st*)0)->member)
#pragma pack(1)
struct S1
{
    short a;
    long b;//对齐参数:min(4,1)=1,sizeof(long)=4;->最大成员
};
#pragma pack()

#pragma pack(2)
struct S2
{
    char c;//offset = 0;
    struct S1 d;//offset = 1(对齐参数为 min(1,2)),sizeof(s1)=6;
    double e;//offset = 8(对齐参数为 min(8,2))
};
#pragma pack()

int main()
{
    printf("sizeof(struct S1) = %d\n",sizeof(struct S1));//6
    printf("sizeof(struct S2) = %d\n",sizeof(struct S2));//16
    printf("Member‘s Offset of Struct S2:\n");
    printf("&s2.c=%d, &s2.d=%d, &s2.e=%d\n",
    OFFSET(S2,c),OFFSET(S2,d),OFFSET(S2,e));
    return 0;
}
时间: 2024-11-16 05:25:01

C语言学习笔记--#error 、 #line 和 #pragma 的使用的相关文章

R语言学习笔记-Error in ts(x):对象不是矩阵问题解决

1.问题 在对时间序列进行拟合操作时,发生:Error in ts(x):对象不是矩阵的错误,而直接在arima()函数中使用时没有问题的. > sample<-c2 > sample [1] 0.00 0.00 0.00 0.00 0.00 0.00 0.06 0.09 0.20 0.09 0.08 0.14 0.14 0.23 [15] 0.08 0.06 0.12 0.20 0.14 0.11 0.20 0.14 0.17 0.15 0.18 0.15 0.20 0.12 [29]

JavaScript--基于对象的脚本语言学习笔记(二)

第二部分:DOM编程 1.文档象模型(DOM)提供了访问结构化文档的一种方式,很多语言自己的DOM解析器. DOM解析器就是完成结构化文档和DOM树之间的转换关系. DOM解析器解析结构化文档:将磁盘上的结构化文档转换成内存中的DOM树 从DOM树输出结构化文档:将内存中的DOM树转换成磁盘上的结构化文档 2.DOM模型扩展了HTML元素,为几乎所有的HTML元素都新增了innerHTML属性,该属性代表该元素的"内容",即返回的某个元素的开始标签.结束标签之间的字符串内容(不包含其它

R语言学习笔记

參考:W.N. Venables, D.M. Smith and the R DCT: Introduction to R -- Notes on R: A Programming Environment for Data Analysis and Graphics,2003. http://bayes.math.montana.edu/Rweb/Rnotes/R.html 前言:关于R 在R的官方教程里是这么给R下注解的:一个数据分析和图形显示的程序设计环境(A system for data

go语言学习笔记

go语言学习笔记 go语言学习笔记(初级) 最近一直在学习go语言,因此打算学习的时候能够记录 一下笔记.我这个人之前是从来没有记录笔记的习惯, 一直以来都是靠强大的记忆力去把一些要点记住. 读书的时候因为一直都是有一个很安静和很专心的环境, 因此很多事情都能记得很清楚,思考的很透彻.但是随着 年纪不断增加,也算是经历了很多的事情,加上工作有时会让人 特别烦闷,很难把心好好静下来去学习,去思考大自然的终极 奥秘,因此需要记录一些东西,这些东西一方面可以作为一种自我激励 的机制,另一方面,也算是自

Go语言学习笔记(二) [变量、类型、关键字]

日期:2014年7月19日 1.Go 在语法上有着类 C 的感觉.如果你希望将两个(或更多)语句放在一行书写,它们 必须用分号分隔.一般情况下,你不需要分号. 2.Go 同其他语言不同的地方在于变量的类型在变量名的后面.例如:不是,int a,而是 a int.当定义了一个变量,它默认赋值为其类型的 null 值.这意味着,在 var a int后,a 的 值为 0.而 var s string,意味着 s 被赋值为零长度字符串,也就是 "". 3.Go语言的变量声明和赋值 在Go中使

JavaScript--基于对象的脚本语言学习笔记(一)

1.两种嵌入js的方式 使用javascript前缀构建url:<a href="javascript:alert('运行JavaScript..')">运行js</a> js脚本放在<style></style>之间: <style type="text/javascript"> alert("运行JavaScript..") </script> 2.如果没有声明变量直接使

Perl语言学习笔记 9 正则表达式处理文本

1.替换 s/PATTERN/REPLACE/; #返回是否替换成功的布尔值 可以使用捕获变量,如:s/(\w)/$1/ 匹配失败则不做任何处理 2.定界符 对于没有左右之分的定界符,重复三次即可,如:s///.s### 对于有左右之分的定界符,需使用两对,一对包含模式,一对包含替换字符串,这两对可以不一样,如:s{}{}.s[]{}.s<>[] 3.可选修饰符 /g可进行全局替换,替换所有匹配到的字符串,如:s/ / /g /s:使得 . 匹配所有字符 /i:大小写无关 4.绑定操作符 $f

Perl语言学习笔记 6 哈希

1.哈希的键是唯一的,值可以重复! 2.访问哈希元素 $hashname{"$key"};#哈希为大括号,数组为方括号,键为字符串 $family_name{"fred"} = "firstd";#给哈希元素赋值 3.哈希键支持任意表达式 $foo = "na"; $family_name{$foo."me"};#获取$family_name{"name"}对应的值 4.访问整个哈希 %

R语言学习笔记2——绘图

R语言提供了非常强大的图形绘制功能.下面来看一个例子: > dose <- c(20, 30, 40, 45, 60)> drugA <- c(16, 20, 27, 40, 60)> drugB <- c(15, 18, 25, 31, 40) > plot(dose, drugA, type="b") > plot(dose, drugB, type="b") 该例中,我们引入了R语言中第一个绘图函数plot.pl