一个c语言项目工程由多个 .c 和 .h文件组成 .c 源码文件 .h 头文件
1,.c文件组成
一般来说,.c文件由以下部分组成
(1)头文件包含部分
#include<头文件名字> //包含系统头文件
或
#include"头文件名字" //包含自己写的头文件
作用就是把 该头文件在 这个位置展开-》就是把这个头文件的内容复制粘贴到这个位置 这里有一个问题:包含头文件的时候,只写了文件的名字,没有写文件的路径,怎么找到的? 每个系统都有“环境变量”,我们需要设置它,指定编译器、头文件、库文件等重要文件的路径(位置) <> 和 "" 的具体区别 <>中的头文件 会去 "环境变量"中指定路径去找。 ""中的头文件 会先在 当前工程指定的路径(如果没有指定,默认就是当前文件夹)下去找,如果没有 再去 "环境变量"中指定路径去找。如果都不存在,则报错 (2) 宏定义部分 宏定义语法: 不带参数 #define 宏名 表达式 作用是:在编译(预处理)时刻进行替换,用 表达式 替换 代码中出现的 宏名 如: #define Pi 3.14 在编译(预处理)的时候,文件中凡是有 Pi的地方,全部用 3.14去替换
带参数 #define 宏名(参数) (表达式) 如: #define MAX(a,b) (a>b?a:b) int x=10; int y=20; int c; c = MAX(x,y); //替换之后: c= (x>y?x:y); c = MAX(x+1,y-1) + 1;//替换之后: c = (x+1>y-1?x+1:y-1) + 1; c = MAX(x&10,y/3) ; //替换之后: c = (x&10>y/3?x&10:y/3); 本来的意思是:求 x&10 和 y/3中的最大值并赋值给c ,-》 c = 10 替换之后的结果: c = 6,原因是 >的优先级 高于 & 所以:定义宏的时候,不仅整个表达式需要加括号,而且每个参数都需要加括号 -》 #define MAX(a,b) ((a)>(b)?(a):(b)) c = MAX(x&10,y/3) ; //替换之后: c = ((x&10)>(y/3)?(x&10):(y/3)) ; 练习: int x=10; int y=5; #define SQR(x,y) ((x/y)*(x/y)) 求 SQR(x/y,x*y) 的值 0 ((x/y/x*y)*(x/y/x*y)) 即使每个参数都加了括号,有些特殊情况,还是会有问题: 替换宏的时候,会多次执行参数表达式 例如: int a = 8; int b = 9; int x = MAX(a++,b++);//本来的意思是求 a++和b++这两个表达式的最大值并赋值给x //x = 9 但事实上: int x = ((a++)>(b++)?(a++):(b++)) ; x = 10;
怎么改进?? #define MAX(a,b) ({ \ typeof(a) aa = a;\ typeof(b) bb = b;\ (aa)>(bb)?(aa):(bb);\ }) int x = MAX(a++,b++); 替换之后: int x = ({ \ typeof(a++) aa = a++;\ typeof(b++) bb = b++;\ (aa)>(bb)?(aa):(bb);\ }) typeof(a++) 只是求a++的类型,并不会进行计算 宏和函数的区别: 调用函数需要额外的开销(比如说为形参分配内存,现场保护和现场恢复..) 但是宏不需要开销,因为是在编译时刻直接替换(运行时刻根本就没有宏了) 宏只适用于简单的功能,稍微复杂的功能肯定还是得写函数
(3) 声明部分 (如果有.h文件的话,这部分应该写在.h文件中) 声明:就是告诉编译器这是个什么东西 函数声明 结构体/共用体/枚举等类型声明 外部变量的声明 函数声明: 返回值类型 函数名(形参1类型 形参1名,形参2类型 形参2名 ...); 或者 返回值类型 函数名(形参1类型 ,形参2类型 ...); 类型声明: struct student { int id; char name[20]; double score; //.... }; 声明了一个新的类型 : struct student typedef struct student Student; 声明了一个类型 Student ..... 外部变量的声明 什么叫做外部变量?定义在这个项目工程的其它文件中的未被 static修饰的全局变量(不是定义在当前文件) 声明语法: extern 类型 变量名; 例如:这个项目工程有2个.c 文件 1.c 2.c int data = 100;//全局 想要访问 data,必须要进行外部声明: extern int data;//告诉编译器这是定义在别的文件中的一个变量 //声明之后就可以在 2.c文件中访问 data了 一个工程中如果有多个.c文件,怎么编译? gcc .c文件名列表 (4) 全局变量的定义 类型 变量名; 类型 变量名=初始值; 定义会为变量分配内存空间 声明不会,声明只是告诉编译器这个东西是什么 (5) 函数定义 返回值类型 函数名(形参1类型 形参1名,形参2类型 形参2名 ...) { ..... } int main() { .... } void test1() { ..... } 功能性语句必须要写在函数内部(除各种定义和声明语句外)!!!! 条件编译:有选择的进行编译 (1) #if 表达式 ...我们写的代码..... //如果 #if 后面的表达式成立,这段代码就会编译 //如果 #if 后面的表达式不成立,这段代码就不会编译 #endif ------------------------------ (2) #if 表达式 ...我们写的代码1..... #else ...我们写的代码2..... #endif 如果#if后面的表达式成立,只会编译 代码1 如果#if后面的表达式不成立,只会编译 代码2 ----------------------------------- (3) #ifdef XXX //( #ifdef : if define) ...我们写的代码..... //如果前面定义了 XXX这个宏,那么就会编译此处的代码 //如果前面没有定义了 XXX这个宏,那么就不会编译此处的代码 //判断条件是: 有没有这个宏,而不是这个宏的内容 #endif
--------------------------- (4) #ifndef XXX //( #ifndef : if not define) ...我们写的代码..... //如果前面没有定义 XXX这个宏,那么就会编译此处的代码 //如果前面定义了 XXX这个宏,那么就不会编译此处的代码 //判断条件是: 有没有这个宏,而不是这个宏的内容 #endif
2, .h文件
一个简单的项目可以没有 .h文件
但是稍微复杂一点的项目 必须要写 .h文件
为啥需要写 .h文件 ? 在进行项目开发时,需要把功能模块化:一个功能是一个模块,一个模块对应着一个 .c和.h文件 这样便于多人共同开发(一个人负责若干个模块,一个人负责若干个.c和.h文件的编写); 张三:负责A功能 A.c -> 写了很多关于A功能的函数 李四:负责B功能 B.c -> 写了很多关于B功能的函数 王五:负责C功能 C.c -> 写了很多关于C功能的函数 主函数单独写在一个文件中,不写在功能模块.c文件中 这些功能之间不是完全独立的,意味着各个功能模块间需要相互调用函数,使用其它模块的自定义数据类型.... 张三要调用李四的函数,怎么办? 需要问李四这个函数长什么样(名字是什么,参数是什么,返回值是什么...) 调用前需要进行声明 王五要调用李四的函数,怎么办? 需要问李四这个函数长什么样(名字是什么,参数是什么,返回值是什么...) 调用前需要进行声明 ...... 效率太低 -》 每个.c文件对应一个 .h文件 ,.h文件中写各种声明语句 :类型的声明 、函数的声明、外部变量的声明 ... 有了.h文件后,A模块需要调用B模块的函数,只需要包含B模块的.h文件 。
.h文件中写各种声明语句 :类型的声明 、函数的声明(包括注释)、外部变量的声明..... 根据 .c文件来写,.c文件中包含了哪些东西,.h文件中就声明什么
具体的应用参考: main.c math.c math.h sort.c sort.h 为了避免重复包含头文件(一个.c文件直接或间接包含多次同一个.h文件)的危害,.h文件应该这个写: #ifndef __XXX_H__ //XXX是这个头文件的名字 #define __XXX_H__ 头文件的内容 #endif
int a[][3] = {{1,2,3},{4,5,0}}, (*pa)[3],i; pa = a; -> pa = &a[0]; pa[1][i] <-> *(*(pa+1)+i) <-> *(*(&a[0]+1)+i) <-> *(a[1]+i) <-> *(&a[1][0] + i) <-> *(&a[1][i]) <-> a[1][i] for(i=0;i<3;i++) { if(i<2) pa[1][i]-1; else pa[1][i] = 1; }
作业: 声明一个复数类型 struct complex { int real; int imag; }; 复数基本操作有 加减乘除,写函数实现 要求: .c .h