栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

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

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

前言

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

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


文章目录

前言GCC和GDB

GCC编译器

GNU工具GCC工具所支持的后缀编译器的主要组件GCC的基本用法和选项GCC编译过程 GDB调试工具 条件编译和结构体

条件编译结构体

定义一个结构体类型的一般形式为:**定义结构体变量方法**结构体的大小结构体变量的使用形式结构体变量的初始化 结构体数组

定义初始化使用 结构体指针 共用体和typedef

共用体

定义 赋值typedef 改名 内存管理

静态存储分配动态存储分配malloc/free野指针 写在最后


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’。只有在代码处于“运行”或“暂停”状态时才能查看变量值。设置断点后程序在指定行之前停止

条件编译和结构体
条件编译

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

    根据宏是否定义,其语法如下:
    #ifdef
    ……
    #else
    ……
    #endif根据宏的值,其语法如下:
    #if
    ……
    #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是变量名 

特点:

    结构体类型是用户自行构造的。它由若干不同的基本数据类型的数据构成。它属于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) 

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

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

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

自身对齐值: 数据类型本身的对齐值,例如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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/728749.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号