文章目錄
- 一、實驗目的:
- 二、實驗要求
- 三、實驗內容
- 四、實驗操作
- 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來會非常頭疼滴 ~