[英]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.