簡體   English   中英

在字符串上使用指針

[英]Use of pointers on strings

我真的很困惑在字符串上使用指針。 感覺他們遵守不同的規則。 請考慮以下代碼

  1.  char *ptr = "apple";// perfectly valid here not when declaring afterwards like next ptr = "apple"; // shouldn't it be *ptr = "apple" 
  2. printf()行為也不同 -

     printf("%s", ptr) // Why should I send the address instead of the value 
  3. 我還在一本書中看到了以下代碼

     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

正確,字符串是不可變的。

  1. "SOME STRING"在內存中以\\0結尾創建一個char序列,並返回其第一個char地址,以便將其分配給指針:
    char *ptr = "Hello";

  2. printf函數也可以使用地址,類型說明符定義它應該如何從內存中讀取數據。

  3. 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-namechar ,而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 變量ptrarr現場的地址0x7fffcb4d45180x7fffcb4d4510 ,分別為2。

變量ptr包含值0x400c80 ,它是"apple"字符串文字的第一個元素的地址(x86以“little-endian”順序存儲多字節值,因此最不重要的字節首先出現,這意味着你有從右到左閱讀。

還記得上面的“除外”條款嗎? 在第二個聲明中,字符串文字"apple"用於初始化聲明中的char 數組 ; 而不是轉換為指針值,字符串文字的內容被復制到數組,您可以在內存轉儲中看到。

  1. 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 ; 您可能會遇到段錯誤, 或者您的代碼可能會按預期工作, 或者您最終可能會在列支敦士登發布核武器。 所以你不能寫*pp[i] ; 但是,您可以將新值寫入p ,將其指向其他位置。


  1. 從技術上講,它是0x0000000000400c80 ; %p說明符會丟棄前導零。
  2. 同樣的交易 - 技術上,值為0x000000007fffcb4d45180x000000007fffcb4d4510 請注意,特定地址值將從運行更改為運行。
  3. *str相當於str[0]
  4. C語言定義識別某些錯誤的操作,但不對編譯器提出任何以任何特定方式處理該代碼的要求。 不同的平台以不同的方式存儲字符串文字; 有些將它們放在只讀內存中,因此嘗試修改它們會導致段錯誤,而其他平台會將它們存儲在可寫段中,以便操作成功。 有些人可能會以不會出現段錯誤的方式存儲它們,但字符串不會更改。

暫無
暫無

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

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