1. gcc和gdb的用法
GNU工具:
编译工具:把一个源程序编译为一个可执行程序
调试工具:能对执行程序 进行源码或汇编调试
软件工程工具:用于协助多人开发或大型软件项目的管理,如make、CVS、Subvision
其他工具:用于把多个目标文件链接成可执行文件的链接器,或用做格式转换的工具。
GCC:
全称为GNU CC,GNU项目中符合ANSI C标准的编译系统
编译如C,C++,Object C,Java,Fortran,Pascal,Modula-3,Ada等多种语言
GCC是可以在多种硬件平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%
一个交叉平台编译器,适合在嵌入式领域的开发编译
gcc所支持后缀名解释:
- .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 [选项] [文件名]
-c,只编译,不连接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件
-o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
-g,产生符号调试工具(GNU的gdb)所必要的符号资讯,想要对源代码进行调试,我们就必须加入这个选项。
-O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些
-O2,比-O更好地优化编译、连接,当然整个编译、连接过程会更慢
-I dirname,将dirname所指出地目录加入到程序头文件目录列表中,是在预编译过程中使用地参数。
-L dirname,将dirname所指出地目录加入到程序函数档案库文件的目录列表中,是在链接过程中使用的参数
GCC的编译流程分为四个步骤:
预处理
编译
汇编
链接
C语言编译流程
调试器-GDB调试流程
首先使用gcc对文件进行编译,一定要加上选项‘-g’
#gcc -g demo1.c -o demo
#gdb demo
调试流程
查看文件 - (gdb) l
设置断点 - (gdb) b 6
删除断点 - (gdb) del num
查看断点情况 - (gdb) info b
运行代码 - (gdb) r
查看变量值 - (gdb) p n
单步运行 - (gdb) n
- (gdb) s
恢复程序运行 - (gdb) c
main函数设置参数 - (gdb) set args [参数]...
帮助 - (gdb) help [command]
2. 条件编译和结构体
条件编译:
编译器根据条件的真假决定是否编译相关的代码,常见的条件编译有两种方法:
根据宏是否定义:
#ifdef <macro>
....
#else
....
#endif
根据宏的值:
#if <macro>
....
#else
....
#endif
结构体:
在实际的处理对象中,有许多信息是由多个不同类型的数据组合在一起进行描述,而且这些不同类型的数据是互相联系组成了一个有机的整体。这就要用到一种新的构造类型数据——结构体,简称结构。
结构体的使用为处理复杂的数据结构(如动态数据结构等)提供了有效的手段,而且为函数间传递不同类型的数据提供了方便。
结构体是用户自定义的新数据类型,在结构体中可以包含若干个不同数据类型和不同意义的数据项。
结构体的定义:
struct 结构体名
{
数据类型 成员名1;
数据类型 成员名2;
...
数据类型 成员名n;
};
结构体变量的定义:
先定义域结构体,再定义变量名
struct 结构体名
{
成员列表;
};
struct 结构体名 变量名
在定义类型的同时定义变量:
struct 结构体名
{
成员列表;
}变量名;
直接定义结构体变量
struct //没有结构体名
{
成员列表;
}变量名;
结构体的使用:
结构体变量名.成员名
结构体初始化:
struct 结构体名 变量名={初始数据表};
/*案例:demo2*/
#include <stdio.h>
#include <string.h>
#define N 20
struct student{
int no;
char name[N];
float score;
}s3 = {3, "s3", 90}, s4 = {4, "s4", 88};
int main(int argc,char *argv[])
{
struct student s1, s2;
s1.no = 1;
strcpy(s1.name,"s1");
s1.score = 90;
printf("%d %s %.2f\n",s1.no,s1.name,s1.score);
s2 = s1;
s2.no = 2;
printf("%d %s %.2f\n",s2.no,s2.name,s2.score);
printf("%d %s %.2f\n",s3.no,s3.name,s3.score);
printf("%d %s %.2f\n",s4.no,s4.name,s4.score);
return 0;
}
/*运行结果:
linux@ubuntu:~/Linux_c/day04$ gcc demo2.c -Wall -lm
linux@ubuntu:~/Linux_c/day04$ ./a.out
//上两行为命令行内编译和执行代码
1 s1 90.00
2 s1 90.00
3 s3 90.00
4 s4 88.00
*/
3. 结构体数组和结构体指针
结构体数组的定义:
具有相同结构体类型的结构体变量组成的数组,叫结构体数组,它的定义同平台数组一样。
结构体数组的初始化:
struct 结构体名 数组名[元素个数]={初始化数据表};
结构体指针:
一个指针变量用来指一个结构体变量。此时该指针变量的值是结构变量的起始地址,该指针被称为结构体指针。
一般定义形式:
struct 结构体名 *结构指针名;
/*案例:demo3*/
#include <stdio.h>
#define N 20
struct student {
int no;
char name[N];
float score;
}s2[5];
int main(int argc,char *argv[])
{
struct student s1[] = {{1,"s1",90},{2,"s2",89},{3,"s3",78}};
int i;
for(i = 0;i<3;i++){
printf("%d %s %.2f\n",s1[i].no,s1[i].name,s1[i].score);
s2[i] = s1[2-i];
}
puts("\n-------s2--------");
for(i = 0;i<3;i++)
printf("%d %s %.2f\n",s2[i].no,s2[i].name,s2[i].score);
return 0;
}
/*运行结果:
linux@ubuntu:~/Linux_c/day04$ gcc demo3.c -Wall
linux@ubuntu:~/Linux_c/day04$ ./a.out
1 s1 90.00
2 s2 89.00
3 s3 78.00
-------s2--------
3 s3 78.00
2 s2 89.00
1 s1 90.00
*/
/*案例:demo4*/
#include <stdio.h>
#define N 20
struct student {
int no;
char name[N];
float score;
};
int main(int argc,char *argv[])
{
struct student s1[] = {{1,"s1",90},{2,"s2",89},{3,"s3",78}};
int i;
struct student *p;
p = s1;
for(i = 0;i<3;i++){
printf("%d %s %.2f\n",p->no,p->name,p->score);
p++;
}
return 0;
}
/*运行结果:
linux@ubuntu:~/Linux_c/day04$ gcc demo4.c -Wall
linux@ubuntu:~/Linux_c/day04$ ./a.out
1 s1 90.00
2 s2 89.00
3 s3 78.00
*/
4. 共用体及typedef
共用体:
在C语言中,不同数据类型的数据可以使用共同的存储区域,这种数据构造类型称为共用体,简称共用,又称联合体。在定义说明和使用形式上与结构体相似。本质不同仅在于使用内存上。
定义一个共用体类型的一般形式为:
union 共用体名
{
成员表列;
};
例如:
union gy
{
int i;
char c;
float f;
};
这里定义了一个共用体类型union gy,它由三个成员组成,这三个成员在内存中使用共同的存储空间。共用体变量在存储时总是按其成员中数据长度最大的成员占用内存空间。
在共用体中起作用的成员是最后一次存放的成员,存入一个新的成员后原有的成员就失去作用。
/*案例:共用体内存空间测试 demo5*/
#include <stdio.h>
union gy
{
char a;
short b;
int c;
};
int main(int argc,char *argv[])
{
printf("%d %d %d\n",sizeof(char),sizeof(short),sizeof(int));
printf("%d\n",sizeof(union gy));
return 0;
}
/*运行结果:
linux@ubuntu:~/Linux_c/day04$ gcc demo5.c -Wall
linux@ubuntu:~/Linux_c/day04$ ./a.out
1 2 4
4
*/
typedef:
在C语言中,允许使用关键字typedef定义新的数据类型,其语法如下:
typedef <已有数据类型> <新数据类型>;
5. C语言的内存管理模型(内存管理一)
动态内存
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的话,没有任何效果
-释放一块内存中的一部分是不被允许的
/*案例:malloc和free的使用demo6*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
char * p;
p = (char *)malloc(10 * sizeof(char));
if (p == NULL){
printf("malloc failed\n");
return 0;
}
printf("p=%p\n",p);
printf("input:");
scanf("%s",p);
printf("p=%s\n",p);
free(p);
return 0;
}
/*运行结果:
linux@ubuntu:~/Linux_c/day04$ gcc demo6.c -Wall
linux@ubuntu:~/Linux_c/day04$ ./a.out
p=0x82d8008
input:20240803
p=20240803
*/
6. 动态内存(内存管理二)
malloc/free
注意事项:
删除一个指针p(free(p);),实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身,释放堆空间后,p成了空悬指针。
malloc于free是配对使用的,free只能释放堆空间。如果malloc返回的指针值丢失,则分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另外分配。
动态内存分配的时候要注意其生命周期。它并不依赖于建立它的作用域,比如在函数中建立的动态对象在函数返回后仍可使用。
野指针:
不是NULL指针,是指向已被释放内存的指针。成因主要有:
指针变量没有被初始化
指针p被free之后,没有置为NULL
指针操作超越了变量的作用范围。
/*综合案例:demo7*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 20
typedef struct student{
int no;
char name[N];
float score;
}Stu;
Stu * get_info(){
Stu * p;
if((p = (Stu *)malloc(sizeof(Stu))) == NULL){
printf("malloc failed\n");
return NULL;
}
p->no = 1;
strcpy(p->name, "Tom");
p->score = 90;
return p;
}
int main(int argc,char *argv[])
{
Stu * s;
if ((s = get_info()) == NULL)
return 0;
printf("Student info:%d %s %.2f\n",s->no,s->name,s->score);
free(s);
s = NULL;
return 0;
}
/*运行结果:
linux@ubuntu:~/Linux_c/day04$ gcc demo7.c -Wall
linux@ubuntu:~/Linux_c/day04$ ./a.out
Student info:1 Tom 90.00
*/