簡體   English   中英

為什么C ++允許我將const char分配給const char * ?!

[英]Why is C++ allowing me to assign a const char to a const char *?​!

令我驚訝的是,它匯編為:

const char* c_str()
{
    static const char nullchar = '\0';
    return nullchar;
}

並且在我的代碼中引入了一個錯誤。 幸運的是,我抓住了它。

這是C ++故意還是編譯器錯誤? 是否有理由主動忽略數據類型?
它可以在Visual C ++ 2010和GCC中工作 ,但是鑒於明顯的數據類型不匹配,我不明白為什么要工作。 static也是不必要的。)

如您所定義, nullchar是一個整數常量表達式,其值為0。

C ++ 03標准將空指針常量定義為:“空指針常量是整數類型的整數常量表達式(5.19)rvalue,其值為零。” 長話短說,您的nullchar是一個空指針常量,這意味着可以將其隱式轉換並分配給基本上任何指針。

請注意,所有這些元素都是使隱式轉換正常運行所必需的。 例如,如果您使用的是'\\1'而不是'\\0' ,或者您沒有nullchar指定const限定nullchar ,那么您將不會獲得隱式轉換-您的分配將會失敗。

包含此轉換是有意的,但眾所周知。 0為空指針常量,是從C繼承的。我相當確定Bjarne和大多數C ++標准委員會(以及大多數C ++社區)將非常希望刪除此特定的隱式轉換,但是這樣做會破壞與許多C代碼的兼容性(可能接近所有C代碼)。

這是一個古老的歷史:它可以追溯到C。

C中沒有null關鍵字。C中的null指針常量是:

  • 整數表達式中具有0值,如00L'\\0'記住, char是一個組成型), (2-4/2)
  • 這樣的表達式轉換為void* ,例如(void*)0(void*)0L(void*)'\\0'(void*)(2-4/2)

NULL (不是關鍵字!)擴展為這種空指針常量。

在第一個C ++設計中,僅允許將整數常量表達式用作空指針常量。 最近std::nullptr_t已添加到C ++。

在C ++中,但在C中不是,用整數常量表達式初始化的整數類型的const變量是整數常量表達式:

const int c = 3;
int i;

switch(i) {
case c: // valid C++
// but invalid C!
}

因此,使用表達式'\\0'初始化的const char是一個空指針常量:

int zero() { return 0; }

void foo() {
    const char k0 = '\0',
               k1 = 1,
               c = zero();
    int *pi;

    pi = k0; // OK (constant expression, value 0)
    pi = k1; // error (value 1)
    pi = c; // error (not a constant expression)
}

您認為這不是聲音設計嗎?


已更新,以包括C99標准的相關部分...根據§6.6.6...

整數常量表達式應具有整數類型,並且僅應具有整數常量,枚舉常量,字符常量,結果為整數常量的sizeof表達式的sizeof以及作為強制類型轉換的立即數的浮點常量。 整數常量表達式中的強制轉換運算符只能將算術類型轉換為整數類型,但作為操作數的一部分要轉換為sizeof運算符。

對於僅C ++程序員的一些說明:

  • 對於C ++程序員稱為“文字”,C使用術語“常量”。
  • 在C ++中, sizeof始終是一個編譯時間常數。 但是C具有可變長度的數組,因此sizeof有時不是編譯時間常數。

然后,我們看到§6.3.2.3.3狀態...

值為0的整數常量表達式,或強制類型為void *的表達式,稱為空指針常量 如果將空指針常量轉換為指針類型,則保證生成的指針(稱為空指針 )可以將不相等的指針與指向任何對象或函數的指針進行比較。


要了解此功能的年代久遠,請參閱C99標准中相同的鏡像部件...

第6.6.6條

整數常量表達式應具有整數類型,並且僅應具有整數常量,枚舉常量,字符常量,結果為整數常量的sizeof表達式的sizeof以及作為強制類型轉換的立即數的浮點常量。 整數常量表達式中的強制轉換運算符只能將算術類型轉換為整數類型,但作為操作數的一部分要轉換為sizeof運算符。

§6.3.2.3.3

值為0的整數常量表達式,或強制類型為void *的表達式,稱為空指針常量 如果將空指針常量轉換為指針類型,則保證生成的指針(稱為空指針 )可以將不相等的指針與指向任何對象或函數的指針進行比較。

nullchar是一個(編譯時)常量表達式,值為0。因此,它是將隱式轉換為null指針的公平游戲。

更詳細地講:我在這里引用1996年標准草案

char是整數類型。 nullchar是const,因此是5.19.1節所述的(編譯時)整數常量表達式:

5.19常數表達式[expr.const]

1在某些地方,C ++要求表達式的計算結果為整數或枚舉常量...整數常量表達式可能涉及... const變量...

此外,按照第4.10.1節的規定, nullchar值為0,從而可以將其隱式轉換為指針:

4.10指針轉換[conv.ptr]

1整數類型的整數常量表達式( expr.const )的右值為0(稱為空指針常量),可以轉換為指針類型。

可能的一個直觀原因“為什么”(剛好在我腦海中)是未指定指針寬度,因此允許從任何大小的整數常量表達式轉換為空指針。


已更新(較新的)C ++ 03標准的相關部分...根據§5.19.1...

整數常量表達式只能包含用常量表達式(8.5)初始化的整數或枚舉類型的文字(2.13),枚舉數, const變量或靜態數據成員,整數或枚舉類型的非類型模板參數以及sizeof表達式。

然后,我們來看§4.10.1...

空指針常量是整數類型的整數常量表達式(5.19)rvalue,其值為零。 空指針常量可以轉換為指針類型。 結果是該類型的空指針值,並且可以與指向對象或函數類型的指針的所有其他值區分開。 相同類型的兩個空指針值應比較相等。

出於與編譯相同的原因進行編譯

const char *p = 0; // OK

const int i = 0;
double *q = i; // OK

const short s = 0;
long *r = s; // OK

右邊的表達式的類型為intshort ,而要初始化的對象是一個指針。 這會讓您感到驚訝嗎?

在C ++語言(以及C語言)中,值為0整數常量表達式(ICE)具有特殊的狀態(盡管ICE在C和C ++中的定義不同)。 它們符合空指針常量 在指針上下文中使用它們時,它們將隱式轉換為適當類型的空指針。

char類型是整數類型,在這種情況下與int並沒有太大區別,因此由0初始化的const char對象在C ++中也是空指針常量(但在C中不是)。

順便說一句,C ++中的bool類型也是整數類型,這意味着以false初始化的const bool對象也是空指針常量

const bool b = false;
float *t = b; // OK

后來針對C ++ 11的缺陷報告更改了空指針常量的定義。 校正后,空指針常量只能是“值為零的整數文字或std :: nullptr_t類型的prvalue”。 更正后,以上指針初始化在C ++ 11中不再具有良好的格式。

它不會忽略數據類型。 這不是錯誤。 它利用了您在其中放置的const的優勢,並且看到它的值實際上是整數0(char是整數類型)。

整數0是有效的(根據定義)空指針常量,可以將其轉換為指針類型(成為空指針)。

您希望空指針的原因是要具有一些“指向無處”並且可以檢查的指針值(即,您可以將空指針與整數0進行比較,並且返回true)。

如果刪除const,則會出現錯誤。 如果將double放在其中(與許多其他非整數類型一樣;我想例外是只能通過[轉換運算符的重載]轉換為const char *的類型),您將得到一個錯誤(甚至是w / o const)。 依此類推。

整個問題是,在這種情況下,您的實現看到您正在返回一個空的ptr常量; 您可以將其轉換為指針類型。

看來,這個問題的許多真正答案已經在評論中結束了。 總結一下:

  • C ++標准允許將整數類型的const變量視為“整數常量表達式”。 為什么? 很可能繞過C僅允許宏和枚舉保留整數常量表達式的位置的問題。

  • 至少可以追溯到C89,值為0的整數常量表達式可以隱式轉換為(任何類型的)空指針。 這在C代碼中經常使用,其中NULL通常#define(void*)0

  • 回到K&R,立即數0已用於表示空指針。 該約定在各處使用,其代碼如下:

     if ((ptr=malloc(...)) {...} else {/* error */} 

有一個自動演員表。 如果運行良好,請執行以下操作:

#include <stdio.h>
const char* c_str()
{
    static const char nullchar = '\0';
    return nullchar;
}

int main()
{
    printf("%d" , sizeof(c_str()));
    return 0;
}

輸出在我的計算機上應該是4->指針的大小。

編譯器自動轉換。 注意,至少gcc發出警告(我不知道VS)

我認為這可能是因為空字符在類型之間很常見。 您正在做的是在返回空字符時設置空指針。 如果使用了其他任何字符,這將失敗,因為您沒有將字符的地址傳遞給指針,而是將字符的值傳遞給了指針。 Null是有效的指針和字符值,因此可以將空字符設置為指針。

簡而言之,任何類型的null都可以用來設置空值,無論它是數組,指針還是變量。

暫無
暫無

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

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