本文介紹在Ubuntu12.10,g++環境下C++內存分配問題。並由此解釋在用g++編譯時,字符串常量賦值給字符指針類型時提示“warning:deprecated conversion from string constant to 'char *'”警告的原因。
一、C++內存分配
C++編譯器將應用程序的內存空間分成四個部分,從內存低地址開始依次爲:代碼和常量區(用於存儲只讀的代碼數據和常量值)、全局變量和靜態變量區(用於存儲全局變量和靜態變量)、堆區(用於保存new、malloc申請的內存)、棧區(用於保存函數返回地址、局部變量等)。
我們將用如下代碼來測試我們的假設:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
using namespace std;
int quanju;/*全局變量,全局區/靜態區(static)*/
void fun(int f_jubu); /*代碼區*/
int main(void)
{
int m_jubu;/*棧區(stack)*/
static int m_jingtai;/*靜態變量,全局區/靜態區(static)*/
const int conInt = 5;/*main中的局部變量,位於棧區,被const修飾,變量值不可變*/
static const int sconInt = 6;/*常量,位於常量區*/
char *m_zifum,*m_zifuc = "hello";/*指針本身位於棧。指向字符串"hello",位於代碼區/常量區*/
char* zifuc_hw = "hello world";/*字符串"hello world",位於常量區*/
/*正確的賦值方式*/
char *zif = (char*)malloc(sizeof(char) * 6);
strcpy(zif, "hello");
cout<<zif<<endl;
cout<<m_zifuc<<endl;
void (*pfun)(int); /*棧區(stack)*/
int (*pmain)(void);
pfun=&fun;
pmain = &main;
m_zifum = (char *)malloc(sizeof(char)*10);/*指針內容指向分配空間,位於堆區(heap)*/
pfun(1);
cout<<"全局變量 :"<<&quanju<<endl;
cout<<"main靜態變量 :"<<&m_jingtai<<endl;
cout<<"main字符串常量(hello) :"<<(void*)m_zifuc<<endl;
cout<<"main字符串常量(h w) :"<<(void*)zifuc_hw<<endl;
cout<<"main局部變量(const修飾):"<<&conInt<<endl;
cout<<"main靜態整型常量 :"<<&sconInt<<endl;
cout<<"fun函數代碼地址 :"<<(void*)pfun<<endl;
cout<<"main函數代碼地址 :"<<(void*)pmain<<endl;
cout<<"main字符串(堆分配) :"<<(void*)m_zifum<<endl;
cout<<"main函數函數指針變量 :"<<&pfun<<endl;
cout<<"main局部變量 :"<<&m_jubu<<endl;
cout<<"main局部變量 :"<<&m_zifuc<<endl;
cout<<"main局部變量 :"<<&m_zifum<<endl;
return 0;
}
void fun(int f_jubu)
{
static int f_jingtai;
char* f_zifuc = "hello";
cout<<"fun靜態變量 :"<<&f_jingtai<<endl;/*靜態變量,位於靜態區*/
cout<<"fun字符串常量(hello) :"<<(void*)f_zifuc<<endl;/*常量,位於常量區,同main中的“hello”地址相同*/
cout<<"fun形參變量 :"<<&f_jubu<<endl;/*棧區(stack),但是與主函數中m_jubu位於不同的棧*/
cout<<"fun局部變量 :"<<&f_zifuc<<endl;
}
實驗結果:
hello
fun字符串常量(hello) : 0x8048c58
main字符串常量(hello) : 0x8048c58
main字符串常量(hw) : 0x8048c5e
main靜態整型常量 : 0x8048e64
main函數代碼地址 : 0x80487dc
fun函數代碼地址 : 0x8048a73
fun靜態變量 : 0x804a0f8
main靜態變量 : 0x804a0fc
全局變量 : 0x804a0f0
main字符串(堆分配) : 0x9ed0008
fun形參變量 : 0xbfe0e8b0
fun局部變量 : 0xbfe0e89c
main函數函數指針變量 : 0xbfe0e8d4
main局部變量 : 0xbfe0e8c8
main局部變量 : 0xbfe0e8d0
main局部變量 : 0xbfe0e8cc
main局部變量(const修飾) :0xbfe879d8
二、堆區和棧區的比較
兩個區的主要區別在於以下幾個方面:
1、管理方式不同;
2、空間大小不同;
3、能否產生碎片不同;
4、生長方向不同;
5、分配方式不同;
6、分配效率不同;
棧是一塊連續的內存空間,具有後進先出的特點因此不存在內存碎片,而且計算機底層直接提供支持,分配效率高。無需程序員管理,編譯器會自動申請和釋放。
堆的管理方式一般是採用鏈表管理可用的內存塊,當用戶申請一塊內存時,系統從可用內存塊中查找一塊滿足要求的內存空間分配給用戶使用;用戶釋放內存後,系統將其回收。因此堆的內存管理需要由程序員來進行,分配效率不如棧。
三、字符串賦值給指針變量的告警
在用g++作編譯器時,將字符串常量賦值給指針變量將得到“warning:deprecated conversion from string constant to 'char *'”的警告。例如:char*ptrSt = “hello”;
這是因爲將字符指針變量ptrSt指向一個位於常量區的字符串常量“hello”。如果在運行時修改ptrSt指向內存的值,例如:ptrSt[0]= 'j',將會拋出異常,在linux的g++下,拋出了“Segmentation fault”的異常。
因此我們在將一個字符串常量賦值給字符指針變量時,應該將該變量限定爲const型,即const char*。如果需要在代碼中修改字符指針指向的變量的值,應該採用如下兩種方式:
chars[] = “hello”;
char*ptrSt = s;
或者
char*ptrSt = (char*)malloc(sizeof(char) * 6);
strcpy(ptrSt,“hello”);
Reference
1.C++內存分配方式詳解——堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區