【从零开始的嵌入式生活】LinuxC语言4——LinuxC高级

请添加图片描述

前言

今天是2021年的第六天,本来打算把makefile也更新的,结果没来及。下次一定!
三连即可提高学习效率0.0

🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
全文大约阅读时间: 60min



GCC和GDB

GCC编译器

GNU工具

  • 编译工具:把一个源程序编译为一个可执行程序
  • 调试工具:能对执行程序进行源码或汇编级调试
  • 软件工程工具:用于协助多人开发或大型软件项目的管理,如make、CVS、Subvision
  • 其他工具:用于把多个目标文件链接成可执行文件的链接器,或者用作格式转换的工具。

GCC工具

  • 全称为GNU CC ,GNU项目中符合ANSI C标准的编译系统
  • 编译如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多种语言
  • GCC是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%
  • 一个交叉平台编译器 ,适合在嵌入式领域的开发编译

所支持的后缀

  • .c C原始程序
  • .C/.cc/.cxx C++原始程序
  • .m Objective-C原始程序
  • .i 已经过预处理的C原始程序
  • .ii 已经过预处理的C++原始程序
  • .s/.S 汇编语言原始程序
  • .h 预处理文件(头文件)
  • .o 目标文件
  • .a/.so 编译后的库文件

编译器的主要组件

  • 分析器: 分析器将源语言程序代码转换为汇编语言。因为要从一种格式转换为另一种格式(C到汇编),所以分析器需要知道目标机器的汇编语言。
  • 汇编器: 汇编器将汇编语言代码转换为CPU可以执行字节码。
  • 链接器: 链接器将汇编器生成的单独的目标文件组合成可执行的应用程序。链接器需要知道这种目标格式以便工作。
  • 标准C库: 核心的C函数都有一个主要的C库来提供。如果在应用程序中用到了C库中的函数,这个库就会通过链接器和源代码连接来生成最终的可执行程序。

GCC的基本用法和选项

Gcc最基本的用法是∶gcc [options] [filenames]

  • -c,只编译,不连接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。
  • -o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
  • -g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。
  • -O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些。
  • -O2,比-O更好的优化编译、连接,当然整个编译、连接过程会更慢。
  • -I dirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。
  • -L dirname,将dirname所指出的目录加入到程序函数档案库文件的目录列表中,是在链接过程中使用的参数。

GCC编译过程

请添加图片描述


GDB调试工具

首先使用gcc对test.c进行编译,注意一定要加上选项‘-g’

gcc -g test.c -o test
gdb test 

调试信息:

命令功能
(gdb) l查看文件
(gdb) b 6设置断点
(gdb) info 6查看断点
(gdb) r运行代码
(gdb) p n查看变量值
(gdb) n 、(gdb)s单步运行
(gdb) c恢复运行(断点继续)
(gdb) help [command]帮助
(gdb) set args设置传入参数

注意的点:

  • 在gcc编译选项中一定要加入‘-g’。
  • 只有在代码处于“运行”或“暂停”状态时才能查看变量值。
  • 设置断点后程序在指定行之前停止

条件编译和结构体

条件编译

编译器根据条件的真假决定是否编译相关的代码**(非常常用)**
常见的条件编译有两种方法:

  1. 根据宏是否定义,其语法如下:
    #ifdef <macro>
    ……
    #else
    ……
    #endif
  2. 根据宏的值,其语法如下:
    #if <macro>
    ……
    #else
    ……
    #endif

结构体

在实际的处理对象中,有许多信息是由多个不同类型的数据组合在一起进行描述,而且这些不同类型的数据是互相联系组成了一个有机的整体。此时,就要用到一种新的构造类型数据——结构体(structure),简称结构。

定义一个结构体类型的一般形式为:

struct  结构体名
{
	数据类型   成员名1;
	数据类型   成员名2...
	数据类型   成员名n;
}

举个例子:
定义一个职工worker结构体如下:

struct worker
{   
	long number;
	char name[20];
	char sex;		
	int age;          //  age是成员名
	float salary;
	char address[80];
};      			//注意分号不能省略
int  age = 10;    //age是变量名 

特点:

  1. 结构体类型是用户自行构造的。
  2. 它由若干不同的基本数据类型的数据构成。
  3. 它属于C语言的一种数据类型,与整型、实型相当。因此,定义它时不分配空间,只有用它定义变量时才分配空间。

定义结构体变量方法

1.先定义结构体类型再定义变量名
这是C语言中定义结构体类型变量最常见的方式

struct 结构体名
{
            成员列表;
}struct 结构体名 变量名;

举个例子:

truct worker
{
	long number;
	char name[20];
	char sex;
	int age;
	float salary;
	char address[80];
	char phone[20];
};   
struct worker worker1,worker2; 

2.在定义类型的同时定义变量

struct 结构体名
{
	成员列表;
}变量名;

3.直接定义结构类型变量
其一般形式为:

	 	struct      //没有结构体名
	 {
      成员列表;
	  }变量名

结构体的大小

使用sizeof来求

sizeof(struct worker)
sizeof(worker1) 

补充一个知识点,就是结构体的大小求解中的两个原则:

  1. 不但结构体的成员有有效对齐值,结构体本身也有对齐值,这主要是考虑结构体的数组,对于结构体或者类,要将其补齐为其有效对齐值的整数倍。结构体的有效对齐值是其最大数据成员的自身对齐值;
  2. 存放成员的起始地址必须是该成员有效对齐值的整数倍。

对于每个对齐值有如下定义:

  • 自身对齐值: 数据类型本身的对齐值,例如char类型的自身对齐值是1,short类型是2;
  • 指定对齐值: 编译器或程序员指定的对齐值,32位单片机的指定对齐值默认是4;(整个结构体的长度需要是默认对齐的整数倍)
  • 有效对齐值: 自身对齐值和指定对齐值中较小的那个。

举个例子:

struct ep{
	char a;
	int b;
	char c;
	char d;
};

首先a在第一个字节,而b因为自身长度为4,所以其实地址在低四个字节,占据四个字节此时长度为8个字节,两个char再占用两个字节,所以数据域长度为10个字节,由于ep自身的对齐值为4,所以最终应该为12个字节。


结构体变量的使用形式

结构体变量的成员用一般形式表示:结构体变量名.成员名
例如:worker1.number;worker1.name;worker1.sex;
在定义了结构体变量后,就可以用不同的赋值方法对结构体变量的每个成员赋值。例如:

		strcpy(worker1.name,”Zhang San”);
		worker1.age=26;
		strcpy(worker1.phone,1234567);
		worker1.sex=’m’;

结构体变量的初始化

struct 结构体名 变量名={初始数据表};
或者:

struct  结构体名
{
	成员列表;
}变量名={初始数据表}

结构体数组

具有相同结构体类型的结构体变量也可以组成数组,称它们为结构体数组。结构体数组的每一个数组元素都是结构体类型的数据,它们都分别包括各个成员(分量)项。

定义

定义结构体数组的方法和定义结构体变量的方法相仿,只需说明其为数组即可。与上面的结构体的定义类似。
举个例子

//第一种
struct 
{
 char name[20];
 char sex;
 int age;
 char addr[20];
}stu[3]; 
//第二种
struct student
{
 char name[20];
 char sex;
 int age;
 char addr[20];
}stu[3]; 
//第三种
struct student
{
 char name[20];
 char sex;
 int age;
 char addr[20];
};
struct student stu[3]; 

初始化

struct 结构体名
			{
  			 成员列表;
			};

struct 结构体名 数组名[元素个数]={初始数据表};


使用

1.引用某一元素中的成员。
stu[1].name
2.一个元素赋给另外一个

student1=stu[0];
stu[0]=stu[1];
stu[1]=student1; 

注意:字符串类型的不能直接等于赋值!!但是结构体可以直接赋值,虽然里面有字符串类型。


结构体指针

定义:
struct 结构体名 *结构指针名;
访问元素:

(*p).name 
p->name

两者是完全等价的。

共用体和typedef

共用体

不同数据类型的数据可以使用共同的存储区域,这种数据构造类型称为共用体,简称共用,又称联合体(union)。


定义

union 共用体名
{
	成员表列;
}

例如

union gy
{
	int i;
	char c;
	float f;
};

因为每个成员长度是不一样的,从内存的角度占用的空间是所有元素中最大的。


赋值

a.i = 1;
a.c = 'a';
a.f = 1.5;

因为内存区域是公用的关系,在保存结束a.f之后,其他的都已经没有意义,因为相关的内存的区域会被覆盖掉,数据就会发生变化。


在程序中经常使用结构体与共用体相互嵌套的形式。

struct datas
{
  char *ps;
  int type;
  union
  {
    float fdata;
    int idata;
    char cdata;
   }udata;
};

typedef 改名

在C语言中,允许使用关键字typedef定义新的数据类型
其语法如下:

		typedef   <已有数据类型>   <新数据类型>;

例如:

typedef   int  INTEGER;

此时:INTEGER i; 等价于 int i;


在C语言中经常在定义结构体类型的时使用typedef

typedef   struct  _node_
{
    int  data;
    struct _node_  *next;
} listnode, *linklist;

这里定义了两个新的数据类型listnode和linklist。其中listnode
等价于数据类型struct node 而 linklist等价于struct node *

内存管理

C/C++定义了4个内存区间:
代码区、全局变量与静态变量区、局部变量区即栈区、动态存储区,即堆区


静态存储分配

通常定义变量,编译器在编译时都可以根据该变量的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。
在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。


动态存储分配

  • 有些操作对象只有在程序运行时才能确定,这样编译器在编译时就无法为他们预定存储空间,只能在程序运行时,系统根据运行时的要求进行内存分配,这种方法称为。
  • 所有动态存储分配都在堆区中进行。
  • 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc申请任意多少的内存,程序员自己负责在何时用free释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

malloc/free

void * malloc(size_t num)
void   free(void *p)
  • malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。
  • malloc申请到的是一块连续的内存,有时可能会比所申请的空间大。其有时会申请不到内存,返回NULL。
  • malloc返回值的类型是void *,所以在调用malloc时要显式地进行类型转换,将void * 转换成所需要的指针类型。
  • 如果free的参数是NULL的话,没有任何效果。
  • 释放一块内存中的一部分是不被允许的。

注意事项:

  • 删除一个指针p(free§;),实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身,释放堆空间后,p成了空悬指针,建议将指针置为NULL
  • 动态分配失败。返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。
  • malloc与free是配对使用的, free只能释放堆空间。如果malloc返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏, 同一空间重复释放也是危险的,因为该空间可能已另分配, 所以必须妥善保存malloc返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。

野指针

是NULL指针,是指向“垃圾”内存的指针。“野指针”是很危险的。
“野指针”的成因主要有三种:

  • 指针变量没有被初始化。
  • 指针p被free之后,没有置为NULL,让人误以为p是个合法的指针。
  • 指针操作超越了变量的作用范围。(这种通常被人们忽略)

写在最后

今天主要讲解了C语言的补充知识,有些东西非常常用,在接下来的数据结构会更加常用到,大家学废了么?最后三连即可提高学习效率!!!


另外我在更新的就是算法笔记的一些例题笔记,这个系列是用于提高我的算法能力,如果有兴趣对算法领域感兴趣找不到合适的入门文章也可以追更,如果我更新的太慢了请大家点赞收藏,一键三连才能更有更新的动力呀0.0

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XingleiGao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值