[英]How strcpy changes the value of string
我得到了這個作業來決定下面的代碼將做什么(在紙上,沒有在計算機上測試)。
char s1[]="Short Message Service", *s2, *s3;
s2=strchr(s1,'M');
s3=strchr(s2,'S');
strncpy(s1+1,s2,1);
strcpy(s1+2,s3);
當我想檢查是否做對時,我在計算機上運行它並得到以下結果:
s1 = SMservice
s2 = ice
s3 = Service
我以為s2
將是"Message Service"
但它變成了"ice"
。 顯然它在strcpy(s1+2,s3)
后發生了變化; 有人可以解釋 function 為什么以及如何影響s2
嗎?
答案是“未定義的行為”——任何事情都可能發生。 arguments 到strcpy()
和strncpy()
不得重疊。 — 然而在這里,arguments 到strcpy()
確實重疊。
C11 §7.24.2.3 strcpy
function ¶2 :
strcpy
function 將s2
指向的字符串(包括終止 null 字符)復制到s1
指向的數組中。 如果復制發生在重疊的對象之間,則行為未定義。
strncpy
function 從s2
指向的數組復制不超過n
字符(不復制 null 字符之后的字符)到s1
指向的數組。 308)如果復制發生在重疊的對象之間,則行為未定義。308)因此,如果
s2
指向的數組的前n
字符中沒有 null 字符,則結果不會以空值結尾。
這意味着沒有可靠的答案可以給出。 您可能會決定然后描述如果復制操作從源的開頭復制到目標會發生什么,這可能是您的講師所期望的。 但這不是保證的行為。
給定以下代碼和從左到右的復制假設:
char s1[] = "Short Message Service";
char *s2 = strchr(s1, 'M');
char *s3 = strrchr(s2, 'S');
strncpy(s1+1, s2, 1);
strcpy(s1+2, s3);
我們可以推斷出s2
指向&s1[6]
和s3
指向&s1[14]
(這是強制性的)。 s1
在各個階段的值是:
s1 = "Short Message Service" -- initial text
s1 = "SMort Message Service" -- after strncpy
s1 = "SMService" -- after strcpy (but this assumes UB works as expected)
因此,如您所見,從s2
開始的字符串現在包含ice
。
但是,必須再次強調,這不是必需的行為。
其他答案已經告訴了你一個苦澀的事實:使用strncpy
和strcpy
復制重疊的字符串是未定義的行為,應該避免,尤其是在涉及更復雜的格式時(對於sprintf
等函數也是如此)。
無論如何,您可以通過逐步分析您的代碼來解釋您所看到的內容。 我想再次強調,當存在未定義的行為時,任何編譯器都可以選擇不同的行為,所以我們不能確定這是一個普遍的解釋。
需要考慮的重要一點是所有指針共享相同的 memory 位置。 s1
初始化后
char s1[]="Short Message Service", *s2, *s3;
它指向的 char 數組如下所示:
----------------------------------------------
|S|h|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e|\0|
----------------------------------------------
^
s1
然后在第二個和第三個單詞的開頭設置s2
和s3
:
s2=strchr(s1,'M');
s3=strrchr(s2,'S');
這里三個指針是如何定位的
----------------------------------------------
|S|h|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e|\0|
----------------------------------------------
^ ^ ^
s1 s2 s3
由於每個字符串實際上都是從相應指針到第一個終止符的數組,因此如果您打印您看到的三個字符串:
s1: "Short Message Service"
s2: "Message Service"
s3: "Service"
然后在s1
的第一個字符之后復制s2
的一個字符:
strncpy(s1+1,s2,1);
請注意,當源字符串長於傳遞給 strncpy的最大長度時,不會復制字符串終止符。 數組將如下所示:
----------------------------------------------
|S|M|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e|\0|
----------------------------------------------
^ ^ ^
s1 s2 s3
打印字符串不會有太大變化: s1
剛剛變成了"Short Message Service"
。 最后你使用
strcpy(s1+2,s3);
-----------------------------------------------
|S|M|S|e|r|v|i|c|e|\0|a|g|e| |S|e|r|v|i|c|e|\0|
-----------------------------------------------
^ ^ ^
s1 s2 s3
這就是為什么你得到
由於每個字符串實際上都是從相應指針到第一個終止符的數組,因此如果您打印您看到的三個字符串:
s1: "SMService"
s2: "ice" // Because of the terminator in the middle
s3: "Service" // The original string ending
如果你需要一個指向每個單詞的指針,你只需要存儲單詞的開頭,就像你已經做的那樣,然后在每個空格的 position 中放置一個字符串終止符。
這樣, s1
將是"Short"
(因為將在第一個空格所在的位置找到終止符), s2
將是"Message"
(因為將在第二個空格所在的位置找到終止符), s3
將是"Service"
(因為原來的終結者)。
順便說一句:這就是strtok
所做的:找到一個標記的出現,在其中放置一個字符串終止符並返回指針經過它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.