Linux C 预处理详解

1.预处理程序
按照ANSI标准的定义,预处理程序应该处理以下指令:
#if #ifdef #ifndef #else #elif
#endif
#define
#undef
#line
#error
#pragma
#include
显然,上述所有的12个预处理指令都以符号#开始,每条预处理指令必须独占一行。

2.#define
#define指令定义一个标识符和一个串(也就是字符集),在源程序中发现该标识符时,都用该串替换之。这种标识符称为宏名字,相应的替换称为宏代换。一般形式如下:
#define macro-name char-sequence
这种语句不用分号结尾。宏名字和串之间可以有多个空白符,但串开始后只能以新行终止。
例如:我们使用LEFT代表1,用RIGHT代表0,我们使用两个#define指令:
#define LEFT 1
#define RIGHT 0
每当在源程序中遇到LEFT或RIGHT时,编译程序都用1或0替换。
定义一个宏名字之后,可以在其他宏定义中使用,例如:
#define ONE 1
#define TWO ONE+ONE
#define THREE ONE+TWO
宏代换就是用相关的串替代标识符。因此,如果希望定义一条标准错误信息时,可以如下定义:
#define ERROR_MS “Standard error on input \n”
如果一个串长于一行,可在行尾用反斜线”\”续行,如下:
#define LONG_STRING “This is a very very long \
String that is used as an example”
我们可以用宏写函数:
[cpp] view plaincopy
  1. #define SWAP(a,b)    \  
  2. {                    \  
  3. int temp = a;        \  
  4. a = b;               \  
  5. b = temp;            \  
  6. }                    \   
这里的"\"意为这是条整语句,编译完成后,函数如果调用了SWAP(a,b),就变成了{int temp = a; a = b;b = temp;};
宏定义函数与外部调用函数对比:
1)宏定义优点:效率高,因为函数调用涉及压栈,出栈,而宏定义函数不需要;
2)宏定义缺点:占空间,因为没有压栈和出栈,就会一直占着内存;
宏定义是空间换时间

3.#error
#error指令强制编译程序停止编译,它主要用于程序调试。#error指令的一般形式是:
#error error-message
注意,宏串error-message不用双引号包围。遇到#error指令时,错误信息被显示,可能同时还显示编译程序作者预先定义的其他内容。

4.#include
  文件包含
程序中的#include指令要求编译程序读入另一个源文件。被读入文件的名字必须用双引号(“”)或一对尖括号(<>)包围。
例如:
#include “stdio.h”
#include  <stdio.h>
都使C编译程序读入并编译头文件以用于I/O系统库函数。
包含文件中可以包含其他#include指令,称为嵌套包含。允许的最大嵌套深度随编译器而变。
文件名被双括号或尖括号包围决定了对指定文件的搜索方式:
1)文件名被尖括号包围时,搜索按编译程序作者的定义进行,一般用于搜索某些专门放置包含文件的特殊目录。
2)当文件名被双括号包围时,搜索按编译程序实时的规定进行,一般搜索当前目录。如未发现,再按尖括号包围时的办法重新搜索一次。
我们编译时也可以加入 gcc -I /home/fs/include  如果我们在当前目录里找不到"stdio.h",就会到/home/fs/include(这是我们自己存放头文件的位置)里去搜索,如果还是搜索不到,便会到系统头文件里去查找;
通常,绝大多数程序员使用尖括号包围标准的头文件,双引号用于包围与当前程序相关的文件名。
5. 条件编译指令
若干编译指令允许程序员有选择的编译程序源代码的不同部分,这种过程称为条件编译。
5.1#if、#else、#elif #endif
条件编译指令中最常用的或许是#if,#else,#elif和#endif。这些指令允许程序员根据常数表达式的结果有条件的包围部分代码。
#if的一般形式是:
#if constant-expression
 Statement sequence
#endif
如#if后的常数表达式为真,则#if和#endif中间的代码被编译,否则忽略该代码段。#endif标记#if块的结束。
 
#else指令的作用与C语言的else相似,#if指令失败时它可以作为备选指令。例如:
[cpp] view plaincopy
  1. #include <stdio.h>   
  2. #define MAX 100  
  3.    
  4. Int main(void)  
  5. {  
  6.  #if MAX > 99  
  7.     printf(“Compiled for array greater than 99.\n”);  
  8.  #else  
  9.     printf(“Complied for small array.\n”);  
  10.  #endif  
  11.  return 0;  
  12. }  

注意,#else既是标记#if块的结束,也标记#else块的开始。因为每个#if只能写一个#endif匹配。
 
#elif指令的意思是“否则,如果”,为多重编译选择建立一条if-else-if(如果-否则-如果链)。如果#if表达式为真,该代码块被编译,不测试其他#elif表达式。否则,序列中的下一块被测试,如果成功则编译之。一般形式如下:
#if expression
 Statement sequence
#elif expression1
 Statement sequence
#elif expression2
 Statement sequence
.
.
.
#elif expression
 Statement sequence
#endif 

5.2#ifdef和#ifndef
条件编译的另一个方法是使用编译指令#ifdef和#ifndef,分别表示“如果已定义”和“如果未定义”。#ifdef的一般形式如下:

#ifdef 标识符 

Statement sequence

#endif

如果macro-name原先已经被一个#define语句定义,则编译其中的代码块。

#ifndef的一般形式是:

#ifndef 标识符

 Statement sequence

#endif

如果macro-name当前未被#define语句定义,则编译其中的代码块。
#ifdef和#ifndef都可以使用#else或#elif语句。

[cpp] view plaincopy
  1. #inlucde <stdio.h>   
  2. #define T 10  
  3.    
  4. Int main(void)  
  5. {  
  6.  #ifdef t  
  7.     printf(“Hi T\n”);  
  8.  #else  
  9.     printf(“Hi anyone\n”);  
  10.  #endif  
  11.  #ifndef M  
  12.     printf(“M Not Defined\n”);  
  13.  #endif  
  14.    
  15.  Return 0;  
  16. }  

6.#undef
#undef指令删除前面定义的宏名字。也就是说,它“不定义”宏。一般形式为:
#undef macro-name


7. #defined
除#ifdef之外,还有另外一种确定是否定义宏名字的方法,即可以将#if指令与defined编译时操作符一起使用。defined操作符的一般形式如下:
defined macro-name
如果macro-name是当前定义的,则表达式为真,否则为假。
例如,确定宏MY是否定义,可以使用下列两种预处理命令之一:
#if defined MY

#ifdef MY
也可以在defined之前加上感叹号”!”来反转相应的条件。例如,只有在DEBUG未定义的情况下才编译。
#if !defined DEBUG
 Printf(“Final Version!\n”);
#endif
使用defined的一个原因是,它允许由#elif语句确定的宏名字存在。


8.#line
#line指令改变__LINE__和__FILE__的内容。__LINE__和__FILE__都是编译程序中预定义的标识符。标识符__LINE__的内容是当前被编译代码行的行号,__FILE__的内容是当前被编译源文件的文件名。#line的一般形式是:
#line number “filename”
其中,number是正整数并变成__LINE__的新值;可选的“filename”是合法文件标识符并变成__FILE__的新值。#line主要用于调试和特殊应用。

9.#pragma
#pragma是编译程序实现时定义的指令,它允许由此向编译程序传入各种指令。例如,一个编译程序可能具有支持跟踪程序执行的选项,此时可以用#pragma语句选择该功能。编译程序忽略其不支持的#pragma选项。#pragma提高C源程序对编译程序的可移植性。


10. 预处理操作符#和##
有两个预处理操作符:#和##,它们可以在#define中使用。
操作符#通常称为字符串化的操作符,它把其后的串变成用双引号包围的串。例如:

[cpp] view plaincopy
  1. #include <stdio.h>   
  2. #define mkstr(s) #s  
  3. int main(void)  
  4. {  
  5.     p(mkstr(I like C));  
  6.    
  7.     Return 0;  
  8. }  

预处理程序把以下的语句:
Printf(mkstr(I like C));
变成
Printf(“I like C”);
操作符##把两个标记拼在一起,形成一个新标记。例如:

[cpp] view plaincopy
  1. #include <stdio.h>   
  2. #define concat(a,a) a##b  
  3.    
  4. int main(void)  
  5. {  
  6.  Int xy = 10;  
  7.  Printf(“%d”,concat(x,y));  
  8.  Return 0;  
  9. }  

预处理程序把以下语句:
Printf(“%d”,concat(x,y));
变成
Printf(“%d”,xy);
操作符#和##主要作用是允许预处理程序对付某些特殊情况,多数程序中并不需要。


11. 预定义宏
C规范了5个固有的预定义宏,它们是:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__
 
__LINE__和__FILE__包含正在编译的程序的行号和文件名。
__DATE__和内容形如month/day/year(月/日/年)的串,代表源文件翻译成目标码的日期。
__TIME__中的串代表源代码编译成目标码的时间,形如hour:minute:second(时:分:秒)
如果__STDC__的内容是十进制常数1,则表示编译程序的实现符合标准C。

附:
在预处理中,我们经常用到define ,这里得提到 typedef ,二者用法很相似,大家还不要搞混;具体的区别是什么呢?
1、typedef 是C语言关键字
2、define 是预处理命令
这里介绍一下typedef的应用,typedef经常被用于为另一种数据类型定义一个新名字,在数据结构中更是频繁出现
typedef <已有数据类型> <新数据类型>
下面举个例子介绍:
[cpp] view plaincopy
  1. typedef struct _node_  
  2. {  
  3.     int data;  
  4.     struct _node_ *next;  
  5. }listnode,*linklist;  
这里定义了两个新的数据类型listnode和linklist。其中listnode等价于数据类型struct _node_ 而linklist等价于stuct _node_  *;
或者,我们这样理解,上面的定义就相当于:
[cpp] view plaincopy
  1. struct _node_  
  2. {  
  3.     int data;  
  4.     struct _node_ *next;  
  5. };  
  6. typedef struct _node_ listnode;  
  7. typedef struct _node_ *linklist; 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402561.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 中使用 @Transactional 注解配置事务管理

From: https://blog.csdn.net/nextyu/article/details/78669997 事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务&#xff1b;声明式事务基于 AOP,将…

TCP中间件_Delphi_client

1、界面 1.1、formMain.pas 1.1.1、 object frmMain: TfrmMainLeft 191Top 103Width 542Height 466Caption frmMainColor clBtnFaceFont.Charset DEFAULT_CHARSETFont.Color clWindowTextFont.Height -11Font.Name MS Sans SerifFont.Style []OldCreateOrder False…

python为类定义构造函数

用python进行OO编程时&#xff0c; 经常会用到类的构造函数来初始化一些变量。 class FileData:def __init__(self, data, name, type):self.bits base64.encodestring(data)self.name nameself.type type其中self类似c或者c#的this指针。转载于:https://blog.51cto.com/muz…

Linux C 函数练习

学习函数主要学习的就是函数的声明、定义和调用&#xff0c;下面请看两个例子&#xff0c;来帮助我们学习函数&#xff1a; 题目一&#xff1a; 编写一个函数iswithin()&#xff0c;它接受两个参数&#xff0c;一个是字符&#xff0c;另一个是字符串指针。其功能是如果字符在字…

Java、Mysql、MyBatis 中枚举 enum 的使用

From: https://yulaiz.com/java-mysql-enum/ Java 和 MySql 中都有枚举的概念&#xff0c;合理的使用枚举&#xff0c;可以让代码阅读和数据库数据查询更加直观、高效。那么我们怎么使用呢&#xff0c;什么时候使用&#xff0c;两者之间怎么进行数据关联呢&#xff1f;&#x…

ny12 喷水装置(二)

喷水装置&#xff08;二&#xff09; 时间限制&#xff1a;3000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;4描述有一块草坪&#xff0c;横向长w,纵向长为h,在它的橫向中心线上不同位置处装有n(n<10000)个点状的喷水装置&#xff0c;每个喷水装置i喷水的效果是让…

http请求头大全

转自http://tools.jb51.net/table/http_header HTTP响应头和请求头信息对照表 HTTP请求头提供了关于请求&#xff0c;响应或者其他的发送实体的信息。HTTP的头信息包括通用头、请求头、响应头和实体头四个部分。每个头域由一个域名&#xff0c;冒号&#xff08;:&#xff09;和…

Linux C 指针练习

题目一、 已知数组内容如下 s[] {1,2,3,4,5,6,7,8,9}&#xff0c;输入一个常数 m(1<m<9)&#xff0c;使得该数组内容顺序后移n个位置。如n 3时&#xff0c;数组后移3个位置后的内容为{7,8,9,1,2,3,4,5,6} 代码如下&#xff1a; [cpp] view plaincopy #include <stdi…

Java重载遇到泛型

今天被问到一个有意思的问题&#xff0c;大家都知道重载的概念吧&#xff1a;一个类中定义同名的方法&#xff0c;参数表不同&#xff08;参数类型&#xff0c;或者参数个数不通&#xff09;&#xff1b; 但是&#xff0c;如果是下面这个两个方法呢 public static int fn(List&…

wordpress插件制作视频教程【资料分享】

2019独角兽企业重金招聘Python工程师标准>>> 一共5集&#xff0c;每一集15分钟左右&#xff0c;适合入门用哦~ 资料地址&#xff1a; http://wordpresshy.com/create-plugin 分集介绍&#xff1a; 1 【教学大纲】 1.介绍什么是插件&#xff1b; 2.插件的文件结…

Java中遍历HashMap的5种方式

From: https://blog.csdn.net/w605283073/article/details/80708943 本教程将为你展示Java中HashMap的几种典型遍历方式。 如果你使用Java8&#xff0c;由于该版本JDK支持lambda表达式&#xff0c;可以采用第5种方式来遍历。 如果你想使用泛型&#xff0c;可以参考方法3。如…

字符串按照单词为单位逆序排列

我们前面已经写过一个简单字符串逆序排序的方法&#xff0c;这里再开一个字符串排序问题&#xff1a; 给定一个字符串“I love China”&#xff0c;编写程序完成以单词为单位的逆序&#xff0c;如"China love I",并要求不使用第三方变量保存数据&#xff0c;但可以使…

博主故事:博客提升了我在口腔行业的影响力

自从我上次谈了谈个人开设博客的目的一文后&#xff0c;今天就有一位博友发来了感谢留言&#xff0c;他说他的博客让他在国内做牙齿矫正行业的有了一定影响力&#xff0c;下月被请到上海讲课&#xff0c;也有不少民营机构前来挖他&#xff0c;还被邀请参加中国口腔医学界最盛大…

RabbitMQ入门教程——发布/订阅

什么是发布订阅 发布订阅是一种设计模式定义了一对多的依赖关系&#xff0c;让多个订阅者对象同时监听某一个主题对象。这个主题对象在自身状态变化时&#xff0c;会通知所有的订阅者对象&#xff0c;使他们能够自动更新自己的状态。 为了描述这种模式&#xff0c;我们将会构建…

mybatis枚举自动转换(通用转换处理器实现)

From: https://blog.csdn.net/fighterandknight/article/details/51520595 前言 在上一篇博客&#xff0c;mybatis枚举自动转换实现&#xff0c;已经介绍自动转换的实现步骤&#xff0c;并通过例子告诉大家如何实现枚举的自动转换了。 那么在博客的最后想到&#xff0c;定义一…

linux C 学习---函数指针

我们经常会听到这样的说法&#xff0c;不懂得函数指针就不是真正的C语言高手。我们不管这句话对与否&#xff0c;但是它都从侧面反应出了函数指针的重要性&#xff0c;所以我们还是有必要掌握对函数指针的使用。先来看看函数指针的定义吧。 函数是由执行语句组成的指令序列或者…

如何在MyBatis中优雅的使用枚举

From: https://segmentfault.com/a/1190000010755321 问题 在编码过程中&#xff0c;经常会遇到用某个数值来表示某种状态、类型或者阶段的情况&#xff0c;比如有这样一个枚举&#xff1a; public enum ComputerState {OPEN(10), //开启CLOSE(11), //关闭O…

CSS3与页面布局学习笔记(六)——CSS3新特性(阴影、动画、渐变、变形( transform)、透明、伪元素等)...

一、阴影 1.1、文字阴影 text-shadow<length>①&#xff1a; 第1个长度值用来设置对象的阴影水平偏移值。可以为负值 <length>②&#xff1a; 第2个长度值用来设置对象的阴影垂直偏移值。可以为负值 <length>③&#xff1a; 如果提供了第3个长度值则用来设置…

隐藏nginx 版本号信息

为了安全&#xff0c;想将http请求响应头里的nginx版本号信息隐藏掉&#xff1a; 1. nginx配置文件里增加 server_tokens off; server_tokens作用域是http server location语句块 server_tokens默认值是on&#xff0c;表示显示版本信息&#xff0c;设置server_tokens值是off&am…

Linux C编程学习--main()函数简析

提到C语言的函数&#xff0c;有太多内容要讲&#xff0c;今天我们要看的是main()函数。 main()函数时程序的入口点&#xff0c;任何程序都要有main()函数&#xff0c;一般大家都怎么写main()函数啊&#xff1f; main(); void main(); void main(void); int main(); int main(vo…