簡體   English   中英

如何理解字符串變量? 它們同時是字符串和指針嗎?

[英]How to understand string variables? Are they strings and pointers simultaneously?

#include <stdio.h>
#include <string.h>

int main(int argc, const char * argv[]) {
    char str[] = "hello";
    printf("%s, %p", str, str);
    return 0;
}

上面的代碼給出了輸出

你好,0x7fff5fbff7aa

令我困惑的是,為什么str可以同時是一個字符串和一個指針? 我知道字符串是指向char的指針 所以我認為str只是一個pointer

但是,編譯器如何知道%s提供了str指向的字符串?

編譯器如何工作?

PS

我想當把%c%i用作char變量並得到不同的輸出時,也會發生同樣的情況。

在此代碼中, str是一個數組。 數組和指針是不同的。 您可以創建一個指向數組元素的指針。

在代碼中printf("%s, %p", str, str); str兩種用法實際上都要求一個指向數組第一個元素的指針。 您可以寫&str[0]表示相同的意思,但這是從C語言開始的一項設計決策,即在大多數情況下寫數組的名稱實際上會要求這樣的指針。

定義printf函數,以便在看到%s時跟隨(取消引用)相應的指針並打印出字符,直到到達空終止符為止。 如果看到%p則會打印出指針本身的某種表示形式(不是指針指向的內容)。

char str[] = "hello";

如果您將str視為標識符,則它是一個字符數組

數組和指針的行為不同,例如,

sizeof(array);
// would give you the sizeof(type of array)*total elements in array
sizeof(pointer);
// would give you just the size of the pointer in your system , say 8 bytes

但是,當將數組傳遞給函數時,它 decays to pointer數組第一個元素的decays to pointer ,如下所示:

printf("%s, %p", str, str);
// same as printf("%s, %p", &str[0], &str[0]);

在這里,盡管str是一個數組,但str視為指向數組第一個元素即&str[0]的指針。

您得到不同的結果僅僅是因為您使用了不同的格式說明符,即分別用於確定應如何打印內容的%s%p

這是printf函數,用於使用相同的標識符str打印不同的值。

%s%p是格式說明符。 對於%sprintf將從str指定的地址開始打印一個字符串。 對於%pprintf將打印str給定的內存地址。

編譯器在這里沒有特殊作用。 但是,如果格式說明符和相應的參數之間類型不匹配,它會警告您。 例如,如果您執行printf("%s",10); 它可以警告這種不匹配,說printf期望為%schar * -但您傳遞的是int

PS :請注意, str是char數組-與char指針不同-但是,當將其傳遞給函數時,它將成為指向其第一個元素地址的char指針( char * )。

在這個領域中,新的C程序員很難用數組來結交朋友 什么是指針? 以及如何在函數調用中將數組作為參數傳遞時將數組轉換為指針 通過了解一些簡單的適用知識,可以輕松理解所有內容。

首先,任何變量(例如a = 5; ),其中a容納一個int 的地址 ,而組成該內存的字節數則保留5立即值。 因此,當您分配a = 5; ,將變量標簽a指向的內存設置為5 這里的關鍵是,從這個意義上說,所有變量可能都指向內存中的某個位置。 此處的區別是位於普通變量所指向的內存中的位置,普通變量所指向的內存中包含一些立即值 (此處為5 ),而對於指針, 指針變量指向的是內部變量的地址。可以找到其他地址的內存。 (例如, 指針只是指向某個類型的地址而不是某個值的變量 )。

經典的例子值得再次研究

int a = 5;    /* 'a' stores a memory address holding the value '5' as its value  */

int *pa = &a; /* pointer-to-a 'pa' stores the memory address of 'a' as its value */

應用於任何數組,數組的第一個元素是整個數組的起始地址。 您可以這樣想:

int a[] = { 1, 2, 3, 4 }; /* where &a[0] is the memory address for the array,
                             a[i] = *(a + i), thus &(*(a + 0)), is simply 'a' */

因此,在您的情況下,使用char str[] = "hello"; ,則字符數組的第一個元素(根據其初始化方式(例如char array[] = "stuff"; ))將包含字符之間由nul終止的字符數組的第一個字符的地址,初始化程序的"打開和關閉"

數組的第一個元素位於整個數組的地址處,可以由array[0]進行引用,如上所述,該元素等效於*(array + 0)或只是*array ,然后使用獲取array &(*array) 地址urnary &運算符就是array

這就是為什么您既可以將str作為array本身( 第一個元素地址 )傳遞給格式說明符 %s來輸出為字符串 ,又為什么可以將str傳遞給格式說明符 %p來輸出指針地址。

希望您的問題在這一點上得到了回答,但是...

這也有助於理解為什么當字符數組 str作為函數參數列表中的參數傳遞時, str (或任何數組類型)作為指針傳遞(即,任何數組中的間接尋址的第一級都被轉換為指針作為函數的參數傳遞時)數組作為指針傳遞的歷史原因僅與節省內存有關。 無需傳遞數組中所有元素的副本,只需要引用第一個元素的地址即可。

在C語言中, 字符串是字符值的序列,后跟一個0值的終止符。 例如,字符序列{'H', 'e', 'l', 'l', 'o', 0}是字符串,但是{'H', 'e', 'l', 'l', 'o'}不是-0終止符會有所不同。

字符串(包括字符串文字) 存儲char數組。 給出聲明

char str[] = "Hello";

你得到類似的東西

     +---+
str: |'H'| str[0]
     +---+ 
     |'e'| str[1]
     +---+ 
     |'l'| str[2]
     +---+
     |'l'| str[3]
     +---+
     |'o'| str[4]
     +---+
     | 0 | str[5]
     +---+

在記憶中。 請注意,未為指向數組第一個元素的指針預留任何存儲空間。

在大多數情況下,類型“ N的T元素數組”的表達式將被轉換(“衰變”)為類型“指向T指針”,該表達式的值將是T的第一個元素的地址。陣列。 該規則的例外情況是:數組表達式是sizeof或一元&運算符的操作數,或者表達式是用於在聲明中初始化數組的字符串文字。

因此,讓我們采用以下代碼:

char str[] = "Hello";
char *ptr  = "World";

printf( "%s, %s\n", str, ptr );

字符串文字 "Hello""World""%s, %s\\n"存儲為char數組,以便在程序啟動時分配它們,並在程序的整個生命周期內可用。

"Hello""World""%s, %s\\n"str都是數組表達式 (它們的類型均為“ N個元素的char型數組”)。 ptr的聲明中, "World"數組表達式不是sizeof或一元&運算符的操作數,也不是用於初始化char數組的,因此表達式被轉換(“衰變”)以鍵入“指針” to char ”,表達式的值是數組第一個元素的地址,因此ptr指向"World"的第一個字符。

同樣,在printf調用中,數組表達式"%s, %s\\n"str不是sizeof或一元&運算符的操作數,因此它們也將轉換為指針表達式,而這些指針值實際上就是傳遞給printf

然而,在聲明str中, "Hello"字符串文字用於初始化數組char ,因此它被轉換為指針表達; 相反, str是用字符串文字的內容初始化的,其大小也由文字的大小決定。

這是上面在系統上生成的代碼的具體內存映射:

       Item        Address   00   01   02   03
       ----        -------   --   --   --   --
    "Hello"       0x400b91   48   65   6c   6c    Hell
                  0x400b95   6f   00   30   30    o.00

    "World"       0x400b60   57   6f   72   6c    Worl
                  0x400b64   64   00   25   73    d.%s

 "%s, %s\n"       0x400b66   25   73   2c   20    %s,.
                  0x400b6a   25   73   0a   00    %s..

        str 0x7fff7cec1a50   48   65   6c   6c    Hell
            0x7fff7cec1a54   6f   00   00   00    o...

        ptr 0x7fff7cec1a48   60   0b   40   00    `.@.
            0x7fff7cec1a4c   00   00   00   00    ....

字符串文字"Hello"從地址0x400b91開始存儲, "World"從地址0x400b60開始存儲,格式字符串"%s, %s\\n"從地址0x400b66開始存儲(出於某種原因,編譯器將"World""%s, %s\\n"彼此相鄰)。

數組str從地址0x7fff7cec1a50開始存儲,它包含字符串文字"Hello"的內容的副本 指針ptr從地址0x7fff7cec1a48開始存儲,並包含字符串文字"World"地址 (x86以低字節序存儲指針之類的多字節值)。

printf通話將收到指針0x400b660x7fff7cec1a500x7fff7cec1a48 格式字符串中的%s轉換說明符顯示為“打印從地址開始的字符序列,並持續到我看到0終止符為止”。

暫無
暫無

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

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