主頁 > 軟體工程 > 為什么<constchar*>和<constchar[]>有非常不同的記憶體或指標行為?

為什么<constchar*>和<constchar[]>有非常不同的記憶體或指標行為?

2021-11-26 02:28:17 軟體工程

我正在試驗 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[]to賦值const char*而不會出現任何錯誤。

  1. 為什么改成const char[]const char*解決問題?

uj5u.com熱心網友回復:

理解其余部分所需的基礎知識:

陣列和衰減

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

包含資料

struct StrStruct
{
    const char * myStr;
};

指向資料。

陣列衰減為指標,但本身不是指標。

const char myChars[] = "abcde";

制作一個大小完全正確的陣列(六個字符、五個字母和空終止符)來保存"abcde"字串并將其復制到陣列中。請注意,這不一定是const

const char* myCharsCopy = myChars;

定義一個指向 a 的指標char并將陣列分配給它myCharsmyChars在這個程序中自動衰減到一個指標。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::vector和 的庫容器std::array而不是原始陣列。

基于范圍的 for 回圈

除非您另外指定,否則基于范圍的 for 回圈對串列中專案的副本進行操作。在體內

for (StrStruct strStructEle : strStructList)

在回圈的第一次迭代中,strStructEle不是strStruct1或者甚至副本strStructEle,它位于strStructList,它是第三相同物件。副本在正文結束時被銷毀,從而釋放所使用的存盤空間。

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

而且,更重要的是輸出,這里

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&“修復”了問題,因為沒有復制。每次迭代對不同位置的不同物件而不是同一位置的不同物件進行操作。

第 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當 s 被允許直接包含(并擁有)他們正在收集的資料時,它們處于最佳狀態它們處理所有記憶體管理,并將資料保存在一個漂亮的、易于快取的塊中。

uj5u.com熱心網友回復:

基于Yakov Galka評論區的user4581301

在回答之前需要清除的幾件事:

const char*之間的區別const char[]

從概念上講

const char* 是指向a的指標 const char

const char[] 是字符陣列本身。

在代碼方面

const char*存盤一個記憶體地址,并且它自己的記憶體地址存盤的不同

const char[] 存盤陣列中第一個元素的記憶體地址,并且它自己的記憶體地址它存盤的相同

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;

它的輸出是這樣的:

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]

要回答這三個問題:

問題 1:

在第一個版本中,std::vector::push_back不斷添加它復制的字符陣列中第一個元素的地址,這也是strStructEle.myStr它自己永遠不會改變的地址最后,串列是一堆值完全相同的記憶體地址。

通過使用const strStruct&使用對原始內容的參考。因此,它們唯一且真實的記憶體地址被復制到串列中。

問題2:

差異如上所述。

問題 3:

它允許傳遞原始字符陣列的原始記憶體地址,而不是復制原始字符陣列的內容,然后復制臨時物件的記憶體地址。

轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/365838.html

標籤:C 数组 细绳 C 11

上一篇:當應該作業的內容不起作用時,如何鉆入JSON物件以獲取陣列

下一篇:如何從陣列中洗掉重復項

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • Git本地庫既關聯GitHub又關聯Gitee

    創建代碼倉庫 使用gitee舉例(github和gitee差不多) 1.在gitee右上角點擊+,選擇新建倉庫 ? 2.選擇填寫倉庫資訊,然后進行創建 ? 3.服務端已經準備好了,本地開始作準備 (1)Git 全域設定 git config --global user.name "成鈺" git c ......

    uj5u.com 2020-09-10 05:04:14 more
  • CODING DevOps 代碼質量實戰系列第二課,相約周三

    隨著 ToB(企業服務)的興起和 ToC(消費互聯網)產品進入成熟期,線上故障帶來的損失越來越大,代碼質量越來越重要,而「質量內建」正是 DevOps 核心理念之一。**《DevOps 代碼質量實戰(PHP 版)》**為 CODING DevOps 代碼質量實戰系列的第二課,同時也是本系列的 PHP ......

    uj5u.com 2020-09-10 05:07:43 more
  • 推薦Scrum書籍

    推薦Scrum書籍 直接上干貨,推薦書籍清單如下(推薦有順序的哦) Scrum指南 Scrum精髓 Scrum敏捷軟體開發 Scrum捷徑 硝煙中的Scrum和XP : 我們如何實施Scrum 敏捷軟體開發:Scrum實戰指南 Scrum要素 大規模Scrum:大規模敏捷組織的設計 用戶故事地圖 用 ......

    uj5u.com 2020-09-10 05:07:45 more
  • CODING DevOps 代碼質量實戰系列最后一課,周四發車

    隨著 ToB(企業服務)的興起和 ToC(消費互聯網)產品進入成熟期,線上故障帶來的損失越來越大,代碼質量越來越重要,而「質量內建」正是 DevOps 核心理念之一。 **《DevOps 代碼質量實戰(Java 版)》**為 CODING DevOps 代碼質量實戰系列的最后一課,同時也是本系列的 ......

    uj5u.com 2020-09-10 05:07:52 more
  • 敏捷軟體工程實踐書籍

    Scrum轉型想要做好,第一步先了解并真正落實Scrum,那么我推薦的Scrum書籍是要看懂并實踐的。第二步是團隊的工程實踐要做扎實。 下面推薦工程實踐書單: 重構:改善既有代碼的設計 決議極限編程 : 擁抱變化 代碼整潔代碼 程式員的職業素養 修改代碼的藝術 撰寫可讀代碼的藝術 測驗驅動開發 : ......

    uj5u.com 2020-09-10 05:07:55 more
  • Jenkins+svn+nginx實作windows環境自動部署vue前端專案

    前面文章介紹了Jenkins+svn+tomcat實作自動化部署,現在終于有空抽時間出來寫下Jenkins+svn+nginx實作自動部署vue前端專案。 jenkins的安裝和配置已經在前面文章進行介紹,下面介紹實作vue前端專案需要進行的哪些額外的步驟。 注意:在安裝jenkins和nginx的 ......

    uj5u.com 2020-09-10 05:08:49 more
  • CODING DevOps 微服務專案實戰系列第一課,明天等你

    CODING DevOps 微服務專案實戰系列第一課**《DevOps 微服務專案實戰:DevOps 初體驗》**將由 CODING DevOps 開發工程師 王寬老師 向大家介紹 DevOps 的基本理念,并探討為什么現代開發活動需要 DevOps,同時將以 eShopOnContainers 項 ......

    uj5u.com 2020-09-10 05:09:14 more
  • CODING DevOps 微服務專案實戰系列第二課來啦!

    近年來,工程專案的結構越來越復雜,需要接入合適的持續集成流水線形式,才能滿足更多變的需求,那么如何優雅地使用 CI 能力提升生產效率呢?CODING DevOps 微服務專案實戰系列第二課 《DevOps 微服務專案實戰:CI 進階用法》 將由 CODING DevOps 全堆疊工程師 何晨哲老師 向 ......

    uj5u.com 2020-09-10 05:09:33 more
  • CODING DevOps 微服務專案實戰系列最后一課,周四開講!

    隨著軟體工程越來越復雜化,如何在 Kubernetes 集群進行灰度發布成為了生產部署的”必修課“,而如何實作安全可控、自動化的灰度發布也成為了持續部署重點關注的問題。CODING DevOps 微服務專案實戰系列最后一課:**《DevOps 微服務專案實戰:基于 Nginx-ingress 的自動 ......

    uj5u.com 2020-09-10 05:10:00 more
  • CODING 儀表盤功能正式推出,實作作業資料可視化!

    CODING 儀表盤功能現已正式推出!該功能旨在用一張張統計卡片的形式,統計并展示使用 CODING 中所產生的資料。這意味著無需額外的設定,就可以收集歸納寶貴的作業資料并予之量化分析。這些海量的資料皆會以圖表或串列的方式躍然紙上,方便團隊成員隨時查看各專案的進度、狀態和指標,云端協作迎來真正意義上 ......

    uj5u.com 2020-09-10 05:11:01 more
最新发布
  • windows系統git使用ssh方式和gitee/github進行同步

    使用git來clone專案有兩種方式:HTTPS和SSH:
    HTTPS:不管是誰,拿到url隨便clone,但是在push的時候需要驗證用戶名和密碼;
    SSH:clone的專案你必須是擁有者或者管理員,而且需要在clone前添加SSH Key。SSH 在push的時候,是不需要輸入用戶名的,如果配置... ......

    uj5u.com 2023-04-19 08:41:12 more
  • windows系統git使用ssh方式和gitee/github進行同步

    使用git來clone專案有兩種方式:HTTPS和SSH:
    HTTPS:不管是誰,拿到url隨便clone,但是在push的時候需要驗證用戶名和密碼;
    SSH:clone的專案你必須是擁有者或者管理員,而且需要在clone前添加SSH Key。SSH 在push的時候,是不需要輸入用戶名的,如果配置... ......

    uj5u.com 2023-04-19 08:35:34 more
  • 2023年農牧行業6大CRM系統、5大場景盤點

    在物聯網、大資料、云計算、人工智能、自動化技術等現代資訊技術蓬勃發展與逐步成熟的背景下,數字化正成為農牧行業供給側結構性變革與高質量發展的核心驅動因素。因此,改造和提升傳統農牧業、開拓創新現代智慧農牧業,加快推進農牧業的現代化、資訊化、數字化建設已成為農牧業發展的重要方向。 當下,企業數字化轉型已經 ......

    uj5u.com 2023-04-18 08:05:44 more
  • 2023年農牧行業6大CRM系統、5大場景盤點

    在物聯網、大資料、云計算、人工智能、自動化技術等現代資訊技術蓬勃發展與逐步成熟的背景下,數字化正成為農牧行業供給側結構性變革與高質量發展的核心驅動因素。因此,改造和提升傳統農牧業、開拓創新現代智慧農牧業,加快推進農牧業的現代化、資訊化、數字化建設已成為農牧業發展的重要方向。 當下,企業數字化轉型已經 ......

    uj5u.com 2023-04-18 08:00:18 more
  • 計算機組成原理—存盤器

    計算機組成原理—硬體結構 二、存盤器 1.概述 存盤器是計算機系統中的記憶設備,用來存放程式和資料 1.1存盤器的層次結構 快取-主存層次主要解決CPU和主存速度不匹配的問題,速度接近快取 主存-輔存層次主要解決存盤系統的容量問題,容量接近與價位接近于主存 2.主存盤器 2.1概述 主存與CPU的聯 ......

    uj5u.com 2023-04-17 08:20:31 more
  • 談一談我對協同開發的一些認識

    如今各互聯網公司普通都使用敏捷開發,采用小步快跑的形式來進行專案開發。如果是小專案或者小需求,那一個開發可能就搞定了。但對于電商等復雜的系統,其功能多,結構復雜,一個人肯定是搞不定的,所以都是很多人來共同開發維護。以我曾經待過的商城團隊為例,光是后端開發就有七十多人。 為了更好地開發這類大型系統,往 ......

    uj5u.com 2023-04-17 08:18:55 more
  • 專案管理PRINCE2核心知識點整理

    PRINCE2,即 PRoject IN Controlled Environment(受控環境中的專案)是一種結構化的專案管理方法論,由英國政府內閣商務部(OGC)推出,是英國專案管理標準。
    PRINCE2 作為一種開放的方法論,是一套結構化的專案管理流程,描述了如何以一種邏輯性的、有組織的方法,... ......

    uj5u.com 2023-04-17 08:18:51 more
  • 談一談我對協同開發的一些認識

    如今各互聯網公司普通都使用敏捷開發,采用小步快跑的形式來進行專案開發。如果是小專案或者小需求,那一個開發可能就搞定了。但對于電商等復雜的系統,其功能多,結構復雜,一個人肯定是搞不定的,所以都是很多人來共同開發維護。以我曾經待過的商城團隊為例,光是后端開發就有七十多人。 為了更好地開發這類大型系統,往 ......

    uj5u.com 2023-04-17 08:18:00 more
  • 專案管理PRINCE2核心知識點整理

    PRINCE2,即 PRoject IN Controlled Environment(受控環境中的專案)是一種結構化的專案管理方法論,由英國政府內閣商務部(OGC)推出,是英國專案管理標準。
    PRINCE2 作為一種開放的方法論,是一套結構化的專案管理流程,描述了如何以一種邏輯性的、有組織的方法,... ......

    uj5u.com 2023-04-17 08:17:55 more
  • 計算機組成原理—存盤器

    計算機組成原理—硬體結構 二、存盤器 1.概述 存盤器是計算機系統中的記憶設備,用來存放程式和資料 1.1存盤器的層次結構 快取-主存層次主要解決CPU和主存速度不匹配的問題,速度接近快取 主存-輔存層次主要解決存盤系統的容量問題,容量接近與價位接近于主存 2.主存盤器 2.1概述 主存與CPU的聯 ......

    uj5u.com 2023-04-17 08:12:06 more