我们在学习C语言之前,首先要了解C语言所使用的代码编译器,这样我们才能熟练的使用C语言进行工作学习。
目录
-
GCC编译器
1.编译流程:
- 预处理:宏操作、引入头文件等。
该操作不会检查代码的语法错误。
gcc -E hello.c -o hello.i
- 编译:检查语法错误,将C代码转换为汇编代码。
gcc -S hello.i -o hello.s
- 汇编:将汇编代码转换位二进制的机器码
gcc -c hello.s -o hello.o
- 链接:将多个二进制机器码文件链接到一起,构成一个可执行的文件,将程序中所用到的库链接到文件中。
gcc hello.o -o a.out
2.GCC编译器的组件:
1)编译器:C转换为汇编
2)汇编器:汇编转换为二进制机器码
3)链接器:二进制机器码与库或其他需要的二进制机器码,进行链接生成可执行文件
4)C库
3.GCC编译报错:
1)语法错误:标点符号错误或者代码单词错误。
2)头文件错误:头文件缺失或头文件名字错误。
3)链接错误:引用其他文件的变量或函数错误。
4)未定义符号:变量或者函数没有定义或声明就使用。
4.运行时错误(BUG逻辑错误)
1)段错误:一般是程序非法操作内存。
2)参数错误:程序中所使用的参数没声明。
错误类型太多在实际使用中理解。。。。。。。。
- 程序的调试:
- 使用专用工具或软件调试
一般是使用GDB字符界面的调试工具
- 指针
指针的本质就是对内存的操作。
- 什么是指针:就是C语言中直接操作内存的一个工具。
一般指针有两个含义:(1)表示内存的地址。(2)表示一个存放内存地址的变量。
- 指针变量的定义和表示含义:
存储类型 数据类型 变量名;
auto int a = b; // int b=10;
auto int * p = &a; // 指针的定义与初始化
含义: 定义了一个指针变量p,该变量将会存储一个地址,该地址所在的内存是int类型
&变量名:表示取变量地址运算,其运算结果为一个常量地址。
1)指针变量的赋值:
指针变量 = 地址常量或其他指针变量。
指针变量赋值的物理意义:
通过指针变量赋值与对应的地址建立指向关系;
野指针:表示在对指针定义时,没有给定初始值,或者指针指向内存地址不可用。
空指针:指针变量的值为地址0即NULL。
2)指针的使用:(* 为指针取值运算符)
*指针变量:表示将该指针指向地址中的值取出;
例如:a=1;
Int *p=&a;
*p=1;
写一个函数,实现交换两个数
其中*p和*q就是取得对应地址中所存储得值。
同时在函数参数的使用过程中,我们应当注意,函数实际操作的是形参,只要函数的地址传参方式,才能实现函数操作实参。
- 指针的运算:
指针的关系运算: != 其左右两边只能给指针类型。
例如:P!=NULL;
表示判断这两个指针是否指向同一内存空间。
同时< > <= >= ==都是可以在指针中进行的关系运算。但是其表示的含义是判断两个指针的位置关系。
指针的算数运算:
指针只能进行有条件的算数运算。
两个指针相减:在大地址可以减去小地址。(其表示这两个指针之间所相距的距离,单位为指针所指向的类型,且这两个指针是指向内存空间连续的数组)
数组
数组的本质是一个内存空间地址且是一个元素类型的地址。(在C语言基础中我们有讲过数组的基本知识,这里我就不再讲解)。
由于数组的本质,其数组名是可以当作指针使用的,其表示是一个指向其元素类型的指针,是一个指针常量不能进行指针赋值操作。指针变量可以当数组用,数组的所有操作均可使用。
- 多级指针、指针数组、二维数组等
1.二级指针:即表示一个指针指向另一个指针,该指针中存放的是另一个指针的地址
例如:int **pp = &p; (二级指针定义)。
- 指针数组:即表示一个数组中存放的元素是指针类型的。
例如:int *arr[5];
int **p=arr; //二级指针
- 字符串指针数组:
例如:const char *arr1[3] = {"hello", "world", "你好"};
const char **pp = arr; // 二级指针
1)二维数组与数组指针: 数组指针: 一个指针指向一个数组 &数组名
int arr[5];
int* p = arr;
int arr[2][3];
int (*p)[3] = arr; // 数组指针
二维数组:
int arr[2][3];其中一维元素的类型为int[3]。
-
Const关键字:
修饰变量,表示该变量只读,在程序运行过程中,不可重新被赋值。
例如:char *p = s;
*p ; 表示*p 可读可写,p是可读可写的
const *p ; 表示*p 只读,p是可读可写的
* constp ; 表示 p 只读,*p可读可写的
const * const p; 表示 p 只读,*p也只读。
- void
- void修饰函数:则表示该函数没有返回值。
当函数没有形参时,也可使用void表示该函数不存在参数。
例如:int fun(void)
{};
void修饰指针变量:void *p;
表示该指针可以指向任意类型的地址-----万能指针。
注意:1)该指针只能使用赋值操作,其他所以的指针操作均不能进行。
2)在使用该指针时,需要进行强制类型转换,转换为对应类型的指针使用。
-
函数:
- 函数的定义:返回值类型 函数名(形参列表){函数体}
例如:
- 函数声明:返回值类型 函数名 (形参列表); // 函数原型
例如:
- 函数的五要素:
- 功能明确
- 函数原型
- 函数的作用,传参方式
- 返回值
- 注意事项即BUG部分。
- 函数的调用:函数名(实参列表);
例如:
- 传参
- 复制传参 实参复制其值并且赋值给形参,函数中操作形参(不会改变实参的值)
- 地址传参 将实参的地址赋值给形参(指针类型),函数可以通过这个指针操作实参。
例如:
-
函数中的特殊函数
- 指针函数:指一个函数的返回值是一个指针但其不能为野指针。
返回的该指针应当注意 不能指向无效或者非法的地址。
通常该指针主要指向静态区、堆区、常量区。
通过参数返回数据。
- 函数指针:是一个指针变量 指向一个函数的入口地址
函数类型:
int func(int *a,int *b);
其类型为 int (int *,int *);
如何定义函数指针 : int (*p) (int *,int *);
函数指针初始化: int (*p) (int *,int *) = func;
函数指针赋值 : p = func; 或 p = &func;
int arr[3];
int (*p)[3] =&arr;
int *p = arr;
- 函数名:
1) 代指这个函数
2) 代指这个函数 的 首地址(函数入口地址)
4. 函数指针的使用:
通过该指针调用这个函数 除此外,其他指针操作均不能使用
1) (*函数指针)(参数表)
2) 函数指针(参数表)
指针的总结
1)* & 互为逆运算
2)对指针 * 运算 就是指针指向的 对象本身
3)指针赋值 即 建立指向关系
4)指针算数运算
5)指针的关系运算
6)数组arr元素类型 *p = arr;
7)判断指针操作是否可行 是否有物理意义 是否满足运算规则。