------- android培训、java培训、iOS培训、.Net培训、期待与您交流! ----------
1、预处理指令的概念及分类
以“#”号开头的预处理指令。如包含命令#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称为预处理指令。
宏名一般用大写或者以k开头,变量名一般用小写。
预处理指令的位置可以随便写(作用域:从编写指令的那一行开始,一直到文件结尾)。
1)文件包含指令
(1)格式
#include " " 包含的是一个用户定义的文件,可以是头文件,也可是普通文件 。
当包含我们自己写的文件就是使用 #include "",当包含系统提供头文件的时候,就是用#include <> 。例如:
#include <stdio.h>
#include <math.h>
#include "one.h"
(2)文件包含的实质:
文件包含命令的功能是把指定的文件内容插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件
include 可以包含头文件,也可以包含文本文件。
include 不一定非要写在第一行
int main(int argc, const char * argv[]) {
//把当前目录中a.txt文件中的内容替换到当前写include的地方
//当前目录:和main.c同一个文件夹下得目录
#include "a.txt"
#include "a.txt"//可以重复包含
return 0;
}
2)宏定义
C语言中我们自定义的特殊标示符,习惯大写宏的定义:
#define 宏名 宏字符串(可以是常量、变量、表达式)
注意:预处理指令,经常写在函数之前
被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
宏分为有参宏和无参宏两种。
无参宏 #define M 10
有参宏 #define SUM(a) a+a
SUM(3) //不仅要用a+a替换,而且还要把 实参3代入到字符串中
关于无参宏的用法和注意事项如下:
#include <stdio.h>
#define M 10
#define M1 y*y+3*y
#define R 4
#define PI 3.14
#define AREA PI*R*R //嵌套定义
#define INT1 int
#define P struct Person
void test(){
//#undef M //此处的作用是,取消宏定义
printf("M = %d\n",M);
}
int main(int argc, const char * argv[]) {
int a[M+2]; //相当于int a[12]
printf("%d\n",M); //把M的值打印出来
int y = 3,result=0;
result = 3*M1+2*M1-50;
//错误的
// 3*(y*y+3*y)+2*(y*y+3*y)-50;
//正确的
// 3*y*y+3*y+2*y*y+3*y-50;
printf("result = %d\n",result);
//宏使用的注意事项
//1、宏是有作用域的 #undef 宏名 可以取消宏定义
test();
//2、在字符串中出现的宏名不会被替换
printf("M不会替换");
//3、宏可以嵌套定义
printf("%.2f\n",AREA);
//4、使用宏起别名
INT1 a1;
a1 = 10;
printf("a1 = %d\n",a1);
P{
int age;
};
P p1 = {23};
return 0;
}
(1)宏的形参之间可以出现空格,但是宏名和形参之间不能出现空格。
(2)有参宏的参数最好用括号括起来,避免宏替换时与理想状况有差。
#include <stdio.h>
#define M1(x ,y) x*y+x+y
//#define M1(x ,y) (x)*(y)+(x)+(y)
int main(int argc, const char * argv[]) {
int a=3;
result = M1(a+3, a-1);
//(x,y)--> x*y+x+y
//理想:(a+3)*(a-1)+a+3+a-1=20
//实际:a+3*a-1+a+3+a-1=19
3)条件编译
为什么要使用条件编译
(1)按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。有利于程序的移植和调试。
(2)条件编译当然也可以用条件语句来实现。 但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。
#include <stdio.h>
#define score 99
int main(int argc, const char * argv[]) {
//传统方式
int score = 76;
//判断成绩属于哪个等级
if(score<60){
printf("E\n");
}else if (score<=69){
printf("D\n");
}else if (score<=79){
printf("C\n");
}else if (score<=89){
printf("B\n");
}else{
printf("A\n");
}
//#if-#else条件编译指令
#if score < 60
printf("E\n");
#elif score <= 69
printf("D\n");
#elif score <= 79
printf("C\n");
#elif score <= 89
printf("B\n");
#else
printf("A\n");
#endif
return 0;
}
条件编译指令
#if #elif #else #endif
#ifdef 用来判断某个宏是否定义
#include <stdio.h>
#define DEBUG1 1
#define DEBUG2 0
int main(int argc, const char * argv[]) {
int a = 0;
//ifdef检测宏是否定义
#ifdef DEBUG1 //DEBUG 系统已经定义了这个宏了
a = 10;
#else
a = 10000;
#endif
//ifndef 检测宏是否定义 ifndef 如果没有定义
#ifndef DEBUG2
a = 100;
#else
a = -1;
#endif
printf("%d\n",a);
return 0;
}
2、模块化编程的概念
我们把功能相似的函数封装到不同的文件中
1)模块化编程的优点:
(1)通过头文件来调用库功能。
在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。
(2)多文件编译。
将稍大的项目分成几个文件实现,通过头文件将其他文件的函数声明引入到当前文件。有利于团队的分工协作。
(3)头文件能加强类型安全检查。
如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。
2)实现方法:
.c C语言的源文件:包含头文件即可,便可调用里面的方法。
.h (header)头文件的作用:
(1)包含方法的声明,但是不能实现方法
(2)声明变量
(3)声明结构体