日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

编译器构造c语言描述pdf,关于编译器构造:为什么每次都要在C中指定数据类型?...

發(fā)布時間:2024/1/18 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 编译器构造c语言描述pdf,关于编译器构造:为什么每次都要在C中指定数据类型?... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

從下面的代碼片段中可以看到,我聲明了一個char變量和一個int變量。 編譯代碼時,它必須標識變量str和i的數(shù)據(jù)類型。

為什么在掃描變量期間需要通過將%s或%d指定為scanf來再次告訴它是字符串還是整數(shù)變量? 聲明變量時,編譯器還不夠成熟,無法識別嗎?

#include

int main ()

{

char str [80];

int i;

printf ("Enter your family name:");

scanf ("%s",str);

printf ("Enter your age:");

scanf ("%d",&i);

return 0;

}

%x,%d,%s都是"說明" printf()如何顯示數(shù)據(jù)的格式說明符;即是將位流顯示為十六進制數(shù)字還是十進制整數(shù)或ASCII表示形式。數(shù)據(jù)就是數(shù)據(jù)就是數(shù)據(jù)。 :-)程序員(使用printf)可以隨意解釋它。

請參見Yu Haos的答案...問題是printf和scanf是varargs函數(shù),這意味著它們的參數(shù)沒有靜態(tài)類型。 C是弱類型的……函數(shù)沒有可以檢查的運行時類型信息。格式用于此目的。

我對格式說明符的回答如何?并且您認為@xvp先生建議使用scanf()不一定要使用stdio.h嗎?

誰說格式始終是編譯器可以分析的像"%s"這樣的字符串文字?它可以是具有計算值的字符串。編譯器無法在編譯時分析它們。

編譯器的成熟與否是否與語言功能有關(guān),任何人都難以回答關(guān)于您的編譯器的問題,而您還沒有說是哪個。

盡管C語言的局限性以及您可以在運行時創(chuàng)建格式字符串的事實意味著對于printf,scanf和朋友,您將始終需要再次指定類型,如果格式字符串是經(jīng)過硬編碼的,則有些編譯器至少會為您驗證類型是否匹配:gcc.gnu.org/onlinedocs/gcc/

@TheCodeArtist如果這足以關(guān)閉程序員的C語言,我們還是不希望他們。

盡管這一點是正確的,但沒有真正的原因使編譯器看不到"%?"。 (其中%?是編譯器擴展),然后使用typeof(next_arg_in_list)來猜測是什么?應該被替換為,并帶有使用" 0x"之類的上下文來指定十六進制等的獎勵。

在許多方面,這一評論之上的第二條評論可能是我見過的最糟糕的評論...

@technosaurus首先,該建議是一種提供運行時類型信息的方法,與OP完全不同,后者將完全消除format參數(shù)。其次,有一個真正的原因:C語言標準不支持任何此類東西。也許有一天會。

因為像scanf和printf這樣的變量參數(shù)函數(shù)沒有可移植的方式來知道變量參數(shù)的類型,甚至沒有傳遞多少個參數(shù)。

請參見C常見問題解答:如何發(fā)現(xiàn)函數(shù)實際使用了多少個參數(shù)?

這就是為什么必須至少有一個固定的自變量來確定變量自變量的數(shù)量,也許是類型的原因。這個參數(shù)(標準稱為parmN,請參見C11(ISO / IEC 9899:201x)§7.16變量參數(shù))起著特殊的作用,并將被傳遞給宏va_start。換句話說,您無法在標準C語言中使用具有以下原型的函數(shù):

void foo(...);

+1是這里唯一能識別出問題所在的答案...缺少可變參數(shù)的靜態(tài)類型。

另外,如上面的注釋中所述,在var類型和printf格式"類型"之間沒有一一對應的關(guān)系。

@MarkHu是的,但這更像是結(jié)果,而不是原因。無論如何,請考慮一下,為什么printf的格式說明符使用%f輸出double,float呢?

在我上面的評論之后添加的第二段是非常不幸的,完全是錯誤的。僅在沒有添加其他語言功能的情況下,沒有其他方法可以在基于堆棧的實現(xiàn)中指定參數(shù)的起始地址,才需要第一個參數(shù)。確定參數(shù)類型的其他方法有很多,包括全局變量,并且沒有特殊的原因必須將類型信息放在第一個參數(shù)中……例如,在開始未知類型的參數(shù)之前,可以有5個已知類型的參數(shù),并且您仍然必須對所有這些都使用va_arg。

@JimBalter我沒有在任何地方說第一個論點,對嗎?根據(jù)定義,parmN是...之前最右邊的參數(shù),它不一定是第一個參數(shù)。在標準C中是必需的。

@YuHao是的,我討厭第一個參數(shù),但是其余的都是正確的……需要parmN才能將地址??放入堆棧;它與如何確定類型無關(guān),這取決于函數(shù)……而類型肯定不必從parmN派生。當然,它是標準C的"必需",因為它是標準的一部分...我在X3J11上,并且確切地知道為什么...這是現(xiàn)有實現(xiàn)中現(xiàn)有varargs機制的標準化,并由C編譯器供應商認可誰主導了委員會。

@JimBalter我的意思是,即使您使用其他方法確定類型信息,標準C變量自變量函數(shù)也必須在語法上具有parmN自變量。

您寫道:"這是原因……確定數(shù)量,也許是類型……"。那根本不是原因。您無法通過parmN判斷參數(shù)的數(shù)量。例如,對于某些函數(shù),最后一個參數(shù)為NULL,否則為結(jié)束指示符。

最后,第二段與問題無關(guān)……第一段對此進行了說明。

編譯器無法提供必要信息的原因很簡單,因為此處未涉及編譯器。函數(shù)的原型未指定類型,因為這些函數(shù)具有變量類型。因此,實際的數(shù)據(jù)類型不是在編譯時確定的,而是在運行時確定的。

然后,該函數(shù)從堆棧中獲取一個參數(shù),在另一個參數(shù)之后。這些值沒有任何關(guān)聯(lián)的類型信息,因此,函數(shù)唯一的方法就是通過使用調(diào)用方提供的信息(格式字符串)來知道如何解釋數(shù)據(jù)。

函數(shù)本身不知道傳入的數(shù)據(jù)類型,也不知道傳遞的參數(shù)數(shù)量,因此printf不能自行決定。

在C ++中,可以使用運算符重載,但這是一種完全不同的機制。因為在這里編譯器會根據(jù)數(shù)據(jù)類型和可用的重載函數(shù)選擇適當?shù)暮瘮?shù)。

為了說明這一點,printf在編譯時如下所示:

push value1

...

push valueN

push format_string

call _printf

printf的原型是這樣的:

int printf ( const char * format, ... );

因此,除了格式字符串中提供的內(nèi)容之外,沒有任何類型信息會遺留。

通常對機制進行很好的解釋,然后再講技術(shù)性的語言。

使用字符串作為格式的一個很好的用途是,該格式實際上可以來自各種來源:其未在源代碼中進行硬編碼。您甚至可以要求用戶提供自己的格式字符串,或者將其與gettext一起使用以更改模式順序。

@ Max-P,是的,那絕對是優(yōu)勢,但同時也是危險的填空游戲。 :)當然,無論如何,您都必須編寫一些包裝器,因為如果可以在命令行上提供格式字符串(可以這么說),則必須確保還提供了適當?shù)膮?shù)。

push ... push call序列是實現(xiàn)printf調(diào)用的一種可能方式。實際的調(diào)用約定不是由C標準指定的(通常由ABI為平臺指定)。例如,某些參數(shù)可以在寄存器中傳遞,并且可以以任何順序傳遞。

@KeithThompson,我知道。這取決于實施和優(yōu)化。但是,無論如何完成,都沒有關(guān)聯(lián)的類型信息,只有調(diào)用者指定的原始二進制值。

printf不是內(nèi)部函數(shù)。它本身不是C語言的一部分。編譯器所做的全部工作就是生成代碼以調(diào)用printf,并傳遞任何參數(shù)。現(xiàn)在,由于C不提供反射作為在運行時找出類型信息的機制,因此程序員必須顯式提供所需的信息。

@Kevin Panko感謝您的出色編輯!就編輯而言,我仍然是菜鳥。

對兩位投票者:有幫助的解釋...

+1表示printf不是語言功能或任何特殊功能,因此編譯器沒有義務對其進行優(yōu)化

@Tarik如果printf不是C語言的一部分,那么它實際上在哪里實現(xiàn)?通過操作系統(tǒng)?

編譯器可能很聰明,但是函數(shù)printf或scanf卻很愚蠢-它們不知道您為每次調(diào)用傳遞的參數(shù)的類型是什么。這就是為什么您需要每次通過%s或%d的原因。

您如何才能使這些功能更智能? C不支持反射。

在C++中,您可以:cin >> counter之類的東西;或cout << str;按預期工作。但是在C中您不能這樣做,這就是為什么發(fā)明了像%d這樣的字符串格式的原因。

在C ++中,cin / cout的示例不合適。之所以起作用,是因為重載,因此編譯器可以在編譯時決定它將為調(diào)用提供哪個函數(shù)。這是與varargs完全不同的機制。對于用戶來說,它看起來很相似,但從技術(shù)上講并不是這樣。

那是因為C ++支持運算符重載,這最終意味著您有一個用于<

我認為理論上編譯器可以插入正確的%x。

從理論上講,是的,但是字符串的格式是未知的。畢竟,您可以傳入任意字符串。從理論上講,在編譯時可以完成的工作基本上是像printf一樣解析格式字符串,并檢查參數(shù)是否正確。但是,那么您將必須為所有標準功能提供此功能。但是話又說回來,您可以打印一個字符串作為指針或字符串。

即使這樣也行不通:沒有什么可以阻止某人嘗試傳遞比int,double或char*更復雜的內(nèi)容:例如,如果變量是某個怪異結(jié)構(gòu)的指針。

@Timo從理論上講,可以做任何事情,但這將改變C的底層性質(zhì)以及它所具有的靈活性。不再是C ...但是為什么要打擾呢? C ++已經(jīng)存在。

第一個參數(shù)是格式字符串。如果您要打印一個十進制數(shù)字,它可能看起來像:

"%d"(十進制數(shù))

"%5d"(十進制數(shù)字用空格填充到寬度5)

"%05d"(十進制數(shù)字用零填充到寬度5)

"%+d"(十進制數(shù),始終帶有符號)

"Value: %d

"(數(shù)字前后的一些內(nèi)容)

等等,請參閱例如Wikipedia上的Format占位符,以了解可以包含哪些格式字符串。

這里也可以有多個參數(shù):

"%s - %d"(字符串,然后是一些內(nèi)容,然后是數(shù)字)

Isn't the compiler matured enough to identify that when I declared my

variable?

沒有。

您使用的是數(shù)十年前指定的語言。不要指望C提供現(xiàn)代設計美學,因為它不是現(xiàn)代語言。現(xiàn)代語言將傾向于在編譯,解釋或執(zhí)行中犧牲少量效率,以提高可用性或清晰度。從計算機處理時間昂貴且供應極為有限的時代開始,它的設計便體現(xiàn)了這一點。

這也是為什么當您真正在乎快速,高效或接近金屬時,C和C ++仍然是首選語言的原因。

那真是奇怪的評論。我從來沒有主張過。

是的,很抱歉,我將其寫到了錯誤的打開標簽中。抱歉給你帶來不便

GCC(可能還有其他C編譯器)至少在某些情況下會跟蹤參數(shù)類型。但是語言不是那樣設計的。

printf函數(shù)是一個接受變量參數(shù)的普通函數(shù)。可變參數(shù)需要某種運行時類型識別方案,但是在C語言中,值不攜帶任何運行時類型信息。 (當然,C程序員可以使用結(jié)構(gòu)或位操作技巧創(chuàng)建運行時鍵入方案,但這些未集成到語言中。)

當我們開發(fā)這樣的函數(shù)時:

void foo(int a, int b, ...);

我們可以在第二個參數(shù)之后傳遞"任意"數(shù)量的附加參數(shù),這取決于我們使用函數(shù)傳遞機制之外的某種協(xié)議來確定有多少個參數(shù)以及它們的類型。

例如,如果我們這樣調(diào)用此函數(shù):

foo(1, 2, 3.0);

foo(1, 2,"abc");

被呼叫者無法區(qū)分案件。參數(shù)傳遞區(qū)域中只有一些位,我們不知道它們是表示字符數(shù)據(jù)的指針還是浮點數(shù)。

交流此類信息的可能性很多。例如,在POSIX中,exec系列函數(shù)使用的變量參數(shù)具有相同的類型char *,并且使用空指針來指示列表的結(jié)尾:

#include

void my_exec(char *progname, ...)

{

va_list variable_args;

va_start (variable_args, progname);

for (;;) {

char *arg = va_arg(variable_args, char *);

if (arg == 0)

break;

/* process arg */

}

va_end(variable_args);

/*...*/

}

如果調(diào)用者忘記傳遞空指針終止符,則該行為將是不確定的,因為該函數(shù)在使用完所有參數(shù)后將繼續(xù)調(diào)用va_arg。我們的my_exec函數(shù)必須這樣調(diào)用:

my_exec("foo","bar","xyzzy", (char *) 0);

需要對0進行強制類型轉(zhuǎn)換,因為沒有上下文可以將其解釋為空指針常量:編譯器不知道該參數(shù)的預期類型是指針類型。此外,(void *) 0是不正確的,因為它只是作為void *類型而不是char *類型傳遞,盡管幾乎可以肯定兩者在二進制級別兼容,所以它將在實踐中起作用。該類型的exec函數(shù)的常見錯誤是:

my_exec("foo","bar","xyzzy", NULL);

編譯器的NULL恰好被定義為0而沒有任何(void *)強制轉(zhuǎn)換。

另一種可能的方案是要求調(diào)用者傳遞一個數(shù)字,該數(shù)字指示有多少個參數(shù)。當然,該數(shù)字可能不正確。

在printf的情況下,格式字符串描述參數(shù)列表。該函數(shù)對其進行解析并相應地提取參數(shù)。

如開頭所述,某些編譯器,尤其是GNU C編譯器,可以在編譯時解析格式字符串,并根據(jù)參數(shù)的數(shù)量和類型執(zhí)行靜態(tài)類型檢查。

但是,請注意,格式字符串可以不是文字字符串,并且可以在運行時計算

時間,這對于此類類型檢查方案是不可滲透的。虛構(gòu)的例子:

char *fmt_string = message_lookup(current_language, message_code);

/* no type checking from gcc in this case: fmt_string could have

four conversion specifiers, or ones not matching the types of

arg1, arg2, arg3, without generating any diagnostic. */

snprintf(buffer, sizeof buffer, fmt_string, arg1, arg2, arg3);

scanf如原型int scanf ( const char * format, ... );所述,根據(jù)參數(shù)格式將給定數(shù)據(jù)存儲到附加參數(shù)所指向的位置。

它與編譯器無關(guān),都與為scanf定義的語法有關(guān)。需要參數(shù)格式以使scanf知道要為輸入的數(shù)據(jù)保留的大小。

這是因為這是告訴函數(shù)(例如printf scanf)要傳遞哪種類型的值的唯一方法。例如-

int main()

{

int i=22;

printf("%c",i);

return 0;

}

此代碼將打印字符而不是整數(shù)22,因為您已告訴printf函數(shù)將變量視為char。

因為在printf中您沒有指定數(shù)據(jù)類型,所以您在指定數(shù)據(jù)格式。這在任何語言中都是重要的區(qū)別,在C語言中則是雙重重要的。

使用%s掃描字符串時,并不是說"為我的字符串變量解析字符串輸入"。您不能在C中這么說,因為C沒有字符串類型。 C與字符串變量最接近的是固定大小的字符數(shù)組,該數(shù)組恰巧包含表示字符串的字符,字符串的結(jié)尾由空字符表示。因此,您真正要說的是"這里有一個用于容納字符串的數(shù)組,我保證它足夠大,足以容納要解析的字符串輸入"。

原始?當然。 C是40多年前發(fā)明的,當時一臺典型的機器最多具有64K RAM。在這樣的環(huán)境中,保存RAM比復雜的字符串操作具有更高的優(yōu)先級。

盡管如此,%s掃描程序仍然存在于更高級的編程環(huán)境中,在該環(huán)境中存在字符串數(shù)據(jù)類型。因為是關(guān)于掃描,而不是打字。

不,每個格式說明符都指定所需的參數(shù)類型。 %s需要類型為char*的參數(shù)(必須是指向字符串的指針)。 %d需要類型為int的參數(shù)。 %x和%o require和類型為unsigned int的參數(shù)。依此類推。 %s有點不同,它處理自變量指向的數(shù)據(jù)。哦,NULL不能指示字符串的結(jié)尾;多數(shù)民眾贊成在一個空的指針常量。它由空字符\0表示。

首先,您誤用了" requires"一詞。這意味著如果提供錯誤的數(shù)據(jù)類型,則會出現(xiàn)錯誤。您會收到警告,僅此而已。 %s期望char *,但是如果您提供其他內(nèi)容,您仍然可以編譯。 //雖然您對NULL的看法是正確的,但是我會改變它。

我并不是說"需要"一詞暗示需要進行診斷。實際上,該行為是不確定的,并且提供錯誤的類型是錯誤的。 (它不需要診斷,因為通常這是不可能的;格式字符串不必是字符串文字。)通過提供"%s"或"%d"格式,程序員(您)將指定您提供或int分別作為對應的參數(shù)。

"這意味著如果提供錯誤的數(shù)據(jù)類型,您將得到一個錯誤" –不,不是。這是錯誤的,因此,在大多數(shù)情況下,這是您的答案。

@JimBalter告訴我林錯但不告訴我原因的評論不是很有用。

閱讀C標準以了解有關(guān)程序和實現(xiàn)的要求。僅需要針對約束錯誤提供診斷程序,但是還有許多其他要求,例如,正如Keith正確指出的那樣,%s需要類型為char*的參數(shù)。

當前的問題是"為什么C用這種方式做事?"這與標準無關(guān)-語言在存在標準之前就已經(jīng)存在。如果您想了解一種語言的設計,則需要閱讀其基礎文檔。在這種情況下,Kernighan&Richie是一個不錯的起點。

printf和scanf是I / O函數(shù),其設計和定義為接收控制字符串和參數(shù)列表的方式。

函數(shù)不知道傳遞給它的參數(shù)的類型,并且Compiler也無法將此信息傳遞給它。

-1據(jù)我所知,這并不能回答問題。

總結(jié)

以上是生活随笔為你收集整理的编译器构造c语言描述pdf,关于编译器构造:为什么每次都要在C中指定数据类型?...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。