文章目录
- 一、实验目的:
- 二、实验要求
- 三、实验内容
- 四、实验操作
- 1、用gcc编译程序,写出编译过程,并给出运行结果。
- 2、调试程序,要求用gdb进行调试并给出修改方案。
- 3、make的使用
一、实验目的:
1、练习并掌握Linux提供的vi编辑器来编译C程序
2、学会利用gcc、gdb编译、调试C程序
3、学会使用make工具
二、实验要求
1、编写C语言程序,用gcc编译并观察编译后的结果,运行生成的可执行文件。
2、利用gdb调试程序。
3、学习编写makefile,并进行编译。
三、实验内容
1、GNU C编译器
(1)使用gcc
通常后跟一些选项和文件名来使用gcc编译器。gcc命令的基本用法如下:
gcc [options] [filenames] 命令行选项指定的编译过程中的具体操作
(2)gcc常用选项
当不用任何选项编译一个程序时,gcc将建立(假定编译成功)一个名为a.out的可执行文件。 选项含义:
-o FILE 指定输出文件名,在编译为目标代码时,这一选项不是必须的。如果FILE 没有指定,默认文件名是a.out. 也可用-o选项来为即将产生的可执行文件指定一个文件名来代替a.out。
-c GCC 仅把源代码编译为目标代码。默认时GCC 建立的目标代码文件有一个.o 的 扩展名。
-E 对文件进行预处理
-S 对文件进行编译,生成汇编代码。
-O 对源代码进行基本优化。这些优化在大多数情况下都会使程序执行得更快。
-g 在可执行程序中包含标准调试信息。
-Wall 允许发出GCC 能提供的所有有用的警告,也可以用-W(warning)来标识指定的警告。
-l name 链接静态库
-L dir 库文件的搜索路径
(3)执行文件
格式: ./可执行文件名
2、gdb调试工具
(1)调试编译代码
为了使gdb正常工作,必须使你的程序在编译时包含调试信息。调试信息里包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号。gdb利用这些信息使源代码和机器码相关联。
在编译时用 –g 选项打开调试选项。
(2)gdb基本命令
命令 | 描述 |
---|---|
file | 装入欲调试的可执行文件 |
kill | 终止正在调试的程序 |
list | 列出产生执行文件的源代码部分 |
next | 执行一行源代码但不进入函数内部 |
step | 执行一行源代码并进入函数内部 |
run | 执行当前被调试的程序 |
quit | 终止gdb |
watch | 监视一个变量的值而不管它何时被改变 |
break | 在代码里设置断点,使程序执行到这里时被挂起 |
make | 不退出gdb就可以重新产生可执行文件 |
shell | 不离开gdb就执行UNIX shell 命令 |
四、实验操作
1、用gcc编译程序,写出编译过程,并给出运行结果。
mypow.c:定义mypow()函数
unsigned long long mypow(unsigned int x, unsigned int y)
{unsigned long long res=1;if (y==0)res = 1;else if (y==1):res = x;elseres = x * mypow(x, y-1);return res;
}
powtest.c:调用mypow()函数
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{unsigned int x, y;unsigned long long res;if ((argc<3) || (sscanf(argv[1], "%u", &x)) != 1 || (sscanf(argv[2], "%u", &y)) != 1 ){printf("Usage:pow base exponent\n");exit(1);}res = mypow(x, y);printf("%u ^ %u = %u\n", x, y, res);return 0;
}
用
gcc -o
命令编译
用
gcc
命令编译
要注意实验中给出的powtest.c文件里没有对mypow.c头文件进行引用,因此做实验的时候自己要加这个头文件如下图第四行
2、调试程序,要求用gdb进行调试并给出修改方案。
(1)源程序的功能:按照正序和逆序输出给定的字符串。要求用gdb进行调试,分析出错的原因并给出修改方案。
#include <stdio.h>
#include <string.h>
#include <malloc.h>
void my_print (char *string);
void my_print2 (char *string);
int main()
{char my_string[] = "hello there";my_print (my_string);my_print2 (my_string);return 0;
}
void my_print (char *string)
{printf ("The string is %s\n", string);
}
void my_print2 (char *string)
{char *string2;int size, i;size = strlen (string);string2 = (char *) malloc (size + 1);for (i = 0; i < size; i++)string2[size - i] = string[i];string2[size + 2] = '\0';printf ("The string printed backward is %s\n", string2);
}
第一种调试方法:
上述调试方法中从26行跳出,然后查证string数组的值,会发现string[0]位置上的值未被替换,仍为正序输入
第二种调试方法
上述方法中可以看出来size的值是11,之后的语句size-i我们可知是从string2[11]开始赋值的,那这样我们赋值到string2[1]时,“hello there"就复制结束了,但是string2[0]仍未被赋值,这就出现了错误。
Warning:
string2[0]='\000' string2[1]='e' string2[2]='r' string2[size]='h'
也就是说没有从string2[10]开始存储值,而是从string2[11]开始存储的,故string2[0]产生空置。
方案:赋值时应该size-1-i开始逆序赋值,而不是size-i
将 string2[size - i] = string[i]; 替换成 string2[size - i - 1] = string[i];
将string2[size + 2] = ‘\0’; 替换成 string2[size] = ‘\0’;
3、make的使用
(1)用vi编辑以下程序,程序清单:
main.c
function1.h
function1.c
function2.h
function2.c
//main.c
#include "function1.h"
#include "function2.h"
int main(int argc, char **argv)
{
function1_print("hello");
function2_print("world");return 0;
}
//function1.h
void function1_print(char *str);
//function1.c
#include "function1.h"
void function1_print(char *str)
{
printf("This is function1 print %s\n", str);
}
//function2.h
void function2_print(char *str);
//function2.c
#include "function2.h"
void function2_print(char *str)
{
printf("This is function2 print %s\n", str);
}
(2)实验要求:
a)画出各个源程序、目标文件以及最终的目标文件之间的依赖关系图。
b)编辑makefile文件
ps:上传图片的时候才发现自己把function2.o写成function2.0了,我个铁憨憨
c)利用make命令进行上述程序的编译,生成可执行代码并运行。
d)修改其中一个源文件,重新make,察看编译过程。
e)通过使用makefile变量和隐含规则,对makefile文件进行简化
这里的
$(CFLAGS)
可以不写,其是c编译器的选项,无默认值,这里我们也不需要选择编译器,但是养成良好的代码习惯也是很必要滴 ~ 万一以后需要选择编译器但没写(不大可能),找起bug来会非常头疼滴 ~