一、预处理命令
(一)编译过程
1、预处理
作用:将代码中相关预处理命令执行,最终生成出一个只包含C语言代码的文件;
2、编译
作用:对语法进行检查,将这个c的源代码生成汇编代码;
3、链接
作用:将使用到的其他代码链接到一起,生成最终可执行文件;
4、可执行程序
注:C语言中的预处理功能的最终效果是做文本替换。
(二)宏定义
<一>、不带参数的宏定义
1、形式:#define 标识符 表达内容
eg:无脑的文本替换,只换不算(其中PI具有全局作用域)
注:末尾不加分号否则编译报错(分号也会被无脑替换(语法错误))。
2、写宏左右两边尽量加括号避免优先级高低问题;eg:
输出结果为:63
<二>、带参数的宏定义
1、一般形式:#define 宏名(形参表) 宏的内容
注:不需指定数据类型
2、eg(1):#define ADD(a,b) a+b
printf("%d\n",ADD(3,4));//3+4,运行结果为7
printf("%d\n",ADD(3,4)*5);//3+4*5,运行结果为23
eg(2):
//3 > 4 + 5(加号优先级高)所以为3 > 9,输出结果为0
eg(3):避免运算优先级高低问题:能加括号的都加括号;
输出结果为6
3、扩:(1)带参宏与函数的实现有本质区别(宏直接展开,函数要保护现场恢复现场);
(2)带参宏是以消耗大量代码区空间来提高执行效率,代码没有复用性。
4、例题:给定三角形三边求三角形面积(用宏)
(三)文件包含
1、一般形式:#include<文件名> 或 #include"文件名''
2、采用绝对路径(包含根目录信息)两种格式没有区别,eg:
#include </home/linux/1.txt>
#include "/home/linux/1.txt"
采用相对路径:
#include <1.txt>表示自动在//use/include下寻找文件
#include "1.txt" 表示在当前main.c所在的文件(在同一目录下)寻找
(四)条件编译
1、用法1: #if 0 //0为逻辑结果(非零即为真)
#endif
程序代码替换为空白;
2、用法2:#ifdef 标识符(宏)
程序段1
#else
程序段2
#endif
若宏定义过则保留程序段1,程序段2被替换为空白;若没定义过宏,则保留程序段2;
3、用法3: #ifndef 标识符(宏)
#endif
与用法2的逻辑相反
标准头文件写法
在所有头文件刚开始先加上#ifndef 宏
#define 宏
......
#endif(加在末尾)
避免对同一个头文件重复包含造成重复定义错误
4、条件编译主要用于调试,eg:此时len输出打印变量被省略
5、条件编译3 用法(标准头文件写法):其保证Clock只被定义一次
二、指针
(一)指针概念
1、指针就是用来装地址的数据类型,地址就是数据在内存单元的编号;
2、当指针为a时,指的是指针变量;当指针为0x1000时,指针指的是地址
(二)定义指针变量
1、通用语法:基类型 *指针变量名
(1)基类型:整型,浮点型,字符型,数据类型,指针类型,结构体类型,函数类型;
作用:用来表示这个指针类型指向的内存空间存放的是什么类型的数据;
(2)* 定义的时候为类型说明符,表示此时定义的是一个指针类型的变量;
(3)指针变量名,符合标识符命名的规则;
2、eg:
int i = 10; //i所在的空间是用来存放int类型的数据的
int *p = &i; //此时指针变量p指向了指针变量i(指针变量p保存了变量i的地址)
*p = 100;//通过指针变量p修改i的值(间接访问);i = 100;为直接访问i空间
&i表示获得i所在空间的首地址;表示获得了一块可以存放int类型的内存空间地址。
3、指针类型:int *p;
(int *)就是指针类型
int * p 它首先表示的是一个指针类型,然后再表示指向int型数据的指针类型。
4、指针变量的引用
int i = 10;
int *p = &i; //p指向i,因为保存了i的地址(指针变量p保存变量i的地址)
注:在64位系统下所有指针占8个字节(p指针变量地址占8个字节);
在32位系统下所有指针占4个字节。
5、注意事项:
(1)*是指针运算符,也是单目运算符(自左向右),它的运算对象只能是指针(地址);
(2) *p表示访问p所执行的基类型的内存空间;
(3) *p表达式的数据类型与定义指针变量p时出现的基地址类型一定一致。
(三)间接访问三大步骤
1、根据指针变量中的值去内存中定位;
2、从定位处开始向后偏移sizeof(基类型)个字节;
3、将偏移后的那部分内存空间当作是一个基类型变量来看。
4、直接访问与间接访问:
(四)扩展
1、指针的指针:下图红色部分为指针的指针
2、指针百分之八十的用途:在函数内部可实现在被调函数中修改主调函数
(1)指针作为函数参数:
形参—指针类型变量用来接受实参(实参是要操作的内存空间的地址);
实参—要修改谁,就把谁的地址传过去,要保证空间有效,不能是野指针(保存的地址为随机数,该部分空间不一定被允许写入),eg:
int *p;
*p = 100;
*p为野指针,编译不报错,运行程序崩溃(断错误)
注:被调函数中,一定要有*p运算,间接访问操作;
指针变量(野指针)与指针变量t指向的变量应都是确定的。
(2)值传递:只是实参数据赋值了形参
地址(指针)传递:传的是地址,可以实现被调修改主调,eg:
i的输出结果为100。
注:在创建函数定义形参的时候一定要分清楚哪些是需要值传递,哪些是需要地址(指针)传递。
(五)练习
1、三个数求和
2、求最大值与最小值
3、输出练习:
前3个表达式输出的都是i的地址(*与&可抵消);后2个表达式第一个输出i本身,第二个编译报错。
4、交换两个数的数值
下面的是错误写法:程序运行崩溃(t里是随机数)指针变量t(野指针)与指针变量t指向的变量应都是确定的