我試圖向工作夥伴證明,如果真的想要(並知道如何)通過使用一些欺騙手段來改變常量限定變量的值,在演示期間,我發現存在兩種不變價值的「味道」:你不能改變你做的任何事情,以及那些你可以通過使用骯髒的技巧來改變的東西。更改數據時UB的說明
常數值不變時,編譯器使用文本值,而不是存儲在堆棧(readed here)上的價值,這裏是一個piece of code,顯示我的意思:
// TEST 1
#define LOG(index, cv, ncv) std::cout \
<< std::dec << index << ".- Address = " \
<< std::hex << &cv << "\tValue = " << cv << '\n' \
<< std::dec << index << ".- Address = " \
<< std::hex << &ncv << "\tValue = " << ncv << '\n'
const unsigned int const_value = 0xcafe01e;
// Try with no-const reference
unsigned int &no_const_ref = const_cast<unsigned int &>(const_value);
no_const_ref = 0xfabada;
LOG(1, const_value, no_const_ref);
// Try with no-const pointer
unsigned int *no_const_ptr = const_cast<unsigned int *>(&const_value);
*no_const_ptr = 0xb0bada;
LOG(2, const_value, (*no_const_ptr));
// Try with c-style cast
no_const_ptr = (unsigned int *)&const_value;
*no_const_ptr = 0xdeda1;
LOG(3, const_value, (*no_const_ptr));
// Try with memcpy
unsigned int brute_force = 0xba51c;
std::memcpy(no_const_ptr, &brute_force, sizeof(const_value));
LOG(4, const_value, (*no_const_ptr));
// Try with union
union bad_idea
{
const unsigned int *const_ptr;
unsigned int *no_const_ptr;
} u;
u.const_ptr = &const_value;
*u.no_const_ptr = 0xbeb1da;
LOG(5, const_value, (*u.no_const_ptr));
這將產生下面的輸出:
1.- Address = 0xbfffbe2c Value = cafe01e
1.- Address = 0xbfffbe2c Value = fabada
2.- Address = 0xbfffbe2c Value = cafe01e
2.- Address = 0xbfffbe2c Value = b0bada
3.- Address = 0xbfffbe2c Value = cafe01e
3.- Address = 0xbfffbe2c Value = deda1
4.- Address = 0xbfffbe2c Value = cafe01e
4.- Address = 0xbfffbe2c Value = ba51c
5.- Address = 0xbfffbe2c Value = cafe01e
5.- Address = 0xbfffbe2c Value = beb1da
由於我在UB林依託(改變const的數據的值)預計該程序的行爲怪異;但這種古怪比我期待的要多。
讓我們supose編譯器使用文字值,然後,當代碼到達指令改變的值的常數(由參考,指針或memcpy
荷蘭國際集團),簡單地忽略,只要該值是爲了一個文字(雖然是未定義的行爲)。這解釋了爲什麼值保持不變,但:
- 爲什麼兩個變量中的相同的內存地址,但包含的值不同?
AFAIK相同的內存地址不能指向不同的值,因此,在輸出中的一個是在撒謊:
- 什麼真的發生了?哪個內存地址是假的(如果有的話)?
製作上的代碼進行一些更改上面我們可以儘量避免使用文字值,所以掛羊頭賣狗肉會做工作(source here):
// TEST 2
// Try with no-const reference
void change_with_no_const_ref(const unsigned int &const_value)
{
unsigned int &no_const_ref = const_cast<unsigned int &>(const_value);
no_const_ref = 0xfabada;
LOG(1, const_value, no_const_ref);
}
// Try with no-const pointer
void change_with_no_const_ptr(const unsigned int &const_value)
{
unsigned int *no_const_ptr = const_cast<unsigned int *>(&const_value);
*no_const_ptr = 0xb0bada;
LOG(2, const_value, (*no_const_ptr));
}
// Try with c-style cast
void change_with_cstyle_cast(const unsigned int &const_value)
{
unsigned int *no_const_ptr = (unsigned int *)&const_value;
*no_const_ptr = 0xdeda1;
LOG(3, const_value, (*no_const_ptr));
}
// Try with memcpy
void change_with_memcpy(const unsigned int &const_value)
{
unsigned int *no_const_ptr = const_cast<unsigned int *>(&const_value);
unsigned int brute_force = 0xba51c;
std::memcpy(no_const_ptr, &brute_force, sizeof(const_value));
LOG(4, const_value, (*no_const_ptr));
}
void change_with_union(const unsigned int &const_value)
{
// Try with union
union bad_idea
{
const unsigned int *const_ptr;
unsigned int *no_const_ptr;
} u;
u.const_ptr = &const_value;
*u.no_const_ptr = 0xbeb1da;
LOG(5, const_value, (*u.no_const_ptr));
}
int main(int argc, char **argv)
{
unsigned int value = 0xcafe01e;
change_with_no_const_ref(value);
change_with_no_const_ptr(value);
change_with_cstyle_cast(value);
change_with_memcpy(value);
change_with_union(value);
return 0;
}
將會產生以下的輸出:
1.- Address = 0xbff0f5dc Value = fabada
1.- Address = 0xbff0f5dc Value = fabada
2.- Address = 0xbff0f5dc Value = b0bada
2.- Address = 0xbff0f5dc Value = b0bada
3.- Address = 0xbff0f5dc Value = deda1
3.- Address = 0xbff0f5dc Value = deda1
4.- Address = 0xbff0f5dc Value = ba51c
4.- Address = 0xbff0f5dc Value = ba51c
5.- Address = 0xbff0f5dc Value = beb1da
5.- Address = 0xbff0f5dc Value = beb1da
正如我們所看到的,const限定的變量改爲每個change_with_*
通話,並且行爲是一樣的,除了這個事實面前,所以我很想假設怪異BEH當常量數據用作文字而不是數值時,內存地址的內容會顯示出來。
所以,爲了保證這一假設,我做了最後一次測試,改變main
的unsigned int value
到const unsigned int value
:
// TEST 3
const unsigned int value = 0xcafe01e;
change_with_no_const_ref(value);
change_with_no_const_ptr(value);
change_with_cstyle_cast(value);
change_with_memcpy(value);
change_with_union(value);
令人驚訝的是輸出是一樣的TEST 2
(code here),所以我假設數據作爲變量傳遞,而不是字面值,因爲它用作參數,所以這讓我想知道:
- 什麼事情使編譯器決定優化一個常量值作爲文字值?
總之,我的問題是:
- 在
TEST 1
。- 爲什麼常量值和非常量值共享相同的存儲器地址,但其包含的值是不同的?
- 產生此輸出的程序遵循哪些步驟?哪個內存地址是假的(如果有的話)?
- 在
TEST 3
- 什麼東西讓編譯器來決定優化一個常量的值作爲文本值?
「UB的解釋」 - 這不是一個矛盾嗎? – 2013-05-21 11:22:43
試圖與UB的理由是完全無用的。該標準提供絕對沒有保證,所以所有投注都關閉,*有龍*等。 – syam
因爲獨角獸總是隱藏在明顯的景象。不管你做什麼,都不要吃橘子! – molbdnilo