繁体   English   中英

为什么<const char*>和<const char[]>有非常不同的 memory 或指针行为?</const></const>

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

让我感到困惑的是:

  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*可以解决问题?

了解 rest 所需的基础知识:

Arrays 和衰减

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的地址。 请注意,只要myCharsconstmyCharsCopy就必须是const 另请注意,您不能分配给数组,并且几乎不可能复制数组,除非您将其放在另一个数据结构中。 您通常可以做的最好的事情是将数组中的内容复制到另一个数组( memcpystrcpy取决于目标以及该数组是否是 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::vectorstd::array这样的库容器,而不是原始的 arrays。

基于范围的 for 循环

除非您另外指定,否则基于范围的 for 循环对列表中的项目的副本进行操作。 在身体里

for (StrStruct strStructEle : strStructList)

在循环的第一次迭代中, strStructEle不是strStruct1甚至不是 strStructList 中的strStructElestrStructList ,它是第三个相同的 object。 副本在正文结束时被销毁,从而释放使用的存储空间。

for (StrStruct & strStructEle : strStructList)

该循环将对strStructList中的项目的引用进行操作,因此不会复制。

既然你已经赶上了...

第 1 点

为什么在第一个版本中所有字符串都具有相同的值? 我尝试在 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 上运行。

第 3 点

为什么将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
}

第 2 点

为什么 const char* 和 const char[] 行为如此不同? 由于以下原因,我认为它们在很大程度上是相同的……

这是如上所述的阵列衰减的结果。

旁白:

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

基于评论部分的Yakov Galkauser4581301

在回答之前要弄清楚的几件事:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM