头文件和宏定义

文章目录

        • 一、头文件
          • 头文件的正文内容
            • 注意:
            • 头文件写法
            • 文件引用
            • 头文件引用<>和””的区别?
            • Makefile:工程管理器
        • 二、宏
            • 编译过程:
            • 基本语法
            • 命名规则:
            • 说明:
            • \#undef 宏名
            • 预定义宏:
            • 带参宏:
            • 说明:
            • 宏延续运算符 \
            • 带参宏和函数的区别:
            • 字符串常量化
            • 标记粘贴运算符

一、头文件

​ 在编写源码过程中,随着功能的复杂,所使用的各种函数模块和其他相关的技术会迅速增加,如果所有的代码都放在源文件.c里面,会显得组织难以维护,互相引用非常 频繁,尤其是大家都要用到的部分内容(比如:函数声明,结构体模板,头文件引用。。。)最好能有一个统一的地方,专门用来存放这些东西。那么我任意一个源文件在需要用这些的时候直接从这里拿,这样可以优化工程项目的代码结构,也方便维护和升级

​ 这个统一存放相应信息的地方就是头文件,以.h为文件名后缀

头文件的正文内容

​ 1其他头文件的引用

​ 2宏定义

​ 3结构体、联合体、枚举的声明

​ 4全局变量

​ 5静态函数或内敛函数定义

​ 6普通函数的声明

注意:

普通函数的定义不能放在头文件中,假如这个头文件被多个.c引用,那么当他们在一起编译的时候,会出现函数重定义的错误

​ static函数和inline函数能定义在头文件里面,而且一般将static函数定义在头文件

​ 由于头文件可以嵌套包含别的头文件,还有就是多个源文件可能包含同一个头文件,为了防止头文件被重复引用,头文件的书写格式是有一定要求的。

头文件写法
#ifndef _SHOP_H_   //如果没有定义_SHOP_H_宏
#define _SHOP_H_   //定义_SHOP_H_宏

//头文件正文

#endif      //结束定义

文件引用
#include 
#include “shop.h”  
头文件引用<>和””的区别?

<>:直接从标准库去寻找头文件

“”:先从当前工程目录去寻找头文件,如果没有找到就会去标准库路径查找相应的头文件

所以一般来说:要引用标准库头文件就用<>,引用自定义头文件就用””

编译命令:

gcc ./src/* -o ./bin/main -I ./include/

-I:表示指定头文件所在路径

Makefile:工程管理器

​ 相当于就是去编译你的整个工程的脚本

all:
	gcc ./src/* -o ./bin/main -I ./include/
clean:
	rm ./bin/main

在Makefile存放的路径下make

make clean:删除bin/main

二、宏

什么是宏?

​ 宏定义,就是一个标识符表示一串字符,可以是常量,表达式,可以带参数,如果后面的代码出现了该标识符,那么就全部替换成指定的这串字符

​ 宏可以写在函数里面

编译过程:

​ 预处理,编译,汇编,链接

gcc编译代码的时候,在预处理过程中,会将所有的宏进行替换,而替换过程中,不检查语法错误

基本语法

#define 宏名 字串

#表示这里是一条预处理命令

宏名:是标识符的一种,命名规则和变量一样,一般用大写字母

字串:可以是数字,表达式,if语句,函数,字符串

命名规则:

​ 1、由字母,数字,下划线组成

​ 2、不能以数字开头

​ 3、不能是C语言自带的关键字

说明:

​ 宏定义用宏名来表示一个字串,在宏展开的时候,用这个字串来取代宏,这个是一种简单粗暴的替换(不会做任何的检查,也不做任何运算)

​ 宏定义不是说明或语句,不能在末尾加分号

​ 宏写在函数体外,作用域从定义开始到程序结束,终止宏定义作用域

​ 在代码中宏被双引号包围,预处理过程中就不会对这个位置进行宏替换

​ 宏定义语句允许嵌套

​ 习惯上宏名一般全部大写,便于与普通变量区别

​ 宏定义可以表示数据类型,但是一般不这么做

#undef 宏名
预定义宏:
__DATE__       预处理日期,格式”MM DD YYYY”的字符串

__TIME__        预处理时间,格式”HH MM SS”的字符串

__FILE__        当前文件名

__LINE__        当前行号

__FUNCTION__   当前函数

STDC       当编译器标准为ANSI的时候,值为1
带参宏:

语法:

#define 宏名(形参列表) 字串

说明:

​ 带参宏不需要指定返回值类型和形参类型

​ 带参宏定义中,宏名和形参列表之间不能有空格,参数列表之间是可以加空格的

​ 在宏定义中,字串内的形参通常要用括号括起来,避免直接替换过程中由于没有考虑到运算符优先级问题,导致的逻辑错误

#define MAX(x, y) ((x)>(y)?(x):(y))   
宏延续运算符 \

当宏在一个单行上如果太长,可以使用这个运算符把字串分行表示

#define MAXX(x, y) \
				({\
					int _a = x;\
					int _b = y;\
					((_a)>(_b)?(_a):(_b));\
				})
int d = MAXX(10, 20);

宏可以用do…while(0)将宏的多条语句这种字串放在一起

#define MAXXX(x, y) do{\
					int _a = x;\
					int _b = y;\
					((_a)>(_b)?(_a):(_b));\
				}while(0)

需要注意:这种写法不带返回值

带参宏和函数的区别:

​ 宏展开仅仅是字串的替换,不会进行任何的表达式计算

​ 宏在编译之前(预处理)就被处理掉了,没有任何机会参与编译,不会占内存

​ 函数是重复使用的代码,会被编译,每次调用函数是执行这块空间的代码,函数形参和局部变量都会分配内存

字符串常量化
#define PRI(a, b) printf("%s and %s we are family\n", a, b)
#define PRIN(a, b) printf(#a " and " #b " we are family\n")				

int main(int argc, char const *argv[])
{
	PRI("xiaoming", "xiaohong"); 
	PRIN(xiaoming, xiaohong); 
	printf("%s  and  %s  we are family\n", "xiaoming", "xiaohong");
    return 0;
}
标记粘贴运算符
#define PRINT(a) printf("n"#a " = %d\n", n##a)

int main(int argc, char const *argv[])
{
	int n1 = 100;
	PRINT(1);
    return 0;
}

d %s we are family\n", “xiaoming”, “xiaohong”);
return 0;
}


###### 标记粘贴运算符  ##

 ```c
 #define PRINT(a) printf("n"#a " = %d\n", n##a)
 
 int main(int argc, char const *argv[])
 {
 	int n1 = 100;
 	PRINT(1);
     return 0;
 }
 

你可能感兴趣的:(Linux环境编程,linux,c语言)