[英]Why do <const char*> and <const char[]> have very different memory or pointer behaviour?
我正在試驗 C++ 並發現const char*
和const char[]
在以下代碼中的行為非常不同。 如果我沒有很好地表達這個問題,真的很抱歉,因為我不清楚代碼中發生了什么。
#include <iostream>
#include <vector>
// This version uses <const char[3]> for <myStr>.
// It does not work as expected.
struct StrStruct
{
const char myStr[3];
};
// This program extracts all the string elements in <strStructList> and copy them to <strListCopy>
int main()
{
StrStruct strStruct1{"ab"};
StrStruct strStruct2{"de"};
StrStruct strStruct3{"ga"};
std::vector<StrStruct> strStructList{strStruct1, strStruct2, strStruct3};
std::vector<const char*> strListCopy{};
for (StrStruct strStructEle : strStructList)
{
strListCopy.push_back(strStructEle.myStr);
std::cout << "Memory address for the string got pushed back in is "
<< &strStructEle.myStr << std::endl;
std::cout << "Memory address for the first element of the string got pushed back in is "
<< (void *) &strStructEle.myStr[0] << "\n" <<std::endl;
}
std::cout << "Show content of <strListCopy>:" << std::endl;
for (const char*& strEle : strListCopy)
{
std::cout << strEle << std::endl;
}
}
以下是它的output:
Memory address for the string got pushed back in is [address#99]
Memory address for the first element of the string got pushed back in is [address#99]
Memory address for the string got pushed back in is [address#99]
Memory address for the first element of the string got pushed back in is [address#99]
Memory address for the string got pushed back in is [address#99]
Memory address for the first element of the string got pushed back in is [address#99]
Show content of <strListCopy>:
ga
ga
ga
但是,如果我只是簡單地更改StrStruct
的實現
從:
// This version uses <const char[3]> for <myStr>.
// It does not work as expected.
struct StrStruct
{
const char myStr[3];
};
至
// This version uses <const char*> for <myStr>.
// It works as expected.
struct StrStruct
{
const char* myStr;
};
程序的output變成這樣:
Memory address for the string got pushed back in is [address#10]
Memory address for the first element of the string got pushed back in is [address#1]
Memory address for the string got pushed back in is [address#10]
Memory address for the first element of the string got pushed back in is [address#2]
Memory address for the string got pushed back in is [address#10]
Memory address for the first element of the string got pushed back in is [address#3]
Show content of <strListCopy>:
ab
de
ga
讓我感到困惑的是:
為什么在第一個版本中所有字符串都具有相同的值? 我嘗試在 for each 循環中使用const strStruct&
而不是strStruct
來解決問題,但我不明白如何。
為什么const char*
和const char[]
行為如此不同? 我認為它們在很大程度上是相同的,原因如下:
const char myChars[] = "abcde";
const char* myCharsCopy = myChars;
std::cout << myChars << " vs " << myCharsCopy << std::endl;
它打印出abcde vs abcde
,您可以直接將const char[]
的值分配給const char*
而不會出現任何錯誤。
const char[]
更改為const char*
可以解決問題?了解 rest 所需的基礎知識:
struct StrStruct
{
const char myStr[3];
};
包含數據
struct StrStruct
{
const char * myStr;
};
指向數據。
Arrays 衰減為指針,但本身不是指針。
const char myChars[] = "abcde";
制作一個大小正好合適的數組(六個字符、五個字母和 null 終止符)來保存"abcde"
並將字符串復制到數組中。 請注意,這不必是const
。
const char* myCharsCopy = myChars;
定義一個指向char
的指針並將數組myChars
分配給它。 myChars
自動衰減到進程中的一個指針。 myCharsCopy
不是myChars
的副本; 它僅保存myChars
的地址。 請注意,只要myChars
是const
, myCharsCopy
就必須是const
。 另請注意,您不能分配給數組,並且幾乎不可能復制數組,除非您將其放在另一個數據結構中。 您通常可以做的最好的事情是將數組中的內容復制到另一個數組( memcpy
或strcpy
取決於目標以及該數組是否是 null 終止的字符數組)。
請注意,在許多用途中,例如 function 參數, const char[]
和const char*
表示相同的意思。
void func(const char a[], // accepts constant pointer to char
const char * b) // also accepts constant pointer to char
這些東西變得非常奇怪,原因(主要)在 1970 年代很有意義。 今天我強烈推薦你使用像std::vector
和std::array
這樣的庫容器,而不是原始的 arrays。
除非您另外指定,否則基於范圍的 for 循環對列表中的項目的副本進行操作。 在身體里
for (StrStruct strStructEle : strStructList)
在循環的第一次迭代中, strStructEle
不是strStruct1
甚至不是 strStructList 中的strStructEle
的strStructList
,它是第三個相同的 object。 副本在正文結束時被銷毀,從而釋放使用的存儲空間。
和
for (StrStruct & strStructEle : strStructList)
該循環將對strStructList
中的項目的引用進行操作,因此不會復制。
既然你已經趕上了...
為什么在第一個版本中所有字符串都具有相同的值? 我嘗試在 for each 循環中使用 const strStruct& 而不是 strStruct 來解決問題,但我不明白如何。
自從
struct StrStruct
{
const char myStr[3];
};
包含復制StrStruct
時的數據。 代碼復制了這里的數據結構
std::vector<StrStruct> strStructList{strStruct1, strStruct2, strStruct3};
而且,更重要的是 output,在這里
for (StrStruct strStructEle : strStructList) // strStructEle copied, so data in it is copied
{
strListCopy.push_back(strStructEle.myStr); //strStructEle.myStr decays to pointer,
// and pointer is stored in strListCopy
// this is not really a copy it's a pointer
// to data stored elsewhere
std::cout << "Memory address for the string got pushed back in is "
<< &strStructEle.myStr << std::endl; // print address of array
std::cout << "Memory address for the first element of the string got pushed back in is "
<< (void *) &strStructEle.myStr[0] << "\n" <<std::endl;
// prints address of the first item in the array, the same as the array
} // strStructEle is destroyed here, so the stored pointer is now invalid.
// Technically anything can happen at this point
但在這種情況下,任何可能發生的事情似乎都是在循環的下一次迭代中將存儲重用於strStructEle
。 這就是為什么所有存儲的指針看起來都是一樣的。 他們是一樣的。 它們是不同的對象,在不同的時間點都位於同一位置。 所有這些對象都已過期,因此嘗試查看它們並不是一個好主意。
const strStruct&
“修復”了這個問題,因為沒有復制。 每次迭代都在不同位置的不同 object 上運行,而不是在同一位置在不同的 object 上運行。
為什么將
const char[]
更改為const char*
可以解決問題?
如果myStr
是指針而不是數組,情況就不同了
for (StrStruct strStructEle : strStructList) // strStructEle copied, so data in it is copied
// BUT! The data in it is a pointer to data
// stored elsewhere that is NOT copied
{
strListCopy.push_back(strStructEle.myStr); //strStructEle.myStr is a pointer and is
// directly stored in strListCopy
// this is still not a copy
std::cout << "Memory address for the string got pushed back in is "
<< &strStructEle.myStr << std::endl; // print address of pointer, not what
// it points at
std::cout << "Memory address for the first element of the string got pushed back in is "
<< (void *) &strStructEle.myStr[0] << "\n" <<std::endl;
// prints address of the first item pointed to by the pointer,
// and will be a totally different address
}
為什么 const char* 和 const char[] 行為如此不同? 由於以下原因,我認為它們在很大程度上是相同的……
這是如上所述的陣列衰減的結果。
旁白:
當vector
被允許直接包含(並擁有)他們正在收集的數據時,它們處於最佳狀態。 它們處理所有 memory 管理,並將數據保存在一個漂亮、易於緩存的塊中。
基於評論部分的Yakov Galka和user4581301
在回答之前要弄清楚的幾件事:
const char*
和const char[]
之間的區別:
從概念上講:
const char*
是指向const char
的指針
const char[]
是字符數組本身。
在代碼方面:
const char*
存儲一個 memory 地址,並且它自己的memory 地址與它存儲的地址不同。
const char[]
存儲的是數組第一個元素的 memory 地址,它自己的memory 地址與它存儲的相同。
const char myCharsArray[] = "abcde"; // Writing like this guarrentees you have an null terminator at the end
const char* myCharsPointer = "qwert\0";
std::cout << "The memory address for <myCharsArray> is "
<< &myCharsArray
<< std::endl;;
std::cout << "The memory address for the first element in <myCharArray> is "
<< (void *) &myCharsArray[0]
<< std::endl;
std::cout << "The memory address for <myCharsPointer> is "
<< &myCharsPointer
<< std::endl;
std::cout << "The memory address for the first element in <myCharsPointer> is "
<< (void *) &myCharsPointer[0]
<< std::endl;
它的output是這樣的:
The memory address for <myCharsArray> is [address#10]
The memory address for the first element in <myCharArray> is [address#10]
The memory address for <myCharsPointer> is [address#88]
The memory address for the first element in <myCharsPointer> is [address#99]
要回答這三個問題:
問題一:
在第一個版本中, std::vector::push_back
不斷添加它復制的字符數組中第一個元素的地址,這也是strStructEle.myStr
本身的地址,它永遠不會改變。 最后,列表是一堆值完全相同的 memory 地址。
通過使用const strStruct&
,使用對原始內容的引用。 因此,它們唯一且真實的 memory 地址被復制到列表中。
問題2:
區別如上所述。
問題 3:
它允許傳遞原始字符數組的原始 memory 地址,而不是復制原始字符數組的內容,然后復制臨時 object 的 memory 地址。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.