簡體   English   中英

如何將字符串輸入到字符指針數組?

[英]How is it possible to input a string into an array of pointers to character?

我的問題是如何將字符串輸入字符指針數組? 是在這里動態分配內存嗎? 實際上存儲在數組“ name ”中的是什么?

char *name[20];
printf("Enter a string:");
scanf("%s",name);
printf("%s",name);

此代碼可以正常工作。 它打印我輸入的字符串。 如何將char *name[20]char name[20]一樣對待?

是在這里動態分配內存嗎?

沒有。

此代碼可以正常工作。 它打印我輸入的字符串。 像char name [20]一樣如何對待char * name [20]?

該程序的行為是不確定的。

它是未定義的,因為它違反了scanfprintf函數的要求。

C標准對scanfprintf都使用%s說明符(引用標准草案文檔N1570):

相應的參數應為指向字符數組初始元素的指針...

name 不是指向字符數組初始元素的指針。 它是(實際上是一個數組,但是)衰減后的指針,它是指向字符的指針數組中指向字符的初始指針。 因此違反了要求,並且未定義程序的行為。

您所說的“行為未定義”是什么意思?

這意味着不能保證程序的行為。 就語言而言,該程序可能會:

  • 產生您期望的輸出。
  • 產生意想不到的輸出。
  • 產生您想要產生的輸出。
  • 產生一些您不想要的輸出。
  • 完全不產生輸出。
  • 崩潰
  • 不崩潰
  • 在另一個系統上的行為有所不同。
  • 在同一系統上的行為不同。
  • 調試時的行為有所不同。
  • 僅在度假時有不同的舉止。
  • 由於任何可能的原因而有所不同。
  • 似乎毫無理由地表現不同。
  • 始終表現相同
  • 行為與未使用char name[20]完全相同。
  • 不會那樣。
  • 有任何行為。

避免未定義的行為。

scanfprintf需要一個字符數組,但是您給它提供了一個指針數組。 但是,它們無法識別出差異,因為它們是可變的。 這將導致不確定的行為,因此一切皆有可能。

可能發生的情況是scanf只是將字符寫入指針數組的內存中,而printf將數據視為字符,因為這兩個函數都不知道您提供給它的內存塊應該存儲指針而不是字符。 打印第二個元素會給您第五個字符,因為您的系統有四個字節指針,因此第二個指針元素從第五個字節開始,因此從第五個字符開始。

同樣,您的代碼表現出未定義的行為,因此上一段僅是推測。 標准並不能保證所有這些,您永遠都不應依賴它。

盡管eerorika的答案是完全正確的,但從我的感覺來看,OP可能需要對這里發生的事情進行更詳細的解釋……

char * name[20]內容不是 指向20個字符數組 指向內存的20個指針的數組 (每個指針都被視為指向一個字符或一個字符數組)。 聲明中的[20]已經指定您需要20個元素,並且前面的內容(在您的情況下為char * )指定這些元素是什么。 因此,正確的聲明(在您的上下文中)只是char name[20]

現在,編譯器看到您需要一個20個字符的內存塊,並為您保留了內存(在堆棧上,此處沒有動態分配)。 然后, char name[20]是一個連續的保留內存塊,可以容納20個字符。

您可以通過&name[0]或簡單地name獲得此塊開頭的地址。 后者僅是因為靜態數組變量(在您的情況下為20個字符的整個塊)可以隱式地轉換 (不等同於)指向該存儲塊開頭的指針,即char * (如果您覺得自己不明白這最后一條陳述,可以咨詢一下此短文或google,例如,有關c中的char數組和char指針之間的區別

以下內容說明了正確的代碼會發生什么,以及您的代碼會發生什么,為什么它是未定義行為(UB),以及為什么它在您的情況下不能正常工作。

使用正確的聲明char name[20]您將獲得

char [20]內存塊未初始化

?character表示尚未指定緩沖區每個單元中的內容,但編譯器將其視為字符。

當您將其傳遞給scanf並且用戶輸入“ HelloKitty”時,此答案將被填充到編譯器保留的char [20]內存塊中。

char [20]內存塊傳遞給scanf之后

那就是你想要的。

現在,這里是您使用char * name[20]聲明實際得到的結果。 剛開始時情況就是這樣

char * [20]內存塊未初始化

?pointer表示尚未指定緩沖區每個單元中的內容,但是編譯器將其視為指向character指針

當您將其傳遞給scanf並且用戶輸入其“ HelloKitty”時,該答案將被填充到編譯器保留的char * [20]內存塊中,如前所述。

現在需要更多關注。 這僅由於scanf不是類型安全的事實才有可能。 根據其format specifiers指定的內容,它將獲取它得到的任何地址(在您的情況下, name[0]的地址)並粗心地填寫它。 因此,它很樂意將字符數組填充到應該包含Poiners數組的內存塊中。 現在,每個字符都確實具有二進制表示形式。 因此,在char * [20]發生的事情是, char * [20]內存塊將用代表字符串“ HelloKitty”(直到該字符串的長度)的“零和一”填充。

char * [20]內存塊(傳遞給scanf之后)

?!pointer表示編譯器仍將內容視為字符指針 ,但是該指針的值(從技術上講是指向地址)已通過調用scanf重寫。 (注意:即使“HelloKitty的”包含11個字符,包括終止NUL,不11細胞,因為重寫sizeof每個小區等於sizeof的指針的典型。 sizeof指針的是4倍的char上的32位體系結構,因此,只有2單元格和第三個單元格的一部分將被重寫)

現在為什么一切對您都很好,只是因為printf和它的姐妹scanf一樣,對它的實際類型一無所知。 因此, printf接受name[0]的地址,並將其“零和一”(返回)解釋為“ HelloKitty”字符串。

實際上,只要您僅在printfscanf (或類似函數)之間傳遞name變量,您的程序就不可能調用UB(見下文)。 保留給char * name[20]的存儲塊的(二進制)內容在某種程度上(請參見下文)與“ HelloKitty”字符串的(二進制表示)等效。 只要scanf讀取的內容不超過提供給它的存儲塊,該程序就不可能(以下列方式)被破壞。 您可能也可以這樣寫int i; scanf("%s",&i); printf("%s",&i); int i; scanf("%s",&i); printf("%s",&i); 只要用戶輸入的字符數少於3個,該程序就可以正常運行。 (假設sizeof int為4個chars

從技術上講,您的代碼屬於UB。 這就是標准所說的。 該標准無法解決“程序員編寫怪異代碼時代碼才能真正起作用”的所有情況。 是的,在scanf中將char * [20]視為char [20]是“奇怪的事情”。 同時,通過指定UB,該標准為編譯器實現者提供了一定的自由度。 如果要編寫編譯器,則可以決定在scanf編譯時解析格式,並生成一組特殊的指令,這些指令依賴於為name提供了正確類型的事實(這一事實我很難想象-至少對於%s切換器-不會更改任何內容)。 所以程序是UB。 期。 :)

ps。>考慮在代碼中使用scanf("%20s", name) 這將防止scanf讀取超出存儲塊長度的字符,這將導致另一個UB。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM