int rounds = 0;
char *names[10];
while (rounds < 10)
{
此時,name
不存在。
在這裏你實例化一個新的name
與未初始化的內容。這發生在每個循環迭代上。
char name[10];
在這裏,您初始化新的緩衝區,如果它是第一輪:
if (rounds == 1)
std::strcpy(name, "Test");
在這一點上,將緩衝區的內容都在2回合開始初始化。
name
的地址是特定於實現的,並不保證是特別的。它在每次循環迭代中碰巧是一樣的,這當然是很好的,但是你不需要注意,因爲C++的語義沒有任何東西可以告訴你期望什麼。
下面的行調用在回合2起,當您嘗試使用name
內容未定義行爲:
// vvvvvvv- undefined behavior
std::cout << &name << " " << name << std::endl;
在這裏,你的name
地址存儲:
names[rounds] = name;
++rounds;
目前的關閉大括號時,name
的範圍結束。該物體被破壞並不復存在。上面存儲的name
的地址是一個懸掛指針,因爲name
實例現在已被銷燬。
這是純粹的巧合,下一次name
被上面的例子,它發生在同一地址,你會得到一些可用的數據。它也可以格式化硬盤,所以要小心:未定義的行爲字面意思是代碼可以自由地做任何事情。
}
在任何情況下,你的代碼都是一些非常奇怪的C和C++範例。當你編寫C++時,你想看到的最後一件事是裸號數組char
和使用C字符串API。
如果你用慣用的C++編寫你的代碼,它會和C#中的幾乎一樣。而且奇妙的是,與C版本相比,這段代碼沒有任何開銷,但是所有的安全結構都是你無法從C獲得的(因爲C不是C++!)。
int main() {
std::vector<std::string> names;
const int N = 10;
names.reserve(N); // optional to prevent the vector from reallocating as it grows
std::generate_n(std::back_inserter(names), N, +[]{ return "Test"; });
for (auto const & name : names)
std::cout << name << std::endl;
}
如果你堅持使用C字符串的API編寫的代碼 - 絕對沒有理由,因爲它比C中沒有性能上的優勢++使用得當 - 在這裏你去:
int main() {
const int N = 10;
char *names[N];
for (int i = 0; i < N; i ++)
names[i] = strdup("Test");
for (auto name : names)
std::cout << (void*)name << " " << name << std::endl;
}
在這裏,每個存儲在names
中的地址將會不同,因爲strdup
會分配一個新的字符串。輸出:
0x7fce7b700000 Test
0x7fce7b700010 Test
0x7fce7b700020 Test
0x7fce7b700030 Test
0x7fce7b700040 Test
0x7fce7b700050 Test
0x7fce7b700060 Test
0x7fce7b700070 Test
0x7fce7b700080 Test
0x7fce7b700090 Test
你也可以這樣做:
int main() {
char *names[10];
std::generate(std::begin(names), std::end(names), +[]{ return strdup("Test"); });
for (auto name : names)
std::cout << (void*)name << " " << name << std::endl;
}
您還沒有初始化的變量,任何東西 - 所以打印出來是自找麻煩。 – doctorlove