分享到plurk 分享到twitter 分享到facebook

case_study/多檔編譯

這是 2009 年在醉資心上的一個討論,有參考價值。

下方原文照列

::

作者  dcfgh (笨蛋魚)                                       站內  homework102A
標題  [程設]作業4
時間  2009/11/20 Fri 15:49:21



有鑑於教授的作業單太奇妙,

有的人沒聽到教授的解釋,

所以來大概說明一下......
--------------------------------

board_main.c 裡面大概長這樣

#include"board.h"

int main (void)
{
內容.....
}
--------------------------------
board.c 裡面大概長這樣


#include"board.h"

void function1()
{
內容......
}

void function2()
{
內容......
}

void function3()
{
內容......
}

.........

--------------------------------

board.h裡面長這樣


#include<stdio.h>
#include<stdlib.h>  /*看你有沒有用到他*/
#define n 16

void function1();   /*就是把原本會main()上面的prototype都搬過來就是了*/
void function2();
void function3();
......
-----------------------------------------------------

.dat檔好像跟.txt 差不多...吧
存檔/讀檔 參照販賣機就可以了



然後 compile的時候

gcc board_main.c board.c
它就會自動幫你組起來
產生一個a.out的執行檔

就能執行了


以上......有錯不要找我((喂


--
※ Origin: 成功大學資訊工程學系[醉資心BBS] <goodguy.csie.ncku.edu.tw>
◆ From: 218-175-223-167.dynamic.hinet.net
→ fivil52 推:我發現board.h裡面 可以不用再宣告一次function耶         09/11/20
→ CrBoy 推:樓上怎麼寫的?照理來說 要是這樣的話你在main裡面用到那些  09/11/20
→ CrBoy 推:function的時候...會.......出........錯.....阿我知道了!!! 09/11/20
→ CrBoy 推:因為implicit declaration的關係 所以compile的時候不會出錯 09/11/20
→ CrBoy 推:但是這樣的習慣「非常不好」 而且容易發生錯誤!            09/11/20
→ pcyu16 推:我們請專業的小畢為我們po版解釋link (拍手)               09/11/20
→ TonyYang 推:畢畢畢~~(拍手~~)                                      09/11/20
→ julieting 推: 玉玉玉~~(拍手~~)                                    09/11/20
→ fivil52 推:恩恩 大概知道什麼意思 可以請小畢解釋不好的原因嗎?      09/11/20
→ BlackJackWei 推:泉泉泉~~(拍手~~)                                  09/11/20



 作者  pcyu16 (hide and see)                                站內  homework102A
 標題  Re: [程設]作業4
 時間  2009/11/22 Sun 01:37:32


先簡單的講個範例

main.c
==============================
#include <stdio.h>
#include "func.h"

int main()
{
	printf("%d\n", func(1));
	retrun 0;
}
==============================

func.c
==================
#include "func.h"
int func(int x)
{
	return x;
}
==================

func.h
===============
#ifndef FUNC_H
#define FUNC_H

int func(int);

#endif
===============

編譯:
gcc -c main.c
gcc -c func.c
gcc -o prog main.o func.o

執行:
./prog

/////////////////////////////////////////////////////////////////////

先解釋這個 link 方法

gcc -c 是編譯成為 object file, 不做 link 的動作

預設會產生同名的 .o 檔, 也就是 object file

他會確認所有函數宣告都是正確的..

這個時候他會提示所有的 undefined reference


func.h 裡面的 #ifndef - #endif

是為了避免重複引用相同 header, 請養成習慣加上去..

至於那個東西到底怎麼寫.. 我想上面例子應該不難理解才對

就是 if 對應 endif, 中間就是條件式成立要做的事

當條件式第一次成立的時候會定義 FUNC_H

第二次遇到這個條件式的時候就不會成立了.. 這就是原理


現在來講一下上一篇的方法哪邊不好..


gcc board_main.c board.c

這個指令會把這幾個 source file 一起編譯

這樣編譯當然是會成功的  其實看起來也沒啥問題Orz

除了那個 define n 以外, 其實這樣 header 連 function 宣告都不用寫Orz

因為 function 中間的連結被直接用 gcc 指令連起來了- -


小畢推文提到的 implicit declaration

翻成中文應該是 隱含的宣告函式

如果 header 裡面沒有定義函數, 又用 gcc 加上全部 source file 來編譯

當漏掉 board.c 的時候, board_main.c 就會因為找不到 function 定義而 error

這個應該大家都能理解..

在這次的作業當中, 頂多不過就兩三個檔案, header 也不多, 這樣不會有什麼問題

如果遇到 source file 爆炸多的情況

直接用 gcc 後面加上全部 source file.. 錯漏的時候很難找= =a

再說如果真的是很龐大的 project.. 重新編譯全部檔案的時間也很可觀


總之~ 希望大家用正確的方式來寫

培養正確的習慣~ 這樣對大家都好~

寫到這句讓我想到goto..(汗)  請大家不要再折磨助教了XD


下台一鞠躬

--
※ Origin: 成功大學資訊工程學系[醉資心BBS] <goodguy.csie.ncku.edu.tw>
◆ From: academy.csie.ncku.edu.tw
→ shadeel 推:小玉好文必推 小畢的呢? XD                              09/11/22
→ lavchi 推:感謝小玉 Q_Q   //小畢要發一篇完整 code 的(誤)           09/11/22
→ julieting 推:好文必推  小玉好厲害:)  (拍手~)                      09/11/22
→ callawaft 推:好文必推  小玉好厲害:)  (拍手~)                      09/11/22
→ Yami 推:     好文必推  小玉好厲害:)  (拍手~)                      09/11/22
→ fivil52 推:  好文必推 小玉好厲害好厲害:)  (拍手~)                 09/11/22
→ fivil52 推:....被微軟新注音婊了                                   09/11/22
→ Rickz008 推: 好文必推  小玉好厲害:)  (拍手~)                      09/11/22
→ colie4 推:   好文必推  小玉好厲害:)  (拍手~)                      09/11/22
→ mike771115 推: 文必推  小玉好厲害:)  (拍手~)                      09/11/22
→ lionking 推:順便推廣一下: 對於像這種有多個 source file 的 project 09/11/22
→ lionking 推:可以學學看怎麼寫 makefile~~很有用喔!                  09/11/22
→ pcyu16 推:詳見作業100a 精華區3-5-7                                09/11/22
→ pcyu16 推:不懂想學可以問樓上上XD  他寫的                          09/11/22
→ lionking 推:............                                          09/11/22
→ dcfgh 推:    好文必推  小玉好厲害:)  (拍手~)                      09/11/22
→ CrBoy 推:    好文必推  小玉好厲害:)  (拍手~)                      09/11/22
→ CrBoy 推:undefined reference是在link stage才會出現的....          09/11/22
→ CrBoy 推:compilation stage頂多告訴你 undeclared function/variable 09/11/22
→ CrBoy 推:即使是compile單一檔案也會有implicit declaration的情形    09/11/22
→ CrBoy 推:這是因為你要是沒有先宣告 那他會隱含宣告一個回傳int的函式 09/11/22
→ CrBoy 推:要是我沒記錯的話啦╮( ̄▽ ̄")╭                          09/11/22
→ cc123 推:             if(m==1)                                    09/11/23
→ cc123 推:    好文必推  小玉好厲害:)  (拍手~)                      09/11/23



 作者  CrBoy (VIM rocks!)                                   站內  homework102A
 標題  Re: [程設]作業4
 時間  2009/11/23 Mon 04:52:03

→ CrBoy 推:因為implicit declaration的關係 所以compile的時候不會出錯 09/11/20
→ CrBoy 推:但是這樣的習慣「非常不好」 而且容易發生錯誤!            09/11/20
→ pcyu16 推:我們請專業的小畢為我們po版解釋link (拍手)               09/11/20
→ fivil52 推:恩恩 大概知道什麼意思 可以請小畢解釋不好的原因嗎?      09/11/20

寫作業寫累了orz 來寫點文章

這邊我會省略很多小細節 因為天黑了 講太詳細會有飆車族

你在compile的時候呢 其實會分成幾個階段:
1. preprocess
2. compile
3. assemble (但是其實這個步驟會跟compile一起 但也可以分開處理)
4. link

拜託不要問我中文 很恐怖...(前置處理、編譯、組譯、連結)

preprocess的時候呢 是經由preprocessor來處理的 他只會去把你的那些#開頭的東西

給處理過 例如#include, #define, #ifndef之類的這些preprocessor directive

compile的時候呢 當然就是交給compiler囉~他會幫你分析你的code

然後把你的code轉換成組合語言(更精確的說是instructions)

然後assemble是透過assembler來處理 他是把組合語言轉換成二進位的機器語言

但是就像上面說的 其實compiler也可以直接把C轉換成機器語言這樣...沒啥差

link嘛...就是很多人常常忽略 卻非常重要非常需要明白的觀念了!

他是由linker來做的 但是我直接講link一定很多人聽不懂 所以我要從compile講起...


(以下這段請慢慢閱讀 並配合大腦思考 看不懂小弟不負任何責任= =)

compile事實上只會幫你檢查該宣告的宣告了沒 那一類的事情而已

他不管你用到的function有沒有定義!

我舉個例你們就會懂了 大家都用過printf吧...

printf在哪定義的?printf這個function寫在哪?

應該很多人會說"系統給的" 但是其實不對唷...所謂系統給的 他也應該存在某個地方吧

那應該也很多人記得萬一沒有#include <stdio.h>就沒辦法用printf吧?

這又是為什麼呢?因為 stdio.h裡面包含了printf這個function的宣告!

不知道有沒有人注意到我用「宣告」跟「定義」這兩個詞?他們的英文分別是

declaration(declare)跟definition(define) 看錯誤訊息的時候應該都會稍微看到

宣告是說「有這個東西存在」

定義是說「這個東西實際上是什麼樣子」

你寫function的prototype(例:int f(int x);)就是宣告

你寫function的實體(例:int f(int x){return x;})就是定義

好 所以compile的時候 只要有宣告他就當你有這個function compile就可以通過了!

但是 如果你的程式實際要跑 總不可能要電腦去執行一個不知道實際上幹嘛的函式吧= =

所以 在真的變成執行檔之前 他一定要知道function的定義所在!

這就是linker在做的事情.....

(什麼?要是看不懂請反覆閱讀加上仔細思考...)

好 現在回到本來的問題上 為什麼要寫.h檔?(其實他叫做header file)

因為可能很多個檔案都要使用同一個函式 你每次都要在那些用到的檔案裡宣告一次

實在很辛苦 更重要的是 修改不易!萬一你的function需要加一個參數怎麼辦?

所以就把這些 每個檔案裡面都會也都應該一樣的宣告抽出來放在header file裡面就好

這樣每個個別檔案在compile的時候就不會發生undeclare的錯誤了...:P

至於有時候可能會有人遇到undefined reference的error 這是發生在link stage的

意思就是說 雖然compile的時候知道有個function被呼叫

但是在link的時候發現這個function根本不知道在哪裡 linker當然就會對你發出抱怨了

這樣懂嗎?欸我打得很辛苦捧個場吧....XD


有些人會不小心造成implict declaration 運氣好說不定不會出錯

運氣不好 link的時候就會發現原來implict declare的東西跟他的實體其實並不一樣

這樣就囧了....

所以說.....   = = 建議千萬別這樣做 上次有人這樣 結果.....

結果link的時候出錯 很不方便

寫程式跟看醫生一樣 最好都可以早期發現早期治療

可以在compilation time發現的錯誤 那就這時候把他找出來!

所以推薦各位在compile的時候加上-Wall這個參數(ex: $ gcc -Wall -o hw hw.c )

他會把所有的warning都列出來 其實warning就表示「潛在的錯誤」 請記得!

可以在link的時候發現的問題 當然就在link的時候發現(我真想不到例子XD)

因為 萬一程式開始執行之後 才發現的問題 通常都會花很多時間才找到答案

應該很多人很有經驗= = 所以....嗯 就這樣~

fivil52這樣有滿意嗎XD

--
※ Origin: 成功大學資訊工程學系[醉資心BBS] <goodguy.csie.ncku.edu.tw>
◆ From: 122-122-218-253.dynamic.hinet.net
→ CrBoy 推:我賭很多人直接按end!ˊˋ 這觀念很重要又很少人會講耶!    09/11/23
→ TonyYang 推: 小畢出品   大碗滿意      實在很感謝 不過現在沒大腦.. 09/11/23
→ fivil52 推:這是控制碼嗎                                           09/11/23
→ fivil52 推:感謝小畢專業解答~                                      09/11/23
→ kelly2019 推:感謝~~~~:D                                           09/11/23
→ locke2833 推:推!                                                  09/11/23
→ pop1210 推:講的好詳盡,辛苦了                                      09/11/23