一、Linux C环境
(一)GCC编译工具
- GCC编译工具:是一个将C源码工程编译为可执行程序的一个工具。
- GCC编译流程:
- 预处理:是将c文件中的头文件、宏定义等进行替换。预处理不会报错。
//将C文件进行预处理,生成.i后缀文件
gcc -E C文件名
- 编译(分析器):是将预处理过后的文件(.i后缀文件)编译为汇编文件(.s后缀文件);会进行语法检测,会报语法错误。
//将.i后缀文件编译成一个汇编文件(.s后缀文件)
gcc -S 预处理文件名
- 汇编(汇编器):是将汇编文件(.s后缀文件)扁你一为二进制目标文件(.o后缀文件)
//将.s文件扁你一为二进制目标文件(.o后缀文件)
gcc -c 文件名
//或者使用as汇编器
as
- 链接(链接器):将多个目标文件或依赖库进行链接,生成可执行目标文件。报链接错误。
//
gcc 二进制目标文件(.o后缀文件)-o 可执行文件名
3.编译出错:不能生成可执行程序。
- C语法错误
- 头文件错误
- 档案库错误(链接错误)
- 未定义引用错误
- 逻辑错误:编译通过,可生成逻辑程序。
- 段错误:非法操作内存,数组溢出,操作越界,指针的非法访问……
- 输入输出格式错误
(二)GDB调试
- 编译时添加-g选项,附加调试信息。
- 进入调试模式:gdb ./可执行文件名
- GDB使用方法:
- (gdb) l 行号:查看源码对应行位置
- (gdb) b 行号:设置断点,程序运行到该位置时停止。
- 查看断点情况:(gdb) info b
- 删除断点:del 断点数字
- (gdb) r:运行代码
- (gdb) n:运行一行
- (gdb) s:若这一行时函数调用
- (gdb) c:恢复程序运行
- 栈回溯命令:backtrace 专门处理段错误问题
- (gdb) quit:退出gdb调试工具。
- 逻辑错误的调试:常用打印法调试。
二、存储类型
(一)五类存储类型
- 自动(auto):
- 全局变量:全局区/静态区,全局的作用域,有外部链接,有全局生命周期。
- 局部变量:栈区,局部变量的作用域,空链接,有局部生命周期。
- 寄存器(register):
- 不是强制的,是一种事情,该类型变量不能取地址操作,一般使用较少。
- 外部变量(extern):
- 修饰全局变量,表示这个变量不是本文件中定义的,是引入其他文件的。
- 全局的作用域:由外部链接,有全局生命周期。
- 静态变量(static):
- 修饰全局变量,作用域受限的全局变量。作用域只能在当前文件中使用和访问,不能在其他文件访问,不能被extern。
- static修饰函数,表示该函数只能在当前文件中被调用,不能被其他文件调用。
- static修饰局部变量,扩展其生命周期,表示该局部变量存储位置变更为静态区。局部的作用域空链接、有全局生命周期。
- const:修饰变量,限制变量 访问,可读不可写。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CSu8QEbf-1655466691896)(F:\华清嵌入式\嵌入式学习\C语言\Linux基础与C高级\Linux基础\Linux基础Day04\存储类型.jpg)]
三、指针
- 指针:指指针变量,一个变量(有存储空间),存放的是一个内存地址;也指内存地址。
- C语言的灵魂:指针操作内存的工具,灵活性非常高。
- 指针变量的定义:
- 定义格式:
存储类型 数据类型* 变量名 = 初始值
示例:
int *p;//野指针,指针变量中存放的是一个随机地址或无效地址
int *p = NULL;//空指针,只能进行赋值操作,取地址操作
- 二级整型指针:指指针类型变量,存放的是另一指针变量的地址。“int * *pp;”。
- 指针的赋值:本质是建立指向关系。指针变量 = 指针类型地址
- 指针类型地址:可以通过对应类型的变量取地址操作进行得到;
- 指针操作:“*”运算。单目运算符,位于指针的左边,本质是访问指针指向的对象。对*p的操作等同于对其指向的对象的操作。
- 指针变量的存储大小:64位机:其存放地址的空间需要8字节;32位机
- 64位机:其存放地址的空间需要8字节
- 32位机:其存放地址的空间需要4字节
- 与指针指向类型无关
- 指针的算术运算:
- 指针与整数的运算:只能+、-运算,本质是指向关系发生移动,其移动单位不是字节,而是指向的对象。不能进行*、/、%运算。
int a;
int *p = &a;
printf("&a=%p\n",&a);
printf("p=%p\n",&p);
p += 1;//其结果是a的内存地址+1字节
printf("p=%p\n",&p);
- 指针与指针可以进行“-”操作,其结果是一个整型,其物理意义是计算来给你个指针间相距多远,单位以对线计,但是这两个的指针的类型必须一直,且这两个指针需要指向同一段连续的内存空间。
练习:使用指针方式求一个字符串的长度
//使用指针方式求一个字符串的长度
#include <stdio.h>
int main()
{
char str1[] = "hello world!";
char *p = str1;
int lenth = 0;
while(*p++ != '\0')
{
lenth++;
}
printf("lenth = %d\n",lenth);
return 0;
}
- 指针的关系运算:两个指针间可以进行关系运算,其运算本质是判定这两个指针的位置关系,这两个指针需要在同一连续内存中,且类型一致。
- const修饰指针变量:
const char *str = "hello";//常量字符串指针
const char *p = "OK";//常量字符串指针
*p = 'h';//指针指向的内容不能修改
p = str;//指针指向的关系可以修改
const char * const p1 = str;//指针指向关系不能修改,指向的内容也不能修改
char * const p2 = str;//指针指向的关系不能修改,指向的内容可以修改。
- void关键字:
- void修饰函数:修饰函数表示该函数没有返回值。
- void修饰指针变量,万能指针,表示该指针暂时可以指向任意类型但是不能直接使用,*、++、<、>……使用时需强制类型转换。
四、指针与数组
(一)指针数组与二级指针
- 字符串指针数组:数组里面存放的是字符串的地址。
- 二级指针:指针指向的对象也是指针,该类指针称为二级指针。
char s1[] = “are you ok”;
char s2[] = “你好”;
char s3[] = “兽人必须死”;
char s4[] = “上海”;//字符串数组
char *arr[5]={s1,s2,s4,s3,NULL};
//二级指针
char * * p = arr;
(二)数组指针
- 数组指针:数组指针指向的对象是二维数组的首元素地址。
int arr[3][4]; arr => int [4];
int (*p)[4] = arr; // 数组指针 一个指向数组首元素的 指针
练习:
char *arr[5]={“hello”,“world”,“are you ok”,“ok”,NULL}
char * *p = arr;
求表达式的值:假设每个表达式中p = arr;
*p; *p==>*arr ==>“hello”
**p; **p==>*(*arr )>*(“hello”)>‘h’
*(p+1); *(p+1)>*(arr+1)>*(“world”)==>“world”
*(*p+1); *(*p+1)>*(*arr+1)>*(“hello”+1)>*(“ello”)>‘e’
*(*(p+1)+1); *(*(p+1)+1)>*(*(arr[1])+1)>*(“world”+1)>*(“orld”)>‘o’
**p++;>**arr>*(“hello”)==>‘h’
*(*p)++;>*(“hello”)++>‘h’