[英]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
是格式說明符。 對於%s
, printf
將從str
指定的地址開始打印一個字符串。 對於%p
, printf
將打印str
給定的內存地址。
編譯器在這里沒有特殊作用。 但是,如果格式說明符和相應的參數之間類型不匹配,它會警告您。 例如,如果您執行printf("%s",10);
它可以警告這種不匹配,說printf
期望為%s
的char *
-但您傳遞的是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
通話將收到指針值0x400b66
, 0x7fff7cec1a50
和0x7fff7cec1a48
。 格式字符串中的%s
轉換說明符顯示為“打印從地址開始的字符序列,並持續到我看到0終止符為止”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.