![](/img/trans.png)
[英]How do I correctly store a string input in to character array using pointers?
[英]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]?
該程序的行為是不確定的。
它是未定義的,因為它違反了scanf
和printf
函數的要求。
C標准對scanf
和printf
都使用%s
說明符(引用標准草案文檔N1570):
相應的參數應為指向字符數組初始元素的指針...
name
不是指向字符數組初始元素的指針。 它是(實際上是一個數組,但是)衰減后的指針,它是指向字符的指針數組中指向字符的初始指針。 因此違反了要求,並且未定義程序的行為。
您所說的“行為未定義”是什么意思?
這意味着不能保證程序的行為。 就語言而言,該程序可能會:
char name[20]
完全相同。 避免未定義的行為。
scanf
和printf
需要一個字符數組,但是您給它提供了一個指針數組。 但是,它們無法識別出差異,因為它們是可變的。 這將導致不確定的行為,因此一切皆有可能。
可能發生的情況是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]
您將獲得
?character
表示尚未指定緩沖區每個單元中的內容,但編譯器將其視為字符。
當您將其傳遞給scanf
並且用戶輸入“ HelloKitty”時,此答案將被填充到編譯器保留的char [20]
內存塊中。
那就是你想要的。
現在,這里是您使用char * name[20]
聲明實際得到的結果。 剛開始時情況就是這樣
?pointer
表示尚未指定緩沖區每個單元中的內容,但是編譯器將其視為指向character的指針 。
當您將其傳遞給scanf
並且用戶輸入其“ HelloKitty”時,該答案將被填充到編譯器保留的char * [20]
內存塊中,如前所述。
現在需要更多關注。 這僅由於scanf
不是類型安全的事實才有可能。 根據其format specifiers
指定的內容,它將獲取它得到的任何地址(在您的情況下, name[0]
的地址)並粗心地填寫它。 因此,它很樂意將字符數組填充到應該包含Poiners數組的內存塊中。 現在,每個字符都確實具有二進制表示形式。 因此,在char * [20]
發生的事情是, char * [20]
內存塊將用代表字符串“ HelloKitty”(直到該字符串的長度)的“零和一”填充。
?!pointer
表示編譯器仍將內容視為字符的指針 ,但是該指針的值(從技術上講是指向地址)已通過調用scanf
重寫。 (注意:即使“HelloKitty的”包含11個字符,包括終止NUL,不11細胞,因為重寫sizeof
每個小區等於sizeof
的指針的典型。 sizeof
指針的是4倍的char
上的32位體系結構,因此,只有2單元格和第三個單元格的一部分將被重寫)
現在為什么一切對您都很好,只是因為printf
和它的姐妹scanf
一樣,對它的實際類型一無所知。 因此, printf
接受name[0]
的地址,並將其“零和一”(返回)解釋為“ HelloKitty”字符串。
實際上,只要您僅在printf
和scanf
(或類似函數)之間傳遞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.