簡體   English   中英

C中數組,指針和字符串之間的差異

[英]Differences between arrays, pointers and strings in C

請記住一個讓我煩惱的問題。

我知道pointersarrays在C中是不同的,因為pointers存儲addressarrays存儲'real' values

但是當談到string時我會感到困惑。

char *string = "String";

我讀到這行做了幾件事:

編譯器創建一個字符數組,它的值為String

然后,該數組被視為pointer ,程序將指針分配給指針字符串,該指針指向編譯器創建的數組的first element

這意味着, arrays被視為pointers

那么,這個結論是真是假,為什么呢?

如果為false,那么pointersarrays之間的區別是什么? 謝謝。

指針包含對象的地址(或者是指向任何對象的空指針)。 指針具有特定類型,指示它可以指向的對象類型。

數組是連續的有序元素序列; 每個元素都是一個對象,並且數組的所有元素都是相同的類型。

字符串定義為“由第一個空字符終止並包括第一個空字符的連續字符序列”。 C沒有字符串類型。 字符串是數據布局,而不是數據類型。

數組和指針之間的關系可能令人困惑。 我所知道的最好的解釋是comp.lang.c FAQ的第6節給出的。 要記住的最重要的事情是數組不是指針

在C和C ++中,數組在某種意義上是“二等公民”。 它們不能被賦值,作為函數參數傳遞,或者進行相等性比較。 操作數組的代碼通常使用指向數組的各個元素的指針來執行此操作,並使用一些顯式機制來指定數組的長度。

混淆的主要原因是數組類型的表達式(例如數組對象的名稱)在大多數上下文中被隱式轉換為指針值。 轉換后的指針指向數組的初始(第零個)元素。 如果數組是以下情況,則不會發生此轉換:

  • sizeof的操作數( sizeof array_object產生數組的大小,而不是指針的大小);
  • 一元&&array_object的操作數產生整個數組對象的地址); 要么
  • 初始值設定項中的字符串文字,用於初始化數組對象。

    char * string =“String”;

為避免混淆,我將在您的示例中進行一些更改:

const char *ptr = "hello";

字符串文字"hello"創建一個char[6] (在C中)或const char[6] (在C ++中)的匿名對象,包含字符{ 'h', 'e', 'l', 'l', 'o', '\\0' }

在此上下文中,對該表達式的求值產生指向該數組的初始字符的指針。 這是一個指針 ; 沒有隱式創建的指針對象。 該指針值用於初始化指針對象ptr

任何時候都不會將數組“視為”指針。 數組表達式轉換為指針類型。

混淆的另一個原因是看起來是數組類型的函數參數實際上是指針類型; 在編譯時調整類型。 例如,這個:

void func(char param[10]);

真正意思:

void func(char *param);

10被默默地忽略了。 所以你可以這樣寫:

void print_string(char s[]) {
    printf("The string is \"%s\"\n", s);
}
// ...
print_string("hello");

看起來就像操縱數組一樣,但實際上數組"hello"被轉換為指針,而指針就是傳遞給print_string函數的指針。

那么,這個結論是真是假,為什么呢?

你的結論是錯誤的。

數組和指針是不同的。 comp.lang.c FAQ列表·問題6.8解釋了數組和指針之間的區別:

數組是一個預先分配的單個連續元素(所有相同類型),大小和位置都是固定的。 指針是對任何數據元素(特定類型)的引用 必須指定一個指針指向其他地方分配的空間,但是可以隨時重新分配它(並且可以調整空間,如果從malloc派生的話,可以調整大小)。 指針可以指向一個數組,並且可以模擬(與malloc一起)一個動態分配的數組,但指針是一個更通用的數據結構。

當你這樣做

char *string = "String";  

當C編譯器遇到這個時,它為字符串文字String留出7個字節的內存。 然后將指針string設置為指向已分配內存的起始位置。

在此輸入圖像描述

當你申報時

char string[] = "String";    

當C編譯器遇到這個時,它為字符串文字String留出7個字節的內存。 然后給出該內存位置的名稱,即第一個字節, string

在此輸入圖像描述

所以,

  • 在第一種情況下, string是一個指針變量,在第二種情況下,它是一個數組名稱。
  • 存儲在第一種情況下的字符不能被修改,而在數組版本中它可以被修改。

這意味着數組不被視為C中的指針,但它們在指針算術和數組索引在C中是等效的意義上密切相關,指針和數組是不同的。

我們可以創建一個名為'string'的數組

char string[] = "Hello";

我們可以分配一個指向該字符串的指針

char* stringPtr = string;

數組名稱將轉換為指針

因此,數組名稱與指針類似。 但是,它們並不相同,因為數組是連續的內存塊,而指針只引用內存中的單個位置(地址)。

char *string = "String";

此聲明創建數組並設置指向用於存儲數組的內存塊的指針的地址。

這意味着,數組被視為指針。 那么,這個結論是真是假

錯誤,數組不是指針。 但是,只是為了混淆(!),指針可能看起來像是數組,因為解引用運算符[]

char *string = "String";
char letter = string[2];

在這種情況下string [2] ,首先將字符串轉換為指向數組第一個字符的指針,並使用指針算法,返回相關項。

你必須要了解內存中發生的事情。

字符串是連續的內存單元塊,以特殊值(空終止符)終止。 如果你知道這個內存塊的開始,並且你知道它的結束位置(通過被告知存儲器單元的數量或者通過讀取它們直到你到達null),那么你很高興。

指針只不過是存儲塊的開始,它是第一個存儲單元的地址,或者是指向第一個元素的指針。 所有這些術語都意味着同樣的事情。 它就像電子表格中的單元格引用一樣,如果你有一個巨大的網格,你可以通過它的XY坐標告訴一個特定的單元格,所以單元格B5告訴你一個特定的單元格。 在計算機術語(而不是電子表格)中,內存實際上是一個非常非常長的單元格列表,如果您願意,則是一維電子表格,單元格引用看起來像0x12345678而不是B5。

最后一點是了解計算機程序是一個由OS加載到內存中的數據塊,編譯器會找出相對於程序開頭的字符串的位置,這樣你就自動知道哪個內存塊它位於。

這與在堆上分配一塊內存(它只是巨大內存空間的另一部分)或堆棧(同樣是為本地分配保留的一塊內存)完全相同。 您擁有字符串所在的第一個內存位置的地址。

所以

char* mystring = "string";

char mystring[7];
copy_some_memory(mystring, "string", 7);

char* mystring = new char(7);
copy_some_memory(mystring, "string", 7);

都是一樣的。 mystring是第一個字節的內存位置,包含值's'。 語言可能會使它們看起來不同,但這只是語法。 所以數組是一個指針,只是語言使它看起來不同,你可以使用稍微不同的語法對其進行操作,以使其操作更安全。

(注意:第一個和其他示例之間的最大區別在於編譯器集數據集是只讀的。如果您可以更改該字符串數據,則可以更改程序代碼,因為它也只是一塊CPU存儲在為程序數據保留的內存部分中的指令。出於安全考慮,這些特殊的內存塊僅限於您)。

這是另一種看待它們的方式:

首先, memory是一些可以存儲數據的地方。

其次, address是某些內存的位置。 address引用的存儲器可能存在也可能不存在。 您不能將任何內容放在 address ,只能放在 address - 您只能將數據memoryaddress所指的memory中。

array是內存中的連續位置 - 它是特定類型的一系列內存位置。 它存在,並且可以將實際數據放入其中。 memory任何實際位置一樣,它一個address

pointer包含一個address 那個地址可以來自任何地方。

string是NUL終止的字符array

這樣看:

memory - 一所房子。 你可以把東西放進去。 房子 address

array - 一排房子,一排在另一排,都是一樣的。

pointer - 一張紙,你可以寫一個address 你不能在紙上存放任何東西(地址除外),但你可以把東西放在你在紙上寫的地址上。

然后,該數組被視為指針,程序將指針分配給指針字符串,該指針指向編譯器創建的數組的第一個元素。

這里措辭不是很好。 數組仍然是一個數組,並被視為這樣。 程序將指向第一元素的指針值(rvalue)分配給指向char的變量(通常為lvalue)。 這是這里唯一的中間/非存儲指針值,因為編譯器和鏈接器在編譯鏈接時知道數組的地址。 但是,您無法直接訪問該數組,因為它是匿名文字。 如果你用文字初始化一個數組,那么文字將會消失(想象為優化 - 作為單獨的實體),並且數組可以通過其預先計算的地址直接訪問。

char s[] = "String"; // char[7]
char *p = s;
char *p = &s[0];

暫無
暫無

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

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