簡體   English   中英

為什么我們允許更改“const”限定變量的值?為什么允許指針,但不允許賦值?

[英]Why are we allowed to change values of “const” qualified variables?Why pointers are allowed for this,but not assignment?

考慮以下2個程序prog1prog2。如果我嘗試使用指針ptr更改const限定變量i的值,我得到警告(而不是錯誤) "initialization discards qualifiers from pointer target type|" ,但程序仍然運行並顯示新值。但是如果我嘗試使用賦值語句在第二個程序中更改i的值,我得到assignment of read-only variable 'i'|錯誤 (不是警告) assignment of read-only variable 'i'|

以下是此前提產生的混淆:

1)為什么我們允許在任何情況下更改只讀const限定變量的值?它是否會破壞使用const限定符的目的?如果我們嘗試這樣做,我們不應該得到錯誤嗎?

2)即使由於一些奇怪的原因我們被允許更改常量值,為什么在使用指針(允許,帶警告)和使用賦值更改只讀const限定變量的值之間進行區分操作(根本不允許,並給我們一個錯誤)?

//prog1
#include <stdio.h>

int main ()
{
 const int i=8;
 int *ptr=&i;
 *ptr=9;
 printf("%d",*ptr);  //Prints new value nevertheless
}

警告:初始化從指針目標類型中丟棄限定符

//prog2
#include <stdio.h>

int main()
{
const int i=8;
i=10;
printf("%d",i);
}

錯誤:分配只讀變量'i'|

編輯H2CO3

在這里,我多次更改const限定變量的值。我只得到一個警告,與prog1的相同

//prog3
#include <stdio.h>

int main ()
{
const int i=8;
int *ptr=&i;
*ptr=9;
*ptr=10;
printf("%d",*ptr);  //Prints 10
}

1)為什么我們允許在任何情況下更改只讀const限定變量的值? 它不會破壞使用const限定符的目的嗎?

嘗試通過賦值運算符更改const限定對象是違反約束:

6.5.16在約束條件下:

2賦值運算符應具有可修改的左值作為其左操作數。

可修改的左值在6.3.2.1(1)中定義:

可修改的左值是一個左值,它沒有數組類型,沒有不完整的類型, 沒有const限定類型 ,如果是結構或聯合,則沒有任何成員(包括,遞歸地,任何成員)或具有const限定類型的所有包含聚合或聯合的元素。

作為約束違規,它需要來自編譯器的診斷消息,符合5.1.1.3(1):

如果預處理轉換單元或轉換單元包含違反任何語法規則或約束的情況,則符合的實現應生成至少一個診斷消息(以實現定義的方式標識),即使該行為也明確指定為未定義或實現 - 定義。 在其他情況下不需要產生診斷消息。

但是,不需要實現來拒絕無效程序,因此診斷消息也可以是警告而不是錯誤。

但是,修改通過不具有const限定類型的左值聲明為const的對象不是約束違規,盡管它調用未定義的行為,6.7.3(6):

如果嘗試通過使用具有非const限定類型的左值來修改使用const限定類型定義的對象,則行為未定義。

由於它不是約束違規也不是無效語法,因此甚至不需要發出診斷消息。

如果我們嘗試這樣做,我們不應該得到錯誤嗎?

如果嘗試通過具有const限定類型的左值修改對象,則必須獲取診斷消息。

由於這種嚴重違反聲明的意圖,大多數編譯器在這些情況下會發出錯誤。

如果您嘗試通過具有非const-qualifed類型的左值修改具有const限定類型的對象,如下所示

const int i=8;
int *ptr=&i;
*ptr=9;

通過表達式*ptr = 9修改i的嘗試調用未定義的行為,但不是約束違規(或語法錯誤),因此不需要診斷消息(並且沒有給出)。

為初始化發出了診斷消息

int *ptr = &i;

因為這又是一個約束違規,按照6.5.16.1(1):

以下其中一項應持有:

  • 左操作數具有原子,限定或非限定算術類型,右邊有算術類型;
  • 左操作數具有與右側類型兼容的結構或聯合類型的原子,限定或非限定版本;
  • 左操作數具有原子,限定或非限定指針類型,並且(考慮左值操作數在左值轉換后將具有的類型)兩個操作數都是指向兼容類型的限定或非限定版本的指針, 左側指向的類型具有全部右邊指出的那種限定詞 ;
  • 左操作數具有原子,限定或非限定指針類型,並且(考慮左值操作數在左值轉換后將具有的類型)一個操作數是指向對象類型的指針,另一個是指向合格或非限定版本的指針void,左邊指向的類型具有右邊指向的所有類型的限定符;
  • 左操作數是一個原子,限定或非限定指針,右邊是一個空指針常量; 要么
  • 左操作數的類型為atomic,qualified或nonqualified _Bool,右邊是指針。

然而,該診斷通常是一個警告,而不是一個錯誤,因為一個人可能會明確地拋出const

int *ptr = (int*)&i;

而一個人不能拋棄iconst

通過一個指針到已經通過鑄造掉得到的非const限定對象類型修改對象const從一個指針到一個常量限定對象類型是有效的,如果指向的對象是可修改的 愚蠢的例子:

int i = 8;
const int *cptr = &i;  // valid, no problem adding const
int *mptr = (int*)cptr;
*mptr = 9;             // no problem, pointee is non-const

2)即使由於一些奇怪的原因我們被允許更改常量值,為什么在使用指針(允許,帶警告)和使用賦值更改只讀const限定變量的值之間進行區分操作(根本不允許,並給我們一個錯誤)?

直接分配給具有const限定類型的對象不僅違反了約束,而且明顯違反了所聲明的語義。 聲明一個對象const明確地說“我不希望修改該對象”。

通過指向非const限定類型的指針修改對象不是約束違規,只有當指針對象具有const限定類型時才會修改未定義的行為。 允許將指向const限定類型的指針轉​​換為指向相應的非const限定類型的指針,並且通過該指針修改指針可能是有效的,因此您只獲得警告,並且僅在未進行轉換時明確。

在給定的簡短示例中,編譯器可以檢測到指針對象具有const限定類型,因此修改調用未定義的行為,但通常這樣很難,並且通常無法檢測。 因此,編譯器甚至不會嘗試檢測簡單的情況,這是不值得的。

為什么我們允許在任何情況下更改只讀const限定變量的值?

我們不是。 我不明白你為什么這么認為,也沒有哪個例子表明這一點。

為什么使用指針改變只讀const限定變量的值之間的區別(允許,帶警告)

再說一遍:這是不允許的,因此警告。 (警告應該被認真對待 - 你似乎沒有給它們任何意義......)只是編譯器不知道指針指向某個const限定對象(因為它被聲明為非const) T * )。

至於為什么更改變量有效:

解釋#1:它是未定義的行為(違反約束),所以它可以做任何事情。

解釋#2:可能它只是像本地自動變量一樣存儲在堆棧中,你確實可以改變它。

暫無
暫無

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

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