C++中string轉換為char*類型返回後亂碼問題

来源:https://www.cnblogs.com/hqdong123/archive/2020/07/05/13252396.html
-Advertisement-
Play Games

問題來源: 在寫二叉樹序列化與反序列化時發現序列化函數為char* Serialize1(TreeNode *root) 其函數返回類型為char*,但是我在實現的過程中為了更方便的操作添加字元串使用的是C++中string類型的變數,這就導致我最後得到的結果res是string類型,若是要返回需要 ...


問題來源:

  在寫二叉樹序列化與反序列化時發現序列化函數為char* Serialize1(TreeNode *root)  其函數返回類型為char*,但是我在實現的過程中為了更方便的操作添加字元串使用的是C++中string類型的變數,這就導致我最後得到的結果res是string類型,若是要返回需要轉化為char *類型。而等我將string類型轉為char*後返回在主函數中就成了亂碼。

 

先直接說最後的解決辦法:

  第一種:定義一個char數組,數組長度為stringlength+1,將string的內容依次賦值給char數組,最後加上'\0' ,然後返回char數組名就行了。

  第二種:將string定義為類的成員變數

  就貼第一種方法的代碼

char *result = new char[res.length() + 1];  //定義需要返回的result對象
for (int i = 0; i < res.length(); ++i)
{
     result[i] = res[i];    //將string類型的res內容都放到result內
}
result[res.length()] = '\0';  //加上結束符\0

 

再說說我嘗試的方法

嘗試1:

  一開始我是直接定義char *result=&res[0];想要通過這個語句直接返回這個string類型變數的首地址,但是失敗了,在主函數中的結果變數是亂碼 "葺葺葺葺葺葺葺葺葺葺"

嘗試2:

  於是我開始思考可能的原因  

  1.考慮到局部變數可能隨著函數釋放,因此導致我返回的指針指向的內容隨著函數一起釋放導致了亂碼,但一想到平時寫的函數都是正常返回的,所以這個我很快否決了,但最後發現這個思路是對的。至於平常寫的函數都是正常返回則是因為沒有涉及到類型轉換。  

  2.通過VS的調試發現我使用的char *result=&res[0]語句返回的是res的首個元素地址,並不是res的首地址,因為string作為std封裝的數據結構除了char*這種從C吸收過來的結構還有記憶體分配allocate這些東西所以導致其記憶體地址並不像char數組那樣是首個元素地址

  所以我想乾脆把整個string類型的res都賦值給char *類型的result

  所以我嘗試了char *result=(char*)res.data();語句,將res(res是string類型的結果)賦給result,轉換是成功的,但返回值依舊失效(且這種轉換需要自己加上\0結束符)

  然後嘗試char *result=(char*)res.c_str(); 結果也是成功的,但返回值依舊失效。

  最後嘗試,用new新建一個char數組,將res的內容全部拷貝到char數組內,然後將數組名返回,終於成功。

 

問題根源

  通過VS調試我最終發現了問題根源所在:res所占記憶體隨著函數結束而被釋放

  這是函數未執行完的調試界面

  這是執行完調試界面

 

   很明顯:res沒有了,在函數執行完畢後res記憶體也跟著被釋放了而char數組result卻仍然存在。他們的不同點在哪:result是返回值 

  我們知道函數的函數棧知識點,棧記憶體放著函數入口地址,局部變數,返回地址等,我猜測result作為要被返回的對象其記憶體空間應該是不隨著函數一起被釋放的,也就是主函數內的返回值應該還是用那塊記憶體,經過測試這個結論是對的。主函數中的變數的確是使用返回值那塊記憶體。

  到這裡就發現了,雖然執行char* result=(char*)res.c_str()語句能讓result內是完整的結果內容(也就是轉換完成),但result會隨著string類型的res的釋放而導致char*類型的result所指向的記憶體空間內容全部清空。最後雖然返回了result所指的空間但裡面的內容早就被清空了。就好比把記憶體比作一塊地,res先在其上面蓋了一座房子,而使用上面轉換語句後result也是房子的主人,這下房子有了兩個主人,他們都能對房子進行操作。正因為他們都能進行操作,當他們所屬函數結束也就是res大限到來之時,res將自己建立的房子銷毀了。那麼result也就沒有房子可住了。也就是他們公用的那片記憶體被初始化,這時主函數雖然收到了返回地址但那片地址已經沒有內容了。也就導致亂碼了。

  到這裡,問題的根源就知道了,那麼解決方法也就很明顯了:1.記憶體分離,將res和result的所屬記憶體地址分開。2.或者想辦法讓res所在記憶體不隨著函數結束而釋放.

  具體實現:

  第1種.上面那段new新建char*變數的代碼。為result重新開闢一段空間。

  第2種.i:若在類里:將res設為類的成員變數或者static成員變數(最好不要,能成功但會有新問題出現),他們都不會隨著成員函數的結束而釋放。區別就是普通成員變數會隨著對象的釋放而釋放,static不會,它是存放在靜態存儲區

      ii:若是像C這類面向過程代碼就是將res設為全局變數即可

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在“JavaScript圖形實例:迭代函數系統生成圖形”一文中,我們介紹了採用迭代函數系統(Iterated Function System,IFS)創建分形圖案的一些實例。在該文中,仿射變換函數W的一般形式為 X1=a*X0 + b*Y0 + e Y1=c*X0 + d*Y0 + f 給定不同的I ...
  • First. 什麼是 algolia search? 根據algolia官方網站自我闡述:Algolia是一個托管搜索引擎,提供全文,數字和多面搜索,能夠從第一次擊鍵中提供實時結果。 Algolia強大的API可讓您快速無縫地在網站和移動應用程式中實施搜索。我們的搜索API每月為成千上萬的公司提供數 ...
  • 在“JavaScript圖形實例:SierPinski三角形” 和“JavaScript圖形實例:Levy曲線及其變形”等文章中我們介紹了通過遞歸生成分形圖形的方法。我們可以將繪製的分形圖形每隔一定的時間間隔後,增加遞歸深度重新繪製一次,這樣就可以得到分形圖形的動態生成效果。 1.SierPinsk ...
  • Nuxt 是 Vue 項目伺服器端渲染(SSR)解決方案。而在使用時,就會遇到前後端分離情況下的功能變數名稱或埠不一致導致的跨域問題。本文將介紹如何通過設置代理解決 Nuxt 與 axios 集成的跨域問題。 ...
  • Electron是一個可以使用 JavaScript,HTML 和 CSS 構建跨平臺桌面應用程式的開源框架。 本文主要分享一下採用vue + electron開發桌面程式的搭建過程。 1. 環境準備 這裡採用的是vue-cli3.x,可以通過下麵的指令查看當前vue-cli的版本: vue --v ...
  • #讀後感# 《企業IT架構轉型之道-阿裡巴巴中台戰略思想與架構實戰》鐘華(花名:古謙)編著,阿裡巴巴中間件首席架構師,15年中間件領域行業經驗。 進入新公司第一天,領導就給了這本書,慚愧,剛看完... 一本推動“中台建設”指導性實戰用書,濃縮了10來年的經驗,從架構層面詳細敘述阿裡共用業務事業部:技 ...
  • 抽象工廠模式 優化抽象工廠 非同步工廠 在學習抽象工廠模式前,先來回顧一下前面的簡單工廠和工廠方法模式。簡單工廠的職責非常簡單:構造某個實體類型,然後把實例作為抽象類型返回; 工廠方法模式則進一步抽象出一個抽象的創建者和一個抽象的產品類型,而實際的執行過程是具體工廠創建具體的產品類型,具體工廠和具體產 ...
  • 我的LeetCode:https://leetcode-cn.com/u/ituring/ 我的LeetCode刷題源碼[GitHub]:https://github.com/izhoujie/Algorithmcii LeetCode 題目 字元串有三種編輯操作:插入一個字元、刪除一個字元或者替換 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...