錯誤更正

先前在 C - Type is a Big Problem 文中提到

若未宣告function,參數、回傳值一切以 int作為預設型別

今天發現這是錯誤的,事實上只有回傳值,在此更正

根據 C語言標準的定義

當呼叫函式時未宣告時

編譯器會對該函式字符 (symbol) 預設隱含宣告 (Implicit Declaration)

假設 function名稱為 foo,則隱含宣告為 int foo()

即回傳值為 int,空參數列表之函式

 

空參數列表

現今的習慣用法,當參數列表為空時,代表不接受任何參數

但至少在 C語言,空的參數列表並不真正是這麼回事

空參數列表所代表的是,當呼叫函式時,不限制檢查傳入參數之數量與型別

給什麼就送什麼

或許在舊式C語言中,這是要增加函式實作彈性 (?)

若真需要不接受任何參數之函式

嚴格上宣告時必須於參數列表上填入void,例如 int foo(void);

因此下列程式碼是合法的,編譯階段不會回報任何錯誤

int foo();
foo(1, 2, 3);
foo(1, 2.1);

 

為啥會搞錯

這在C語言當中是一個很基本的常識

為啥會搞錯? 大概因為當時看課本時

把函式隱含宣告,與函式參數隱含型別搞混了

若宣告函式時,參數列的參數僅列名稱但不給予型別

則編譯器會假設該參數型別是 int

例如 void foo(a), a在編譯時會以 int為前提使用

 

空參數列會出什麼問題

狀況一:參數型別不對

以下程式碼,編譯階段是合法的,連結階段因為有正確找到函式參考亦不會有任何錯誤

即從原始碼到產生可執行檔的整個過程中是完全沒有問題

#include <stdio.h>
void foo();

int main(){
    foo(1, 2, 3);
    return 0;
}

void foo(long long a, long long b, long long c){
    printf("%lld %lld %lld\n", a, b, c);
}

但畢竟參數列表與傳遞參數型別不一樣

long long所表為 64-bit (有號) 整數 (64-bit系統中宣告 long即可)

1、2、3文字常量則代表 int (32-bit整數)

以上這段程式碼以 GCC建置後,執行還不會有錯

但那是剛好而已,不能把湊巧誤作正常

因為程式短、參數列表以及傳遞的參數數量很少、

堆疊 (stack) 空間還比傳遞參數空間占用量大

原則上都可以在僅有暫存器的使用下執行完成

呼叫端將參數放置在暫存器 (register) 當中

被呼叫端則從 register置入自己的堆疊 (stack) 內

當呼叫 printf時,再從暫存器將數值放入 printf之參數列當中 (完全沒真正使用到資料記憶體)

原則上現在 CPU的暫存器都已經有 64-bit的大小

因此在程式才剛開始執行時,32-bit的值放入暫存器中

再從暫存器抓取 64-bit數值是沒問題的

但是若程式已經執行一段時間,暫存器前32-bit有被動過的情況下

或者需要的是 float,給的卻是 int 

可能就是完全不一樣的狀況了

有興趣可以看一下編譯出來的組合語言

(float與 int的狀況,型別表示法是不會自動轉換的

有可能會直接把 int數值作浮點表示法解讀

或者發生其他不可預期的狀況,這與硬體會有很大的關係

但若實作與宣告在同一個檔案範圍,編譯器是有辦法判別而產生錯誤的

若是外部鏈結就無法避免)

 

狀況二:傳遞參數之數量過少

這不用說,沒有被傳入的參數一定是莫名其妙的數值

 

狀況三:傳遞參數之數量過多

編譯器在編譯函式時,會根據參數列表、以及區域變數之型別與數量

給予該函式一定額度的堆疊 (stack)空間

若是傳入參數的數量過多,有可能會使堆疊空間不敷使用

因為占用到原有參數與區域變數的空間

若沒有超過堆疊空間還好,因為與區域變數重疊之空間會被區域變數覆蓋

若超過分配的堆疊的額度

聰明一點的系統在程式運行到這種狀況時會產生嚴重錯誤

笨一點的就會任由多出來的資料將其他不屬於該函式的記憶體內容覆寫

狀況一的例子,若函式宣告的參數型別記憶體占用量較傳遞之參數小,亦可能會發生此狀況

 

由於這些例子,因為空參數列表的特性下

編譯連結階段都不會產生任何問題

都要等到執行階段才會發現錯誤

因為大部分執行階段是看不到程式語言層次的,很多時候要除錯就難找原因了

寧願在編譯階段,就讓對程式語言有針對性的編譯器回報清楚明瞭的警告或錯誤

所以寫程式語言還是乖乖的、勤勞一點把該打清楚的文字都打上去吧

arrow
arrow
    文章標籤
    C C++ Function Argument
    全站熱搜

    wylokgo101 發表在 痞客邦 留言(0) 人氣()