简体   繁体   English

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

[英]Why do <const char*> and <const char[]> have very different memory or pointer behaviour?

I was experimenting with C++ and found out that const char* and const char[] behave very differently with the following code.我正在试验 C++ 并发现const char*const char[]在以下代码中的行为非常不同。 Really sorry if I did not phrase this question very well as I am not clear of what is happening in the code.如果我没有很好地表达这个问题,真的很抱歉,因为我不清楚代码中发生了什么。

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

The following is its output:以下是它的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

However, if I just simply change the implementation for StrStruct但是,如果我只是简单地更改StrStruct的实现

from:从:

// This version uses <const char[3]> for <myStr>.
// It does not work as expected. 
struct StrStruct                                                                                                           
{                                                                                                                  
    const char myStr[3];                                                                                                     
};

to

// This version uses <const char*> for <myStr>.
// It works as expected.                                                                                                                      
struct StrStruct                                                                                                           
{                                                                                                                  
    const char* myStr;                                                                                                     
};

Program's output becomes this:程序的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

What confuses me is the following:让我感到困惑的是:

  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.我尝试在 for each 循环中使用const strStruct&而不是strStruct来解决问题,但我不明白如何。

  2. Why do const char* and const char[] behave so differently?为什么const char*const char[]行为如此不同? I thought they are largely the same due to the following:我认为它们在很大程度上是相同的,原因如下:

const char myChars[] = "abcde";                                                                                     
const char* myCharsCopy = myChars;                                                                                  
                                                                                                                        
std::cout << myChars << " vs "  << myCharsCopy << std::endl;  

It prints out abcde vs abcde and you can directly assign value of const char[] to const char* without any error.它打印出abcde vs abcde ,您可以直接将const char[]的值分配给const char*而不会出现任何错误。

  1. Why does changing const char[] to const char* solves the problem?为什么将const char[]更改为const char*可以解决问题?

Fundamentals necessary to understand the rest:了解 rest 所需的基础知识:

Arrays and Decay Arrays 和衰减

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

contains the data包含数据

struct StrStruct
{
    const char * myStr;
};

points at the data.指向数据。

Arrays decay to pointers but are not pointers themselves. Arrays 衰减为指针,但本身不是指针。

const char myChars[] = "abcde";

makes an array of exactly the right size (six characters, five letters and the null terminator) to hold "abcde" and copies the string into the array.制作一个大小正好合适的数组(六个字符、五个字母和 null 终止符)来保存"abcde"并将字符串复制到数组中。 Note that this need not be const .请注意,这不必是const

const char* myCharsCopy = myChars;

defines a pointer to a char and assigns to it the array myChars .定义一个指向char的指针并将数组myChars分配给它。 myChars automatically decays to a pointer in the process. myChars自动衰减到进程中的一个指针。 myCharsCopy is not a copy of myChars ; myCharsCopy不是myChars的副本; it merely holds the address of myChars .它仅保存myChars的地址。 Note that so long as myChars is const , myCharsCopy must be const .请注意,只要myCharsconstmyCharsCopy就必须是const Also note that you cannot assign to an array and that it's next to impossible to copy an array unless you place it inside another data structure.另请注意,您不能分配给数组,并且几乎不可能复制数组,除非您将其放在另一个数据结构中。 The best you can normally do is copy what's in the array to another array ( memcpy or strcpy depending on the goal and whether or not the array is a null terminated character array).您通常可以做的最好的事情是将数组中的内容复制到另一个数组( memcpystrcpy取决于目标以及该数组是否是 null 终止的字符数组)。

Note that in many uses, a function parameter for example, const char[] and const char* mean the same thing.请注意,在许多用途中,例如 function 参数, const char[]const char*表示相同的意思。

void func(const char a[], // accepts constant pointer to char
          const char * b) // also accepts constant pointer to char

This stuff gets really weird for reasons that (mostly) made great sense back in the 1970s.这些东西变得非常奇怪,原因(主要)在 1970 年代很有意义。 Today I strongly recommend you use library containers like std::vector and std::array instead of raw arrays.今天我强烈推荐你使用像std::vectorstd::array这样的库容器,而不是原始的 arrays。

Range-based for Loops基于范围的 for 循环

Range-based for loops operate on copies of the items in the list unless you specify otherwise.除非您另外指定,否则基于范围的 for 循环对列表中的项目的副本进行操作。 In the body of在身体里

for (StrStruct strStructEle : strStructList)

on the first iteration of the loop, strStructEle is not strStruct1 or even the copy of strStructEle that sits in strStructList , it is a third identical object.在循环的第一次迭代中, strStructEle不是strStruct1甚至不是 strStructList 中的strStructElestrStructList ,它是第三个相同的 object。 The copy is destroyed at the end of the body, freeing up the storage used.副本在正文结束时被销毁,从而释放使用的存储空间。

With

for (StrStruct & strStructEle : strStructList)

the loop will operate on references to the items in strStructList , so no copies are made.该循环将对strStructList中的项目的引用进行操作,因此不会复制。

Now that you're up to speed...既然你已经赶上了...

Point 1第 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.我尝试在 for each 循环中使用 const strStruct& 而不是 strStruct 来解决问题,但我不明白如何。

Since自从

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

contains the data when you make a copy of a StrStruct .包含复制StrStruct时的数据。 The code copies the data structure here代码复制了这里的数据结构

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

and, more importantly to the output, here而且,更重要的是 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

But in this case the anything that could happen appears to be the storage is reused for the strStructEle in the next iteration of the loop.但在这种情况下,任何可能发生的事情似乎都是在循环的下一次迭代中将存储重用于strStructEle This is why all the stored pointers appear to be the same.这就是为什么所有存储的指针看起来都是一样的。 They ARE the same.他们是一样的。 They are different objects that all resided in the same location at different points in time.它们是不同的对象,在不同的时间点都位于同一位置。 All of these objects have expired, so attempting to so much as look at them is not a good idea .所有这些对象都已过期,因此尝试查看它们并不是一个好主意

const strStruct& "fixes" the problem because no copy is made. const strStruct& “修复”了这个问题,因为没有复制。 Each iteration operates on a different object at a different location rather than a different object at the same location.每次迭代都在不同位置的不同 object 上运行,而不是在同一位置在不同的 object 上运行。

Point 3第 3 点

Why does changing const char[] to const char* solves the problem?为什么将const char[]更改为const char*可以解决问题?

If myStr is a pointer rather than an array, things are different如果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
}

Point 2第 2 点

Why do const char* and const char[] behave so differently?为什么 const char* 和 const char[] 行为如此不同? I thought they are largely the same due to the following...由于以下原因,我认为它们在很大程度上是相同的……

This is the result of array decay as explained above.这是如上所述的阵列衰减的结果。

An aside:旁白:

vector s are at their best when they are allowed to directly contain (and own ) the data they're collecting.vector被允许直接包含(并拥有)他们正在收集的数据时,它们处于最佳状态。 They handle all of the memory management and they keep the data together in one nice, easily cached block.它们处理所有 memory 管理,并将数据保存在一个漂亮、易于缓存的块中。

Based on Yakov Galka and user4581301 in the comment section基于评论部分的Yakov Galkauser4581301

Few things to clear out before answer:在回答之前要弄清楚的几件事:

Difference between const char* and const char[] : const char*const char[]之间的区别:

Conceptually :从概念上讲

const char* is the pointer to a const char const char*是指向const char的指针

const char[] is the character array itself. const char[]是字符数组本身。

In Terms of Code :在代码方面

const char* stores a memory address, and, its own memory address is different from the one that it stores. const char*存储一个 memory 地址,并且它自己的memory 地址与它存储的地址不同

const char[] stores the memory address of the first element in the array, and, its own memory address is the same as the one that it stores. 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;

Its output is this:它的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]

To answer those three questions:要回答这三个问题:

Question 1:问题一:

In the first version, std::vector::push_back keeps adding the address of the first element in the character array it copied, which is also the address for strStructEle.myStr itself which never changes.在第一个版本中, std::vector::push_back不断添加它复制的字符数组中第一个元素的地址,这也是strStructEle.myStr本身的地址,它永远不会改变。 In the end, the list is a bunch of memory addresses whose values are exactly the same.最后,列表是一堆值完全相同的 memory 地址。

By using const strStruct& , reference to the original content is used.通过使用const strStruct& ,使用对原始内容的引用。 Thus, their unique and true memory addresses got copied in to the list.因此,它们唯一且真实的 memory 地址被复制到列表中。

Question 2:问题2:

Difference as explained above.区别如上所述。

Question 3:问题 3:

It allows the original memory address of the original character array to be passed around, instead of copy the content of an original character array and then the memory address of the temporary object.它允许传递原始字符数组的原始 memory 地址,而不是复制原始字符数组的内容,然后复制临时 object 的 memory 地址。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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