GCC :
1、gcc 组件
(1)分析器:分析语法结构,将C语言编译汇编语言。.s
(2)汇编器:将汇编代码编译成二进制文件
(3)链接器:链接目标文件以及库文件,生成可执行代码。
(4)标准C库:提供核心的C库函数。scanf printf
2、gcc编译过程:
(1)预处理: 宏替换和头文件展开.c - > .i 。
gcc -E test.c -o test.i
(2)编译: 将经过预处理后的文件.i,编译成汇编文件.s
gcc -S test.i -o test.s
(3)汇编: 将汇编文件.s 编译成目标文件.o 二进制码
gcc -c test.s -o test.o
(4)链接: 将一个或多个目标文件,及库链接成为可执行二进制文件
gcc test.o -o a.out
gcc -g test.c 生成可执行文件,同时附加调试信息,以便于GDB工具使用
gcc -O 编译时需要使用编译优化。
-O1 O2
gcc -I头文件路径 指定头文件的搜索路径
gcc -l第三方库名 指定链接的其他库
第一类∶标点符号,符号错误
第二类∶头文件错误
第三类∶档案库错误
第四类∶未定义符号
GDB :调试工具
1、调试的程序,编译时必须加 -g 选项
gcc -g C文件
2、gdb 程序名
gdb ./a.out
GDB调试模式:
1、退出 q
2、l 查看源码
3、b 行号 设置断点
4、info b 查看设置 的断点
5、del 断点号 删除断点
6、r 全速运行
7、n 单步执行
8、p 变量名 查看变量的值
9、c 恢复运行 全速运行到下一断点或程序结束
栈回溯命令 backtrace
用于定位段错误位置。
打印法调试。
变量:
对存储空间的抽象,内存空间命名
数据类型:int char double float
本质:空间的大小,空间使用的方式
存储方式:
全局变量 : 整个文件,及文件外都可以使用: 作用域
从程序开始到结束,变量空间一直有效: 生命周期
存放位置 静态区
局部变量 : 当前代码块 {} 作用域
代码块开始到该块结束 生命周期
存放位置 栈区
静态变量 :
static 修饰的局部变量: 静态局部变量
当前代码块 {} 作用域
从程序开始到结束,变量空间一直有效: 生命周期
存放位置 静态区
在静态区的变量。只能被初始化一次。
static 修饰全局变量 : 静态全局变量
整个文件,限制在文件外的使用: 作用域
从程序开始到结束,变量空间一直有效: 生命周期
存放位置 静态区
static 函数: 静态函数:限制函数的作用域
常量
const 修饰的变量
寄存器变量
register 修饰的变量
指针:
什么是指针变量: 一个4字节空间,存放的是内存地址。 32位 - 4字节 64位 - 8字节
指针变量的定义:
int *p;
指针指向的数据类型 * 指针变量名;
指针的赋值:指针的类型 必须类型一致才可以赋值 p=&a;
将某个空间的地址写入到指针变量的空间
物理意义:给指针变量建立指向关系。
野指针,空悬指针:没有建立指向关系的指针;
空指针:一个指向0地址的指针,该指针不能进行除赋值以及关系运算以外的任何操作。
指针的使用: 前提:1、指针必须建立指向关系;
1、取指针指向的内容;
(* 指针变量名); 代指指向的内容
int a[10]; int * p = a; p -> a[0]
p-> a; p 数组指针 int (*p)[]; a[][]; p = a; p -> a[0]
int **p a[][]; p - > a[0]; (*p) ++;
int **p int *a[]; p -> a[0]
2、指针运算 ,必须满足物理意义。
(1)算数运算: +1 -1 ++ -- +n
本质:地址的移动;
对p移动的字节单位 = n * 数据类型(*p)字节单位
指针进行算数运算后,仍然是一个指针;
int *p;
p=p+1; p+1; p++;
*p++; *(p+1); *p +1;
一级指针和一维数组:
int a[10];
int *p =a ;
a:数组名 1、代指这个数组。 2、代指数组首地址
数组名 不能赋值
p++; (*P)
int **p; int *p[3]; int (*p)[3];
sizeof(); 计算类型的字节大小 sizeof(p) 8 sizeof(*p) 24
参数:1、数据类型
2、变量名,指,计算该变量的类型的大小。
指针与指针的算数运算: 1、只能进行减法操作 2、必须大-小 3、两指针的类型必须一致
计算的结果 = 两指针间相距的类型个数。
指针的关系运算: < > == <= >= !=
本质:指针的位置关系。两指针的类型必须一致
字符数组和字符串;
const 修饰指针:
1、 const int *p;
const 修饰* ,表示 *p 只读,不能写。 p 指针变量本身,不受此限制。
2、 int * const p;
const 修饰p, 表示 p 只读,不能赋值。 *p 不受此限制。
3、 const int * const p;
都不能修改,全部只读;
void 修饰指针;
void *p; 万能指针
表示,先定义这样一个指针容器,指向的内容的类型不明确。
一个地址,地址指向的内容类型不明确。
1、指针实现 strcpy(); 字符串拷贝
char *strcpy(char *dest, const char *src);
2、指针实现 字符串拼接
char *strcat(char *dest, const char *src);
3、字符串倒序
char *str(char *a);
指针数组:和二级指针
本质:一个数组,里面的元素是指针。
定义:指针数据类型* pa[10];
int *p[10];
二级指针变量:
本质:地址空间,存放地址,该地址指向一个指针变量。
定义:数据类型 **指针变量名;
数组指针的数组名 与二级指针可以直接赋值,其类型与表示的物理意义完全一致。
例:
int *a[3];
int **p = a;
其中对 a ,不能进行赋值操作,因为a是一个数组。
对 p 可以进行所有的指针操作
(*p) 是数组a中的一个元素,该元素是一个指针类型,
因此,可以对(*p) 进行指针的所有操作
但,注意,在(*p)进行赋值操作后,指向的新位置,
要确认是否有权限进行*操作。
异或方式交换两个数;
a = 1111 0101 = 0x f5 1111 0000
b = 0011 1100 = 0x 3c 1111 0000 a b
a = a ^ b
a = 1100 1001 0000 0000 = '\0'
b = 0011 1100 0000 0000
b = a ^ b
b = 1111 0101 0000 0000
a = 1100 1001 0000 0000
a = a ^ b
a = 0011 1100
b = 1111 0101
数组指针:与二维数组
数组指针:
本质是一个指针,指针指向一个数组
定义: 数组指针
int (*p)[n];
元素类型 (* 指针名 )[数组的元素个数];
二维数组:
数组名 1、代指这个数组。 2、代指数组首地址
二维数组的数组名 可以使用一个数组指针来存放;
例:
int a[3][4];
int (*p)[4] = a;
p++; 1 * a[0] 4*4
a[1]
对p 可以使用指针的所有操作,也可以当二维数组来使用
(*p) 代指指向的数组,所以,不能进行赋值操作
总结: 指针变量,
指针和数组 (1) 指针数组 (2)数组指针
函数:
(1)、函数的定义
返回值类型 函数名(形参列表) { 函数体 }
指针函数: 返回值是指针类型的函数;
(2)、函数的声明
返回值类型 函数名(形参列表);
(3)、函数的调用
函数名 (实参列表);
(4)、参数的传递:
1、复制实参传递
int func(int a,int b)
func(x,y);
2、实参地址方式传递
int func(int *a,int *b)
func(&x,&y);
(5)、函数指针
本质:一个指针,指向一个函数。
函数名 :1、代指这个函数 2、函数代码块的首地址
函数指针的定义:
返回值类型 (* 指针变量名) (形参列表) ;
函数指针操作: 1、赋值,建立指向关系 2、取指向的函数
p = 函数名。 p 指向函数
void func1( void (*fp)(void) , char a ); 回调函数
数组 函数 指针
函数指针数组:
本质是数组,数组元素是指针,指针指向函数; void (int)
定义: void (*fpa[])(int);
递归函数:
自己调用自己,每次调用缩小规模,到达临界值,确定的临界值,依次返回;
本质: 对栈区内存的使用;
求 1+2 .... n; 和?
使用递归求解。
int sum(int n)
{
if(n == 1) return 1;
if(n > 1) return n+ sum(n-1);
}
n (返回值)
int sum(int n) an+a(n-1)+sum(n-2) .... sum(n-n+1) + 1 临界值
1、n!
2、 1 2 3 5 8 13 ..... 斐波那契数列
n 1 2 3 4 5 6 n n = 20
an = a(n-1) + a(n-2);
结构体:
结构体:是一种构造数据类型。 基本数据类型 int char *
由一个或多个基本数据类型构造而成的数据类型
定义一个结构体类型:
struct 结构类型名
{
成员1;
成员2;
..
};
定义一个结构体类型变量:
struct 结构类型名
{
成员1;
成员2;
..
}结构体变量名;
给类型取别名:
typedef 原类型名 别名;
初始化:
struct 结构类型名
{
成员1;
成员2;
..
}结构体变量名 = {成员1的值,成员2的值,,....};
使用:
结构体不能整体使用,必须进入结构体,使用成员。
使用方法:
结构体名.成员
当两个结构体类型完全一致的变量,直接赋值;
结构体的数据存放:
1、按成员顺序依次存放。
2、满足字节对齐要求。 编译器决定
开辟结构体空间时,最小的开辟单位,4字节对齐。 2字节对齐 单字节对齐。
空间使用,遵循紧凑原则。
结构体指针:
本质:是一个指针,指向结构体;
定义: 结构体数据类型 *结构体指针变量名;
struct st *stp = &st1;
使用: 引用结构体成员
方式1: (*stp).成员;
方式2: 结构体指针专用。
stp -> 成员;
结构体数组:
本质:是数组,数组元素是结构体。
定义结构体数组:
struct st sta[3];
结构体指针数组:
本质:是一个数组,元素是结构体指针。
定义:
struct st *stpa[3];
使用:
(stpa[1])->成员;
结构体数组指针:
本质:是一个指针,指向数组,数组元素是结构体。
定义:
struct st (*stap)[4];
((*stap)[0]).成员
数组 结构体 函数 指针;
结构体中有特殊的成员;
结构体指针;
结构体指针数组;
共用体:
跟结构体类似,存储空间共用。空间的大小由最大的成员决定。
定义共用体类型:
union 共用体类型名
{
成员1;
成员2;
...
};
共用体使用:
跟结构体完全一致;
字节序:
对于多字节数据,超过一个字节的基本数据类型。 int short double float
大端:数据的高位存放在内存的高地址,低位在低地址 PC x86架构 amd CPU
小端:数据的高位存放在内存的低地址,低位在高地址 ARM 架构
有CPU的架构决定。
网络编程。
堆区动态内存分配:
堆的特性:1、需要手动开辟和释放空间。
2、堆区内存:
生命周期:从你申请开始到释放或程序结束而结束。
作用域: 得到指针的范围。
使用:
1、必须先手动申请才能使用。
申请:
头文件:#include <stdlib.h>
void *malloc(size_t size);
函数5要素:
1、作用:申请堆区内存。
2、函数原型:
3、参数:
size:表示要申请的堆区内存的大小,字节。
4、返回值:
void *:万能指针,成功,返回一个指针,指向一片堆区空间。
失败: NULL
5、注意事项;
2释放空间:
void free(void *ptr);
ptr = NULL;
ptr:需要释放的堆区空间指针。
注意事项:
1、不能重复释放
2、不能释放一部分。
内存泄漏:
原因:申请的空间的指针丢失。
结构体指针数组[100];
动态内存:
存放学生详细信息。
struct st* add(char *name,int ID,int high,float weight)
{
malloc
}
makefile 工程管理器:
规则文件:描述编译过程。 makefile Makefile
格式:
目标:依赖
由依赖生成目标的规则
伪目标:
不需要依赖的目标
变量:
变量定义的两种方式 自定义变量:
1、递归展开方式VAR=var
2、简单方式 VAR:=var
3、VAR?=var 变量若存在则不做任何操作。
变量名+= 值 表示在变量的末尾追加值
自动变量:
$* 不包含扩展名的目标文件名称
$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件
$< 第一个依赖文件的名称
$? 所有时间戳比目标文件晚的的依赖文件,并以空格分开
$@ 目标文件的完整名称
$^ 所有不重复的目标依赖文件,以空格分开
$% 如果目标是归档成员,则该变量表示目标的归档成员名称
make -f file 读入当前目录下的file文件作为Makefile