Fw: [問卦] 為甚麼C語言自訂函式一定要在主程式上面?
※ [本文轉錄自 Gossiping 看板 #1SzydGFl ]
作者: jserv (松鼠) 看板: Gossiping
標題: Re: [問卦] 為甚麼C語言自訂函式一定要在主程式上面?
時間: Wed Jun 5 21:55:19 2019
※ 引述《fragmentwing (片翼碎夢)》之銘言:
: 如題
: 最近終於開始自學C語言
: 為甚麼C語言一定至少要放個原型在主程式上方才行?
: 或者說,明明都是一行一行讀取,其他語言是怎麼做到可以把函式到處亂擺的
這問題非常好,我不知為何會引來負面聲音,可能是原發問人沒有把「其他語言」清楚
標示出來吧?抑或是問題太簡短呢?有趣的問題如果被忽略,那真是太可惜。
其實由 Dennis M. Ritchie (以下簡稱 dmr) 開發的早期 C 語言編譯器 [1] 沒有明確
要求 function prototype 的順序。dmr 在 1972 年發展的早期 C 編譯器,原始程式碼
後來被整理在名為 "last1120c" 磁帶中 [2],我們如果仔細看 c00.c 這檔案,可發現
位於第 269 行的 mapch(c) 函式定義,在沒有 forward declaration [3] 的狀況下,
就分別於第 246 行和第 261 行呼叫,奇怪吧?
而且只要再瀏覽 last1120c 裡頭其他 C 語言程式後,就會發現根本沒有 #include 或
#define 這一類 C preprocessor [4] 所支援的語法,那到底怎麼編譯呢?在回答這問題
前,摘錄 Wikipedia 頁面的訊息:
> As the C preprocessor can be invoked separately from the compiler with
> which it is supplied, it can be used separately, on different languages.
> Notable examples include its use in the now-deprecated imake system and for
> preprocessing Fortran.
原來 C preprocessor 以獨立程式的形式存在,所以當我們用 gcc 或 cl (Microsoft
開發工具裡頭的 C 編譯器) 編譯給定的 C 程式時,會呼叫 cpp (伴隨在 gcc 專案的
C preprocessor) 一類的程式,先行展開巨集 (macro) 或施加條件編譯等操作,再來
才會出動真正的 C 語言編譯器 (在 gcc 中叫做 cc1)。值得注意的是,1972-1973 年
間被稱為 "Very early C compilers" 的實作中,不存在 C preprocessor (!),當時
dmr 等人簡稱 C compiler 為 "cc",此慣例被沿用至今,而無論原始程式碼有幾個檔
案,在編譯前,先用 cat [5] 一類的工具,將檔案串接為單一檔案,再來執行 "cc"
以便輸出對應的組合語言,之後就可透過 assembler (組譯器,在 UNIX 稱為 "as")
轉換為目標碼,搭配 linker (在 UNIX 稱為 "ld") 則輸出執行擋。
所以說,在早期的 C 語言編譯器,強制規範 function prototype 及函式宣告的順序
是完全沒有必要的,要到 1974 年 C preprocessor 才正式出現在世人面前,儘管當時
的實作仍相當陽春,可參見 dmr 撰寫的 "The Development of the C Language" [6]
C 語言的標準化是另一段漫長的旅程,來自 Bell Labs 的火種,透過 UNIX 來到研究
機構和公司行號,持續影響著你我所處的資訊社會。
你或許會好奇,function prototype 的規範有什麼好處呢?這就要從 "Rationale for
International Standard -- Programming Languages -- C" [7] 閱讀起,依據 C9X
RATIONALE 的第 70 頁 (PDF 檔案對應於第 78 頁),提到以下的解說範例:
extern int compare(const char *string1, const char *string2);
void func2(int x) {
char *str1, *str2;
// ...
x = compare(str1, str2);
// ...
}
編譯器裡頭的最佳化階段 (optimizer) 可從 function prototype 得知,傳遞給函式
compare 的兩個指標型態參數,由於明確標注了 "const" 修飾子,所以僅有記憶體地
址的使用並讀取相對應的內容,但不會因為修改指標所指向的記憶體內容,從而沒有產生
副作用 (side effect)。這樣編譯器可有更大的最佳化空間,可對照「你所不知道的 C
語言:編譯器和最佳化原理篇」,得知相關最佳化手法。
一如 C9X RATIONALE 提到,C 語言和其他受 Algol 影響的程式語言,都具備 function
prototype 機制,這使得編譯時期,就能進行有效的錯誤分析和偵測。無論是 C 語言、
B 語言,還是 Pascal 語言,都可追溯到 ALGOL 60 [9]。
ALGOL 是Algorithmic Language (演算法使用的語言) 的縮寫,提出巢狀 (nested) 結構
和一系列程式流程控制,今日我們熟知的 if-else 語法,就在 ALGOL 60 出現。
ALGOL 60 和 COBOL 程式語言並列史上最早工業標準化的程式語言。
可參見本看板 #1SdKXN4t
程式語言的演化,深受底層工具和硬體限制的影響,這次探討的 function prototype
及函式宣告的順序,其實就反映出編譯器演化的歷程。人類史上第一個編譯器,A-0
System 開發於 1951-52 年間,注意到當時的用語是 "System",而且名稱 A-0 代表
Arithmetic Language version 0,與其說是今天我們提及的高階語言編譯器,不如說是
程式的載入器 (loader) 或連結器 (linker)。
相關訊息可參見本看板 #1RhWSNPG
黑格爾在其 1820 年的著作「法哲學原理」提到: (德語原文)
> Was vernuftig ist, das ist wirklich;
> und was wirklich ist, das ist vernuftig.
英語可解讀為 "What is rational is actual and what is actual is rational.",
像是 C 語言這樣的工業標準,至今仍活躍地演化,當我們回顧發展軌跡時,凡是合乎
理性 (vernuftig),也就必然會出現、或成為現實 (wirklich),反過來說也成立。
甚至我們可推敲 C9X RATIONALE 字裡行間,每個看似死板規則的背後,其實都可追溯出
像是上方的討論。
歡迎關注「你所不知道的 C 語言」系列講座:
https://hackmd.io/@sysprog/HJpiYaZfl
千萬別忘了 C 語言的發展就是為了開發 UNIX 作業系統和相關系統程式,而 Linux 核心
自然就是將 UNIX 精神發揚光大的智慧結晶,歡迎關注「Linux 核心設計」課程,附有
線上教材:
http://wiki.csie.ncku.edu.tw/linux/schedule
[1] Very early C compilers and language,
https://www.bell-labs.com/usr/dmr/www/primevalC.html
[2] 位於 GitHub 的副本,
https://github.com/mortdeus/legacy-cc/
[3] https://en.wikipedia.org/wiki/Forward_declaration
[4] https://en.wikipedia.org/wiki/C_preprocessor
[5] cat 工具程式的作用是 "concatenate and print file",
https://www.unix.com/man-page/posix/1posix/cat/
[6] The Development of the C Language,
https://www.bell-labs.com/usr/dmr/www/chist.html
[7] Rationale for International Standard -- Programming Languages -- C
http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
[8] 你所不知道的 C 語言:編譯器和最佳化原理篇,
https://hackmd.io/@sysprog/Hy72937Me
[9] Algol 60,
https://en.wikipedia.org/wiki/ALGOL_60
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 140.116.82.61 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/Gossiping/M.1559742928.A.3EF.html
推
06/05 21:55,
4年前
, 1F
06/05 21:55, 1F
→
06/05 21:56,
4年前
, 2F
06/05 21:56, 2F
推
06/05 21:56,
4年前
, 3F
06/05 21:56, 3F
→
06/05 21:56,
4年前
, 4F
06/05 21:56, 4F
推
06/05 21:57,
4年前
, 5F
06/05 21:57, 5F
推
06/05 21:57,
4年前
, 6F
06/05 21:57, 6F
推
06/05 21:57,
4年前
, 7F
06/05 21:57, 7F
推
06/05 21:57,
4年前
, 8F
06/05 21:57, 8F
推
06/05 21:58,
4年前
, 9F
06/05 21:58, 9F
推
06/05 21:58,
4年前
, 10F
06/05 21:58, 10F
推
06/05 21:58,
4年前
, 11F
06/05 21:58, 11F
推
06/05 21:59,
4年前
, 12F
06/05 21:59, 12F
推
06/05 21:59,
4年前
, 13F
06/05 21:59, 13F
推
06/05 22:00,
4年前
, 14F
06/05 22:00, 14F
推
06/05 22:00,
4年前
, 15F
06/05 22:00, 15F
推
06/05 22:00,
4年前
, 16F
06/05 22:00, 16F
推
06/05 22:00,
4年前
, 17F
06/05 22:00, 17F
推
06/05 22:00,
4年前
, 18F
06/05 22:00, 18F
推
06/05 22:01,
4年前
, 19F
06/05 22:01, 19F
推
06/05 22:01,
4年前
, 20F
06/05 22:01, 20F
推
06/05 22:01,
4年前
, 21F
06/05 22:01, 21F
推
06/05 22:01,
4年前
, 22F
06/05 22:01, 22F
推
06/05 22:02,
4年前
, 23F
06/05 22:02, 23F
推
06/05 22:02,
4年前
, 24F
06/05 22:02, 24F
→
06/05 22:02,
4年前
, 25F
06/05 22:02, 25F
推
06/05 22:02,
4年前
, 26F
06/05 22:02, 26F
推
06/05 22:02,
4年前
, 27F
06/05 22:02, 27F
推
06/05 22:02,
4年前
, 28F
06/05 22:02, 28F
推
06/05 22:02,
4年前
, 29F
06/05 22:02, 29F
推
06/05 22:03,
4年前
, 30F
06/05 22:03, 30F
推
06/05 22:03,
4年前
, 31F
06/05 22:03, 31F
推
06/05 22:03,
4年前
, 32F
06/05 22:03, 32F
推
06/05 22:03,
4年前
, 33F
06/05 22:03, 33F
→
06/05 22:04,
4年前
, 34F
06/05 22:04, 34F
推
06/05 22:04,
4年前
, 35F
06/05 22:04, 35F
推
06/05 22:04,
4年前
, 36F
06/05 22:04, 36F
推
06/05 22:04,
4年前
, 37F
06/05 22:04, 37F
推
06/05 22:05,
4年前
, 38F
06/05 22:05, 38F
→
06/05 22:05,
4年前
, 39F
06/05 22:05, 39F
還有 203 則推文
推
06/06 07:13,
4年前
, 243F
06/06 07:13, 243F
推
06/06 07:24,
4年前
, 244F
06/06 07:24, 244F
推
06/06 07:44,
4年前
, 245F
06/06 07:44, 245F
推
06/06 08:15,
4年前
, 246F
06/06 08:15, 246F
推
06/06 08:18,
4年前
, 247F
06/06 08:18, 247F
→
06/06 08:20,
4年前
, 248F
06/06 08:20, 248F
推
06/06 08:25,
4年前
, 249F
06/06 08:25, 249F
→
06/06 08:26,
4年前
, 250F
06/06 08:26, 250F
→
06/06 08:26,
4年前
, 251F
06/06 08:26, 251F
推
06/06 08:36,
4年前
, 252F
06/06 08:36, 252F
推
06/06 08:38,
4年前
, 253F
06/06 08:38, 253F
→
06/06 08:45,
4年前
, 254F
06/06 08:45, 254F
推
06/06 08:46,
4年前
, 255F
06/06 08:46, 255F
推
06/06 09:28,
4年前
, 256F
06/06 09:28, 256F
推
06/06 09:36,
4年前
, 257F
06/06 09:36, 257F
推
06/06 10:02,
4年前
, 258F
06/06 10:02, 258F
推
06/06 10:10,
4年前
, 259F
06/06 10:10, 259F
推
06/06 10:12,
4年前
, 260F
06/06 10:12, 260F
推
06/06 10:15,
4年前
, 261F
06/06 10:15, 261F
推
06/06 10:16,
4年前
, 262F
06/06 10:16, 262F
推
06/06 10:45,
4年前
, 263F
06/06 10:45, 263F
推
06/06 11:03,
4年前
, 264F
06/06 11:03, 264F
→
06/06 11:23,
4年前
, 265F
06/06 11:23, 265F
推
06/06 11:29,
4年前
, 266F
06/06 11:29, 266F
推
06/06 11:33,
4年前
, 267F
06/06 11:33, 267F
推
06/06 11:41,
4年前
, 268F
06/06 11:41, 268F
推
06/06 12:34,
4年前
, 269F
06/06 12:34, 269F
推
06/06 12:37,
4年前
, 270F
06/06 12:37, 270F
推
06/06 13:10,
4年前
, 271F
06/06 13:10, 271F
推
06/06 17:04,
4年前
, 272F
06/06 17:04, 272F
推
06/06 20:18,
4年前
, 273F
06/06 20:18, 273F
→
06/06 20:22,
4年前
, 274F
06/06 20:22, 274F
→
06/06 21:11,
4年前
, 275F
06/06 21:11, 275F
→
06/06 21:11,
4年前
, 276F
06/06 21:11, 276F
→
06/06 21:12,
4年前
, 277F
06/06 21:12, 277F
→
06/06 21:13,
4年前
, 278F
06/06 21:13, 278F
→
06/06 21:13,
4年前
, 279F
06/06 21:13, 279F
推
06/06 22:07,
4年前
, 280F
06/06 22:07, 280F
推
06/07 03:41,
4年前
, 281F
06/07 03:41, 281F
※ 發信站: 批踢踢實業坊(ptt.cc)
※ 轉錄者: okcool (125.230.18.220 臺灣), 06/08/2019 23:34:45