c++ - 为什么 <const char*> 和 <const char[]> 有非常不同的内存或指针行为?

标签 c++ arrays string c++11

我正在试验 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;                                                                                  
    }                                                                                                                                                                                            
}

下面是它的输出:

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;                                                                                                     
};

程序的输出变成这样:

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

让我感到困惑的是以下内容:

  1. 为什么在第一个版本中所有的字符串都具有相同的值?我尝试在 for each 循环中使用 const strStruct& 而不是 strStruct 来解决问题,但我不明白怎么做。

  2. 为什么 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* 而不会出现任何错误。

  1. 为什么将 const char[] 更改为 const char* 可以解决问题?

最佳答案

理解其余部分所需的基础知识:

数组和衰减

struct StrStruct
{
    const char myStr[3];
};

包含数据

struct StrStruct
{
    const char * myStr;
};

指向数据。

Arrays decay to pointers但不是指针本身。

const char myChars[] = "abcde";

创建一个大小正好合适的数组(六个字符、五个字母和空终止符)来保存 "abcde" 并将字符串复制到数组中。请注意,这不必是 const

const char* myCharsCopy = myChars;

定义一个指向 char 的指针,并将数组 myChars 分配给它。 myChars 自动衰减为进程中的指针。 myCharsCopy 不是 myChars 的拷贝;它只包含 myChars 的地址。请注意,只要 myCharsconstmyCharsCopy 就必须是 const。另请注意,您不能分配给数组,并且除非将数组放在另一个数据结构中,否则复制数组几乎是不可能的。您通常可以做的最好的事情是将数组中的内容复制到另一个数组(memcpystrcpy 取决于目标以及数组是否为空终止字符数组) .

请注意,在许多用途中,函数参数例如 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::vectorstd::array 而不是原始数组。

基于范围的 for 循环

除非您另有说明,否则基于范围的 for 循环对列表中项目的拷贝进行操作。在正文中

for (StrStruct strStructEle : strStructList)

在循环的第一次迭代中,strStructEle 不是 strStruct1,甚至不是位于 strStructList< 中的 strStructEle 的拷贝,它是第三个相同的对象。拷贝在正文末尾销毁,释放使用的存储空间。

for (StrStruct & strStructEle : strStructList)

循环将对 strStructList 中的项目的引用进行操作,因此不会生成任何拷贝。

现在您已经跟上了速度...

点 1

Why in the first version all the strings have the same value? I tried to use const strStruct& instead of strStruct in the for each loop which solves the problem but I do not understand how.

struct StrStruct
{
    const char myStr[3];
};

包含复制 StrStruct 时的数据。代码复制这里的数据结构

std::vector<StrStruct> strStructList{strStruct1, strStruct2, strStruct3};

而且,更重要的是这里的输出

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。这就是为什么所有存储的指针看起来都一样。他们是一样的。它们是不同的对象,在不同的时间点都位于同一位置。所有这些对象都已过期,因此尝试尽可能多地查看它们是 not a good idea .

const strStruct& “修复”了这个问题,因为没有复制。每次迭代都对不同位置的不同对象进行操作,而不是对同一位置的不同对象进行操作。

第 3 点

Why does changing const char[] to const char* solves the problem?

如果 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
}

点 2

Why do const char* and const char[] behave so differently? I thought they are largely the same due to the following...

如上所述,这是数组衰减的结果。

旁白:

vector 被允许直接包含(和 own )它们正在收集的数据时,它们处于最佳状态。它们处理所有内存管理,并将数据保存在一个漂亮、易于缓存的 block 中。

关于c++ - 为什么 <const char*> 和 <const char[]> 有非常不同的内存或指针行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70103632/

相关文章:

c++ - Visual C++ 2010 速成版 : How to use with Qt?

c++ - 如何使用 std::map 获取双向迭代器的索引?

javascript - 将对象转换为对象数组

vb.net - 如何在 Visual Basic 中将文本框中的字符串检查为整数?

c++ - std :string prevent me from carelessly stomping on its data?如何

string - 在 Common Lisp 中将字符附加到字符串

python - 将 Levenshtein 比率转换为 C++

c++ - 使用两个不同 LMDB 的多标签

java - 如何在动态规划中链接数组中的元素(java)

c++ - 释放动态数组的最后一个元素