C语言组成
一、C语言写工程、项目
一个项目会由多个模块组成,每个模块之间是相对独立,每个模块是由0个或多个**.c和.h**组成,各个模块之间可以分开编码研发。
如果继续一个**.c文件去编写,各个模块之间不能分开编码研发—>需要分为多个.c**
一般来说哦,每写一个**.c文件对应写一个.h**文件
.c文件里面写的是实现某个模块里的函数
.h里面写**.c**文件中函数的声明、类型声明……声明语句
如果A模块想用B模块的函数,就需要包含B模块的一个**.h**文件。
二、.c文件组成
一般来说,由以下部分组成
#### (1)头文件包含部分 ####
#include<xxxxxx.h>
#include"xxxxxx.h"
#include是把该.h文件/头文件的内容复制粘贴过来
什么时候拿过来?预处理时赋值(编译过程的一个小步骤)
<> 与 " "区别:
include 包含头文件时,只给定了头文件的名字;没有给位置
编译器会有一个默认的查找路径:/usr/include
<> 就是去默认路径找;
" " 先在当前路径找,找不到文件,再去默认的路径下找
所以,一般如果是系统头文件 就用< >;包含自己写的头文件用这个" "
(2)宏定义部分
定义了一个“宏”,宏是用来替换其他内容的东西,一旦定义好了就不能修改
a.不带参数的宏:
定义语法:
<u>#define 宏名(标识符,不能为关键字) 表达式
如:#define PI 3.14
PI是个宏,在程序后面的每一个地方,只要出现PI的地方,都替换成3.14
什么时候替换?
预处理时进行替换(编译过程的一个小步骤)
gcc -E xxxx.c xxxx.i (预处理的命令)
在预处理时,代码里面只要有PI都会用3.14替换
优势:
可读性强,见名知义
可维护性强,比如项目中要用到圆周率,但是突然需要改变精度,需要多位小数,如果直接换,需要修改多次且有可能把需要的数据也给改掉。有宏的话,只需要改一个地方就好了。
b.带参数的宏
定义语法:
#define 宏名(参数) 表达式
建议表达式打括号,并且参数也打括号
如:#define MAX(a,b) a>b?a:b
int m = MAX(a,b);
替换后 :int m = a>b?a:b;
m = MAX(a&3,b);
<u>----> m = a&(3>b)?a&3:b</u>
不会自动加括号,单纯替换,所以写宏的时候参数给打1括号
所以写宏的时候给参数打括号
#define MAX ((a),(b)) (a) > (b)?(a):(b)**
总结:
宏定义在预处理进行替换,不会进行语法检测,也不会增加其他内容比如括号
建议打括号,并且参数也打括号
宏和函数有什么区别
(1)函数在运行才会执行、宏是预处理时替换
(2)函数在执行时会有额外的开销:会为形参分配内存、现场保护和现场恢复、……
宏没有额外开销,效率高
为什么不把函数全换为宏
不好,函数可读性更高;
宏只实现简单的功能。
(3)声明部分
C语言中声明是指把一个标识符/符号与 一个对象关联起来
a.外部变量的声明:
语法:
extern 变量类型 变量名;
比如:
1.c有个重要的全局变量 int a = 10;
在2.c中需要用到它,只需要在2.c中进行一个声明 extern int a;
作用:告诉编辑器,变量a不是本文件中定义的,是在本项目或工程其他文件定义的。如果不想其他文件使用的话,就用static修饰。
b.外部函数的声明:(基本不用,一般写在头文件内)
1.c:
int max(int a,int b)
{
if(a > b)
return a;
else
return b;
}
2.c要用这个函数
extern int max(int a,int b);
c.类型的声明
struct xxx{
类型 变量名;
……
};
类型的声明一般也是放在头文件内,其他文件想使用,就包含该头文件。
写在.c内,表示只有当前文件可以使用这个类型
d.全局变量的定义及函数的实现
条件编译
1、有选择的编译
#if 表达式
代码块1
# else
代码块2
#endif
如果表达式的值为真,就会编译 代码块1,不会就编译 代码块2
表达式的值为假,就会编译 代码块2,不会就编译 代码块1
不会编译:相当于给注释
2、
#ifdef 宏名
代码块
#endif
如果“宏名”之前定义了的话,才会编译代码块,否则不会编译
3、
#ifndef 宏名
代码块
#endif
如:
#ifndef __TEST_H__
#define __TEST_H__
如果“宏名”之前没定义了的话,才会编译代码块,否则不会编译
但是注意一点,不管是 ifdef 还是 ifndef 只会判断宏名有没有 不管 宏 是什么,与宏无关
三、.h文件组成
.h 文件中 写声明语句:函数的声明,类型的说明……
一般来说 一个.c 对应一个.h ,这个.h 文件中的函数(包括注释),以及需要用到的类型
注释: 至少要有以下几点:
函数功能
函数参数的意思
函数返回值
函数的“坑”
为了解决头文件被一个.c 文件重复/多次 包含而产生的错误,
使用 条件编译 来处理,
效果: 无论包含几次,都跟包含一次效果一样
一个项目中无论有多少个 .c 和 .h ,main函数有且仅有一个。
编译时需要把项目中所有的.c文件包含: gcc xxx1.c xxx2.c … -o