1:GNU工具
编译工具:把一个源程序编译为一个可执行程序
例如: gcc编译器。
调试工具:能对执行程序进行源码或汇编级调试
例如: gdb调试器
软件工程工具:大型项目开发软件管理。
例如: make
2:gcc编译过程
4个过程:
1: 预处理: 将源文件中的头文件和宏进行替换,这个过程不进行语法检查
gcc -E xx.c -o xx.i
2:编译:将源文件编译生产汇编代码,进行语法检查,如果有误会报错。
gcc -S xx.i -o xx.s
3:汇编(重点过程): 将汇编代码生产不可执行的二进制代码
gcc -c xx.s -o xx.o
4:链接: 将所有的.o文件(printf.o) 链接生产可执行文件
gcc xx.o -o xx
常用选项:
掌握:
-o output_filename: 指定要生成的目标文件的文件名。
-g: 产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。
-O: 对程序进行优化,
-W: 尽可能多的列出警告信息。
(了解) -Idirname: 将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。
(了解) -Ldirname: 将dirname所指出的目录加入到程序函数档案库文件的目录列表中,是在链接过程中使用的参数。
3:gdb 调试
1: 生产可执行文件
gcc对main.c进行编译,注意一定要加上选项‘-g’
gcc main.c -o main -g
2:运行
gdb main
3:gdb命令
l(小写L): 列出源代码,每次列出10行。
b(break) n :在第n行设置断点
info b: 查看断点情况
r(run):运行代码
s(step):单步运行
n(next): 单步运行
p n: 查看变量n值
c :恢复运行
help [command] :帮助
q:退出。
4:指针
在计算机内部存储器(简称内存)中,每一个字节单元,都有一个编号,称为地址,也可以成为指针
指针变量: 在一个存储空间里面用来存储其他数据的地址。
符号:
“*”:
在定义变量时,用来指明变量是指针变量,用来存储指定类型数据的地址。
例如:
int *p :首先告诉os在内存中开辟一个存储空间,用来存放一个整形数的地址。
在对变量操作时,用来告诉os对变量指向的空间的值进行操作。
*p = 20;
“&”:
获取变量的地址值。
*&p《==》p :* 与& 优先级相同,右结合性
*&p <==> *(&p)
指针变量的说明
一般形式如下:
<数据类型> *<指针变量名> ;
注意: 任何类型的指针变量在32位操作系统中,大小都是四字节(因为地址编号用32位的数据表示)
数据类型: 指针变量中存放的地址是什么类型数据的地址。
char *a: 代表指针变量a中存放的是字符型数据的地址。
int *b: 代表指针变量b中存放的是int型数据的地址。
指针的运算:
指针运算的实质就是地址的计算。
指针运算的类型:算术运算,关系运算,赋值运算
--:算术运算: 元素个数的运算。
int *px = &a;
“+”: px + n:代表地址往大的方向移动n个数据。
int *py = &a2;
py - px:代表地址py和px之间int型数据的个数。
--: 表示它们指向的地址位置之间的关系
同种类型指针之间的运算有意义。
py > px: 如果py指向的地址大于px的话,结果为逻辑真。
常用: != 和 ==
py == NULL: 用于判断py是否指向0空间。
py != NULL: 用于判断py是否不指向0空间。
--:指针赋值运算
“=”
py = NULL: 将指针变量py的值设置为NULL(0)。
5:一维数组的数组名代表一维数组的指针(起始地址)
数组名是一个常量地址。
int a[4]
a[i] <==> *(a + 1)
int *p = a;
p[i] <==> a[i] <==> *(a+i)<==> *(p+1)
6:C语言通过使用char数据类型的数组来处理字符串
在C编程中,当一个字符指针指向一个字符串常量时,不能修改指针指向的对象的值。
char *p = “Hello World”;
*p = ‘h’;
7:指针数组。(整型数组)
指针数组是指由若干个具有相同存储类型和数据类型的指针变量构成的集合。
指针变量数组的一般说明形式:
<存储类型> <数据类型> *<指针变量数组名>[<大小>];
int *p[3]: p是数组,这个数组里的每一个元素存放的都是指向整型数的指针。
8:数组指针
是一个指针变量,变量里面存储一个数组类型的地址。
int (*p)[3]: p里面存储一维数组int [3]类型的首地址。
9:多级指针
二级指针变量指向一级指针。一级指针变量指向普通数据。
二级指针变量的说明形式如下:
<存储类型> <数据类型> ** <指针名> ;
10:const
用const修饰的变量是只读型变量。
const int a;
修饰指针变量时
1: const <数据类型> *<指针变量名称>;
const int *p ; *p是只读型的。不能够通过指针改变其对象的值
2:常量化指针变量
<数据类型> *const <指针变量名称>= <指针运算表达式>;使得<指针变量>的值不能修改(即只
能指向同一个对象)
int *const p = &a; p的值不能通过p = &c这种形式来修改
11:void
1:void型的指针变量是一种指向不确定数据类型的指针变量。它可以通过强制类型转换让该变量指向任何数据类型的对象。
2:当函数没有返回值时,用void修饰函数。
12:函数
函数是一个完成特定功能的代码模块
一般形式如下:
<数据类型> <函数名称>( <形式参数说明> )
{
语句序列;
return (<表达式>);
}
其中:
<函数名称>是一个标识符,要求符合标识符的命名规则;
<数据类型>是整个函数的返回值类型,如无返回值应该写为void型;
<形式参数说明>是逗号”,”分隔的多个变量的说明形式,通常简称为形参;
大括弧对 {<语句序列> },称为函数体;
<语句序列>是大于等于零个语句构成的
例如: int add( int x , int y ) //int ;说明此函数有返回值,返回值类型为int型, add:函数名,代表函数的入口地址。 int x, int y: 形式参数
{
功能代码
}
注意: 函数如果在main函数或会调用到此函的的函数之后实现的话要在调用它的函数之前进行函数声明。
13:函数传参
1: 值传递
2: 地址传递
3: 函数参数时数组,
double TestArray(double b[],int Size); 《==》 double TestArray(double *b,int Size);
14:指针函数,返回值是地址(指针)
指针函数是指一个返回地址量的函数
指针函数的定义的一般形式如下:
<数据类型> *<函数名称>(<参数说明>)
{
语句序列;
}
15:函数指针: 函数指针是用来存放函数地址的指针
函数地址是一个函数的入口地址。
函数指针变量说明的一般形式如下:
<数据类型> (*<函数指针名称>)(<参数说明列表>);
int (*) (int a, intb) : 函数指针类型, 这种变量里面存放函数的地址,这个函数返回值为int, 形式参数为(int a , int b)
定义函数指针变量:
int (*p) (int a ,int b): 变量p是指针变量用来存放函数地址。
函数指针数组
函数指针数组
定义形式如下:
<数据类型> ( * <函数指针数组名称> [<大小>] ) ( <参数说明列表> );
函数名代表函数的入口地址。
调用函数时只要知道了函数入口地址就可以执行函数。
16:递归函数
当函数指针作为一个函数的形式参数时,这个函数就叫做回调函数。
递归函数是指一个函数的函数体中直接调用或间接调用了该函数自身的函数。
递归函数要有一个结束条件。
本质:将大问题变成小问题,
递归函数调用的执行过程分为两个阶段:
递推阶段:从原问题出发,按递归公式递推从未知到已知,最终达到递归终止条件。
回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解。