[英]Use of pointers on strings
我真的很困惑在字符串上使用指針。 感覺他們遵守不同的規則。 請考慮以下代碼
char *ptr = "apple";// perfectly valid here not when declaring afterwards like next ptr = "apple"; // shouldn't it be *ptr = "apple"
printf()
行為也不同 -
printf("%s", ptr) // Why should I send the address instead of the value
我還在一本書中看到了以下代碼
char str[]="Quest"; char *p="Quest"; str++; // error, constant pointer can't change *str='Z'; // works, because pointer is not constant p++; // works, because pointer is not constant *p = 'M'; // error, because string is constant
我無法理解應該暗示什么
請幫忙,我在其他地方找不到任何信息
char *ptr;
ptr = "apple"; // shouldn't it be *ptr = "apple"
不,因為*ptr
會是一個char
。 所以,你可以寫*ptr = 'a'
但你不能按照你的建議寫。
printf("%s", ptr) // Why should I send the address instead of the value
因為C中的字符串是以零結尾的字符序列( char
)的地址(空字符也稱為\\x0
)。
char str[] = "Quest";
char *p = "Quest";
str++; // error, constant pointer can't change
不,指針可以完美地改變,但在這里, str
是一個數組(與指針略有不同)。 但是,因此,它無法處理指針運算。
*str='Z'; // works, because pointer is not constant
不,它有效,因為*str
應該是一個char
。
p++; // works, because pointer is not constant
不,它有效,因為這次是指針(不是數組)。
*p = 'M'; // error, because string is constant
與上面相同,這又是一個char
,所以它起作用,因為它是正確的類型而不是因為字符串是'常量'。 並且,如Michael Walz在評論中所述,即使它可能編譯,它也會在運行時產生未定義的行為(很可能是segfault
崩潰),因為規范不會告訴*p
指向的字符串是否為只讀(或者,似乎大多數現代編譯器實現決定以只讀方式實現)。 這可能會產生一個segfault
。
有關更多信息,請參閱此SO問題 。
當與指針一起使用時,“*”表示獲取指針所指向的內容,例如:
char* ptr;
ptr是一個指向字符的指針,你可以將它分配給一個字符串,如下所示:
const char* ptr = "test";
存儲器中的布局是“t”,后跟“e”,“s”,“t”,最后是nul終結符'\\ 0'。
當您像上面一樣分配它時,它將指針分配給恰好是“t”的第一個內存位置。
* ptr返回ptr指向的內容,並且始終是它聲明的類型的大小,在本例中是“char”,單字節。
*(++ ptr)將返回“e”,因為ptr在返回它現在指向的內容之前遞增到下一個位置。
1-我認為你對變量聲明和定義有些困惑。 這一行:
char *ptr = "apple";
聲明一個指向char的指針,並將第一個字符“a”的地址分配給變量ptr。 這一行相當於以下2:
char* ptr;
ptr = "apple";
現在,C中的字符串文字是只讀的。 它們是隱含的,它和做的一樣
const char* ptr;
因此實際上,您無法更改此指針指向的位置的內容。 現在,即使你可以,你做的方式也是錯的。 因為ptr指向字符串的第一個字符的位置,所以當你執行* ptr時,你正在訪問該字符串的第一個字符的內容。 所以它需要一個char,而不是一個字符串。 所以它會是這樣的: *ptr = 'a'
;
2-嗯,這就是printf的工作方式。 如果要使用%s說明符打印字符串,則需要指向該字符串的指針,該字符串是字符串的第一個字符的地址,而不是字符串的值本身。
3-現在我要評論你的代碼。
str++; // error, constant pointer can't change
你是對的。 其他人一直說數組和指針略有不同,但事實並非如此。 數組只是程序員的一個抽象,表示你存儲的是一系列值。 在裝配級別,沒有任何區別。 你可以說數組是具有可變內容的不可變指針。 數組存儲值序列的第一個元素的地址。 您可以更改數組的內容,但不能更改地址(它指向的第一個元素)。
*str='Z'; // works, because pointer is not constant
現在你正在制造一些混亂。 指針實際上是常量,也就是說,您無法更改它存儲的地址。 但是你可以改變地址指向的內容,這就是上面的行所做的。 它正在改變數組中值序列的第一個值。
p++; // works, because pointer is not constant
正確。 指針不是常量,盡管它指向的內容是。 您可以更改指針存儲的地址,但不能更改它指向的值。 字符串文字是指向不可變字符串的可變指針。
*p = 'M'; // error, because string is constant
正確,字符串是不可變的。
"SOME STRING"
在內存中以\\0
結尾創建一個char序列,並返回其第一個char地址,以便將其分配給指針:
char *ptr = "Hello";
printf
函數也可以使用地址,類型說明符定義它應該如何從內存中讀取數據。
char str[]="Quest"; char *p="Quest";
在第一個你創建一個有6個房間的數組並在其中存儲'Q', 'u', 'e', 's', 't', '\\0'
,然后你可以改變一些索引值str[2] = 'x'
但數組名稱本身是一個常量,它具有指向它指向的第一個位置的地址,所以你不能用str++;
類的東西來改變它str++;
但是在第二個中"Quest\\0"
是一個保存在內存中某個位置的常量字符串,它的第一個內存位置存儲在p
所以你不能改變它,但指針本身不是const
,你可以做p++;
。
我只會回答子問題1.但是你已經觸及了C中頻繁但微妙的混淆,初始化指針的方式與分配給指針之間的輕微不匹配。 仔細觀察。
如果我有一個int
變量,我可以在聲明它時初始化它:
int i = 42;
或者,我可以在一行上聲明它(不初始化它),並在以后給它一個值:
int i;
i = 42;
那里沒有神秘感。 但是當涉及指針時,它看起來有點不同。 同樣,我可以在一行上聲明並初始化:
char *ptr = "apple";
或者我可以拆分聲明和作業:
char *ptr;
ptr = "apple";
但是,一開始看起來很奇怪 - 基於第一種語法,第二種方式不應該像這樣嗎?
*ptr = "apple"; // WRONG
不,它不應該,這就是原因。
ptr
是指向某些字符的指針。 它是在C中引用字符串的一種方式。
*
是指針間接運算符。 在表達式中, *ptr
是指該字符(只是一個字符) ptr
點。 因此,如果我們想要獲取字符串的第一個字符,我們可以使用*
來做到這一點:
printf("first character: %c\n", *ptr);
請注意,此printf
調用中的格式使用%c
,因為它只打印一個字符。
我們也可以指定指針。 如果我們使用指向char
指針,並且如果我們因此將這些指針視為“字符串”,這是在C中進行字符串賦值的一種方法。如果我說
ptr = "apple";
然后無論ptr
用於指向哪里,現在它指向包含字符串“apple”的字符數組。 如果我后來說
ptr = "pear";
然后ptr
不再指向字符串“apple”; 現在它指向包含字符串“pear”的不同字符數組。 您可以將此指針視為一次性分配字符串的所有字符(盡管實際上並不是它正在執行的操作)。
因此,如果*ptr
只訪問一個字符,而ptr
是指針值本身,那么為什么第一個形式
char *ptr = "apple";
工作?
答案是,當你說
char *ptr = "apple";
在那里顯示的*
沒有指針 - 間接運算符。 這並不是說我們試圖訪問任何東西的第一個角色。
當你說
char *ptr = "apple";
*
表示ptr
是一個指針。 這就像你說的那樣
char *ptr;
*
表示ptr
是一個指針。
指針的C'聲明語法有點奇怪。 以下是如何思考它。 語法是
type-name thing-that-that-type ;
所以當我們說
char *ptr;
type-name是char
,而thing-that-that-type是*ptr
。 我們說*ptr
將是一個char
。 如果*ptr
將是一個char
,這意味着ptr
必須是一個指向char
的指針。
然后,當我們說
char *ptr = "apple";
我們說ptr
(我們剛才說的是一個指向char
的指針)應該將指向包含字符串“apple”的數組的指針作為其初始值。
ptr = "apple"; // shouldn't it be *ptr = "apple"
從頭開始......
字符串文字 "apple"
存儲在一個6元素的char
數組中,如下所示:
+---+---+---+---+---+---+
|'a'|'p'|'p'|'l'|'e'| 0 |
+---+---+---+---+---+---+
尾部0
表示字符串的結尾(稱為字符串終止符 )。
當表達式中出現“N元素數組T
”的表達式時,它將被轉換(“衰減”)為“指向T
指針”類型的表達式,表達式的值將是第一個的表達式數組的元素, 除非數組表達式是sizeof
或一元&
運算符的操作數,或者用於初始化聲明中的字符數組。
因此,在聲明中
ptr = "apple";
表達式"apple"
被轉換(“decays”)從類型為“6-element array of char
”的表達式轉換為“指向char
指針”。 表達式ptr
的類型是char *
,或“指向char
指針”; 因此,在上面的賦值中, ptr
將接收"apple"
的第一個元素的地址。
它不應該寫成
*ptr = "apple";
因為表達式*ptr
計算的是ptr
指向的東西的值,此時a)是不確定的,b)分配的錯誤類型。 表達式*ptr
的類型是char
,它與char *
不兼容。
我寫了一個實用程序,打印內存中的項目映射; 鑒於代碼
char *ptr = "apple";
char arr[] = "apple";
地圖看起來像這樣:
Item Address 00 01 02 03
---- ------- -- -- -- --
apple 0x400c80 61 70 70 6c appl
0x400c84 65 00 70 74 e.pt
ptr 0x7fffcb4d4518 80 0c 40 00 ..@.
0x7fffcb4d451c 00 00 00 00 ....
arr 0x7fffcb4d4510 61 70 70 6c appl
0x7fffcb4d4514 65 00 00 00 e...
字符串文字"apple"
位於地址0x400c80
1 。 變量ptr
和arr
現場的地址0x7fffcb4d4518
和0x7fffcb4d4510
,分別為2。
變量ptr
包含值0x400c80
,它是"apple"
字符串文字的第一個元素的地址(x86以“little-endian”順序存儲多字節值,因此最不重要的字節首先出現,這意味着你有從右到左閱讀。
還記得上面的“除外”條款嗎? 在第二個聲明中,字符串文字"apple"
用於初始化聲明中的char
數組 ; 而不是轉換為指針值,字符串文字的內容被復制到數組,您可以在內存轉儲中看到。
printf("%s", ptr) // Why should I send the address instead of the value
因為這是%s
轉換說明符所期望的 - 它接受指向0終止字符串的第一個字符的指針,並將打印出從該位置開始的字符序列,直到它看到終結符。
3 ......我無法理解應該暗示什么
您無法更改數組對象的值。 讓我們來看看str
在內存中會是什么樣子:
+---+
str: |'Q'| str[0]
+---+
|'u'| str[1]
+---+
|'e'| str[2]
+---+
|'s'| str[3]
+---+
|'t'| str[4]
+---+
| 0 | str[5]
+---+
你可以寫入每個str[i]
3 (改變它的值),但你不能寫入str
因為沒有什么可寫的 。 沒有str
對象與數組元素分開。 即使表達式 str
將“衰減”到指針值,也不會為該指針的任何位置留出存儲 - 轉換在編譯時完成。
類似地,嘗試修改字符串文字的內容會調用未定義的行為 4 ; 您可能會遇到段錯誤, 或者您的代碼可能會按預期工作, 或者您最終可能會在列支敦士登發布核武器。 所以你不能寫*p
或p[i]
; 但是,您可以將新值寫入p
,將其指向其他位置。
0x0000000000400c80
; %p
說明符會丟棄前導零。 0x000000007fffcb4d4518
和0x000000007fffcb4d4510
。 請注意,特定地址值將從運行更改為運行。 *str
相當於str[0]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.