簡體   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