簡體   English   中英

為什么編譯器允許字符串文字不是 const?

[英]Why do compilers allow string literals not to be const?

內存中的文字到底在哪里? (見下面的例子)

我無法修改文字,因此它應該是 const char*,盡管編譯器允許我使用 char*,但即使使用大多數編譯器標志,我也沒有警告。

而將 const char* 類型隱式轉換為 char* 類型會給我一個警告,請參見下文(在 GCC 上測試,但在 VC++2010 上的行為類似)。

另外,如果我修改 const char 的值(使用下面的技巧,GCC 最好給我一個警告),它不會出錯,我什至可以在 GCC 上修改和顯示它(即使我猜它仍然是一個未定義的行為,我想知道為什么它沒有對文字做同樣的事情)。 這就是為什么我要問這些文字存儲在哪里,以及更常見的 const 存儲在哪里?

const char* a = "test";
char* b = a; /* warning: initialization discards qualifiers 
  from pointer target type (on gcc), error on VC++2k10 */

char *c = "test"; // no compile errors
c[0] = 'p'; /* bus error when execution (we are not supposed to 
  modify const anyway, so why can I and with no errors? And where is the 
  literal stored for I have a "bus error"? 
  I have 'access violation writing' on VC++2010 */

const char d = 'a';
*(char*)&d = 'b'; // no warnings (why not?)
printf("%c", d);  /* displays 'b' (why doesn't it do the same
  behavior as modifying a literal? It displays 'a' on VC++2010 */

C 標准不禁止修改字符串文字。 它只是說如果進行了嘗試,行為是未定義的。 根據 C99 的基本原理,委員會中有人希望字符串文字是可修改的,因此標准沒有明確禁止它。

請注意,情況在 C++ 中有所不同。 在 C++ 中,字符串文字是 const char 的數組。 但是,C++ 允許從 const char * 轉換為 char *。 不過,該功能已被棄用。

主要是歷史原因。 但請記住,它們在某種程度上是合理的:字符串文字沒有類型char * ,而是char [N] ,其中N表示緩沖區的大小(否則, sizeof不會在字符串文字上按預期工作)並且可以是用於初始化非const陣列。 您只能將它們分配給const指針,因為數組到指針的隱式轉換和非constconst的隱式轉換。

如果字符串文字表現出與復合文字相同的行為會更加一致,但由於它們是 C99 構造並且必須保持向后兼容性,這不是一個選項,因此字符串文字仍然是一個例外情況。

我不確定 C/C++ 標准代表什么字符串。 但是我可以准確地說出 MSVC 中字符串文字的實際情況。 而且,我相信,其他編譯器的行為也類似。

字符串文字駐留在 const 數據部分中。 它們的內存被映射到進程地址空間。 但是,它們存儲的內存頁是只讀的(除非在運行期間顯式修改)。

但還有一些你應該知道的。 並非所有包含引號的 C/C++ 表達式都具有相同的含義。 讓我們澄清一切。

const char* a = "test";

上面的語句使編譯器創建一個字符串文字“test”。 鏈接器確保它位於可執行文件中。 在函數體中,編譯器生成一個代碼,在堆棧上聲明一個變量a ,該變量由字符串文字“test.c”的地址初始化。

char* b = a;

在這里,您聲明另一個變量b它獲取的值在堆棧上a 由於a指向只讀地址 - b也是如此。 b沒有const語義的事實並不意味着您可以修改它所指向的內容。

char *c = "test"; // no compile errors
c[0] = 'p';

以上生成訪問沖突。 同樣,缺少const並不意味着機器級別的任何事情

const char d = 'a';
*(char*)&d = 'b';

首先 - 以上與字符串文字無關。 'a' 不是字符串。 這是一個角色。 這只是一個數字。 這就像編寫以下內容:

const int d = 55;
*(int*)&d = 56;

上面的代碼使編譯器變得愚蠢。 你說變量是const ,但是你設法修改它。 但這與處理器異常無關,因為d仍然駐留在讀/寫內存中。

我想再添加一個案例:

char b[] = "test";
b[2] = 'o';

上面在堆棧上聲明了一個數組,並用字符串“test”初始化它。 它駐留在讀/寫存儲器中,並且可以修改。 這里沒有問題。

內存中的文字到底在哪里? (見下面的例子)

初始化數據段。 在 Linux 上,它是.data.rodata

我無法修改文字,因此它應該是 const char*,盡管編譯器允許我使用 char*,但即使使用大多數編譯器標志,我也沒有警告。

歷史,因為它已經被其他人解釋過了。 大多數編譯器允許您通過命令行選項判斷字符串文字是只讀的還是可修改的。

通常希望字符串文字為只讀的原因是內存中具有只讀數據的段可以(並且通常是)在從可執行文件啟動的所有進程之間共享。 這顯然可以避免浪費一些 RAM,以保留相同信息的冗余副本。

即使使用大多數編譯器標志,我也沒有警告

真的嗎? 當我編譯以下代碼片段時:

int main()
{
    char* p = "some literal";
}

在 g++ 4.5.0 上,即使沒有任何標志,我也會收到以下警告:

警告:不推薦使用從字符串常量到 'char*' 的轉換

您可以寫入c因為您沒有將其設置為 const。 c定義為 const 是正確的做法,因為右側的類型為const char*

它在運行時生成錯誤,因為“測試”值可能分配給只讀的代碼段。 請參閱此處此處

暫無
暫無

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

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