#define 宏定义命令
/*宏定义指令#define用来定义一个标识符和一个字符串,以这个标识符来代表这个字符串,在程序中每次遇到该标识符时就用所定义的字符串替换它。
定义一般形式:#define 宏名 字符串
*表示这是一条预处理命令。
*宏名是一个标识符,必须符合C语言标识符的规定。
*字符串可以是常数、表达式、格式字符串等
*#define命令出现在程序中的函数的外面,宏名的有效范围为定义命令之后到此源文件结束
*/
#include<stdio.h>
#define SIDE 5 //宏名定义后,就可以成为其它宏名定义中的一部分
#define PETIMETER 4*SIDE
#define AREA SIDE*SIDE
#define STANDARD "You are welcome to join us\n" //如果串长于一行,可以在该行行尾用\续行
/*
*带参数的宏定义
*一般形式:#define 宏名(参数表)字符串
*/
#define MIX(a,b) ((a)*(b)+(b))
#define SQR(x) (x)*(x)
int main()
{
int x=3,y=4;
printf("x,y:\n");
printf("%d,%d\n",x,y);
printf("the min number is:%d\n",MIX(x,y));
y=SQR(x+y);
printf("y=%d\n",y);
printf(STANDARD);
#undef STANDARD //终止宏定义的作用域
printf("STANDARD\n");
return 0;
}
/*明示常量(符号常量):#define定义 */
//每行#define由3部分组成:1.预处理命令 2.宏 3.替换体或替换列表
//从宏变成可替换文本的过程叫做宏展开
#include<stdio.h>
/**类对象宏**/
#define TWO 2 //可以使用注释:
#define OW "Consistency is the last\
tive,haha" //反斜杠把该定义延续到下一行
#define FOUR TWO*TWO
#define PX printf("X is %d",x)
#define STR puts("\n********************")
#define FMT "X is %d.\n"
//宏替换体是记号型字符串,而不是字符型字符串(记号:替换体中单独的【用空白把这些词分开】词 )
#define SIX 2 * 3
/**类函数宏**/
#define SQUARE(X) ((X)*(X))
#define PSQR(X) printf("The square of X is %d",((X)*(X)))
/*预处理粘合剂:##(把两个记号粘合为一个记号)*/
#define XNAME(n) x##n //宏展开为x4
#define PRINT_XN(n) printf("x"#n"=%d\n",x##n)
/*变参宏:(宏参数)...*__VA_ARGS__(用于替换体中,代表省略代表什么)*/
#define PR(...) printf(__VA_ARGS__)
/*#undef指令:取消已定义的#defien指令*/
int main()
{
int x=FOUR;
/*
int x=TWO*TWO;
int x=2*2;
*/
PX;
STR;
printf(FMT);
STR;
PSQR(3);
STR;
int XNAME(1)=14; //变成int x1=14
int XNAME(2)=20;
int x3=30;
PRINT_XN(1);
PRINT_XN(2);
PRINT_XN(3);
PR("Howdy"); //一个参数
STR;
PR("weight=%d,shipping=$%d\n",23,45);
return 0;
}
/*作用:删除事先定义好的宏定义(将宏名局限在仅需要的代码段中)
*一般形式:#undef 宏替换名
*/
#include<stdio.h>
#define ABC 1
int main()
{
int i=0;
i=i+ABC;
printf("i=%d\n",i);
#undef ABC
printf("i=%d\n",i);
return 0;
}
条件编译
/*
*只希望其中一部分内容在满足一定条件时才进行编译,
* #if 常数表达式
* 语句段
* #endif
*含义如果#if命令后的参数表达式为真,则编译#if到#endif之间的程序段,否则跳过这段程序
*/
#include<stdio.h>
#define NUM 50
int main()
{
int i=0;
#if NUM>50
i++;
#endif
#if NUM==50
i=i+50;
#endif
#if NUM<50
i--;
#endif
printf("now i is:%d\n",i);
return 0;
}
#include<stdio.h>
#define NUM 50
int main()
{
int i=0;
#if NUM>50
i++;
#else //作用:为#if为假时提供另一种选择
#if NUM<50
i--;
#else
i=i+50;
#endif
#endif
printf("i=%d\n",i);
return 0;
}
#ifdef MAVIS
#include "horse.h" //如果已用#define 定义了MAVIS,则执行下面的指令
#define STABLES 5
#eles
#include "cow.h" //如果没用用#define定义MAVIS,则执行下面的指令
#define STABLES 15
#endif
/*常用这种方法调试程序*/
#include<stdio.h>
#define DEBUG
/*int main()
{
int i;
int total=0;
for(i=1;i<=4;i++)
{
total+=i;
#ifdef DEBUG
printf("i=%d\n",i);
#endif
}
printf("total=%d",total);
}
*/
//防止相同的宏被重复定义
/*arrays.h头文件*/
#ifndef SIZE
#define SIZE 100
#endif
#define SIZE 10
//#inlude"arrays.h" //SIZE则被设置成10,当执行到这行时,由于SIZE是已定义的,所以跳过了#defien SIZE 100这行代码
/*ifndef指令通常用于防止多次包含一个文件:应该像下面这样设置头文件*/
/*things.h*/
#ifndef THINGS_H_
#define THINGS_H
/*
头文件中其他内容
*/
#endif
#if SYS==1
#include "ibm.h"
#endif
#if SYS==1
#include<stdio.h>
#elif SYS==2
#include<string.h>
#elif SYS==3
#include<stdlib.h>
#else
#include<limits.h>
#endif
//测试名称是否定义
/*这里defined是一个预处理运算符,如果 它的参数用#define定义过,则返回1,否则返回0
*/
#if defined(MA)
#include<stdio.h>
#elif defined(MAX)
#include<string.h>
#endif
/*编译指示
_Pragma:预处理运算符,把字符串转换成普通的编译指示。
例如:-Pragme("nonstandardreatmenttypeB on")
等价于下面的指令:
#pragma nonstandardreatmenttypeB on
该运算符不使用#符号,所以可以把它作为宏展开的一部分
#define PRAGM(X) _Pragma(#X)
#defien LIMRG(X) PRAGMA(STDC CX_LIMITED_RANGE X)
*/
#pragma c9x on //让编译器支持C9X
//一些预定义宏
//#error让预处理器发出一条错误消息,该消息包含指令中的文本。如果可能的话,编译过程应该中断。
/*#if__STDC_VERSION__==201112L
#error 不是C99
#endif
*/
//
#include<stdio.h>
#include<stdio.h>
int main()
{
printf("预处理的日期(\"Mmm dd yyyy\"形式的字符串字母量):%s\n",__DATE__);
printf("表示当前源代码文件名的字符串字面量:%s\n",__FILE__);
printf("表示当前源代码文件中行号的整型常量:%d\n",__LINE__);
printf("设置为1时,表名实现遵循C标准:%d\n",__STDC__);
printf("本地环境设置为1,否则设置为0:%d\n",__STDC_HOSTED__);
printf("支持C99标准,设置为199901L;支持C11标准,设置为201112L:%ld\n",__STDC_VERSION__);
printf("翻译代码的时间,格式为\"hh:mm:ss\"%s\n",__TIME__);
/*预定义标识符,它展开为一个代表函数名的字符串,*/
printf("这个函数是:%s",__func__);
//重置__LINE__和__FILE__宏报告的行号和文件名
#line 1000 //把当前行号重置为1000
#line 10 "cool.c" //把行号重置为10,把文件名重置为cool.c
}
/*在#if条件编译命令中,需要判断符号常量所定义的具体值,但有时并不需要判断具体值,只需要知道这个符号常量定义了没有
*这时就可以采用另一种条件编译的方法即#ifdef与#ifndef命令,分别表示“如果有定义”和“如果没定义”
*/
#include<stdio.h>
#define STR "呵呵"
int main()
{
#ifdef STR
printf(STR);
#else
printf("没有被定义");
#endif
printf("\n");
#ifndef ABC
printf("傻逼,没有此定义");
#else
printf(STR);
#endif
printf("\n");
return 0;
}
文件包含
文件包含的作用是把指定的文件模块内容插入到#include所在的位置
使用尖括号时,系统到存放c库函数头文件所在的目录中寻找要包含的文件,这为标准方式
用双引号时,系统先在用户当前目录中寻找要包含的文件,若找不到,再到存放c库函数头文件所在的目录中寻找要包含的文件。
通常情况:如果为调用库函数用#include命令来包含相关的头文件,则用尖括号可以节省查找的时间,
如果要包含的是用户自己编写的头文件,一般用双引号。用户编写的文件通常是在当前目录中,如果文件不在当前目录中,双引号可给出文件路径
注:1.一个#include命令只能指定依噶被包含的文件
2.文件包含可以是嵌套的,即在一个被包含文件中还可以包含另一个被包含文件
头文件:经常用在文件头部的被包含的文件称为“标题文件”,或“头部文件”,一般以.h为后缀,
一般情况下,将如下内容放到.h文件中:
- 1.明示常量——stdio.h中定义的EOF、NULL、BUESIZE(标准I/O缓冲区大小)
- 2.宏函数
- 3.结构、联合、枚举声明
- 4.typedef声明
- 5.外部函数声明
- 6.全局变量声明
其他预处理命令
/*#line用来显示_LINE_与_FILE_的内容。
*_LINE_用来存放当前编译行的行号
*_FILE_用来存放当前编译的文件名
#line的一般形式如下:
#line 行号["文件名"]
其中行号为任一正整数,可选的文件名为任意有效文件标识符。文件名为源文件的名字,行号为源程序中当前行号
*主要用于调试及其他特殊应用
*/
#include<stdio.h>
int main()
{
printf("当前行号:%d\n",__LINE__);
printf("当前行号:%d\n",__LINE__);
printf("文件名:%s\n",__FILE__);
#line 100 "hhaha.c"
printf("当前行号:%d\n",__LINE__);
printf("文件名:%s\n",__FILE__);
return 0;
}
内联函数
/*函数调用:建立调用、传递参数、跳转到函数代码并返回。使用宏使代码内联,可以避免这样的开销,
把函数变成内联函数建议尽可能快地调用该函数 ,具体效果由实现定义,因此,把函数变成内联函数,编译器可能会用内联代码替换函数调用
,并(或)执行一些其他的优化,但是也可能不起作用
创建内联函数的定义有多种办法:标准规定具有内部链接的函数可以成为内联函数,还规定了内联函数的定义
与调用与该函数的代码必须在同一文件中。
最简单的方法是使用函数说明符 inline 和存储类别说明符 static。通常,内联函数应定义在首次使用它的文件中,
所以内联函数也相当于函数原型。
*/
#include<stdio.h>
inline static void eatline() //内联函数定义/原型
{
while(getchar()!='\n')
continue;
}
int main()
{
char ch;
while(ch=getchar()!='\n')
{
eatline(); //编译器查看内联函数的定义(也是原型),可能会用函数体中的代码替换eatline()函数调用,相当于在函数调用位置输入函数体中的代码
putchar(ch);
}
}
_Noreturn函数
Noreturn函数:表明调用完成后函数不返回主调函数。Noreturn 的目的是告诉用户和编译器,这个特殊的函数不会把控制
返回主调程序。告诉用户以免滥用该函数