C語言之sizeof、內存分區

sizeof

  • sizeof 返回的是變量(單個變量或結構體)實際所佔用的空間的大小
typedef struct Person
{
	char a;
	int b;
}PERSON;

int main()
{
    std::cout << "Hello World!\n"; 
	printf("int size is %d\n", sizeof(int));
	double a = 3.14;
	printf("a size is %d\n",sizeof(double));
	PERSON xiaoming;
	printf("xiaoming size is %d\n",sizeof(xiaoming));
}

輸出結果:
Hello World!
int size is 4
a size is 8
xiaoming size is 8
  • 對齊模式問題
#pragma pack(1)
typedef struct Person
{
	char a;
	int b;
}PERSON;

int main()
{
	PERSON xiaoming;
	printf("xiaoming size is %d\n",sizeof(xiaoming));
}

輸出結果:
xiaoming size is 5
  • sizeof 返回的類型是unsigned int類型
void test(void)
{
	unsigned int a = 10;
	if (a - 20 > 0)
	{
		printf("大於0\n");
	}
	else
	{
		printf("小於0\n");
	}
}

輸出結果是“大於0”, 因爲sizeof()的返回值是unsigned int類型。
  • sizeof 計算數組,數組作爲函數參數會退化爲指向數組首元素的指針
int caculateArraySize(int arry[]) //傳數組時寫這種方式比較好
{
	return sizeof(arry);
}

void test1()
{
	int arr[] = {1,2,3,4,5,6};
	printf("arr size is %d\n",sizeof(arr));
	printf("sizeof arry is %d\n", caculateArraySize(arr));
}
輸出結果:
arr size is 24
sizeof arry is 4

變量的間接賦值

void test01()
{
	//直接賦值
	int a = 10;
	a = 100; 
	//間接賦值
	int *p = &a;
	*p = 200;
}
typedef struct Person
{
	char a;
	int b;
	char c;
	int d;
}PERSON;
void test02()
{
	PERSON p = {'a',100,'b',200};
	printf("p.d: %d\n",p.d);
	p.d = 2000;
	printf("%d\n",(char*)&p+12);  //輸出d的地址
	printf("%d\n",&(p.d));        //輸出d的地址
    printf("%d\n",*(int *)((char *)&p + 12));  //輸出d的值
   
    //&p
    int *pp=NULL;
    printf("pp:%d\n",pp);
    printf("pp+1:%d\n", pp+1); 	
}
輸出結果
p.d: 200
7338780
7338780
2000
pp:0
pp+1:4

內存分區概念

運行之前

  • 預處理:宏定義展開,頭文件展開,條件編譯,這裏並不檢查語法
  • 編譯: 檢查語法,並將預處理後文件編程生成彙編文件
  • 彙編:將彙編文件生成目標文件(二進制文件)
  • 鏈接:將目標文件鏈接爲可執行程序
    在沒有運行程序前,也就是說程序沒有加載到內存前,可執行程序內部已經分好3段信息,分別爲代碼區(text),數據區(data), 未初始化數據區(bss)
    棧變量和堆變量在運行時纔有
  • 代碼區 共享(避免內存浪費),只讀

運行之後

額外增加棧區,堆區

  • 堆區 heap:自己控制申請和釋放。程序泄漏問題。一般數據量較大時放堆上。
  • 棧區 stack:編譯器自動申請和釋放。一般棧大小固定。

全局靜態區

全局靜態區包含: 全局區,靜態區,常量區

extern int a; //外部鏈接屬性
static int a; //作用域在本文件內
常量區存放什麼? 
const char *p = "Hello World"//字符串常量前最好加上const
const 修飾的全局變量
常量區的數據,一旦初始化,不能修改,只讀內存。

棧區

不要返回局部變量的地址

int* myFun()
{
	//不要返回局部變量的地址,退出時,a的內存已經被回收
	int a=10;
	return &a;
}

void test()
{
	int *p = myFun();
	printf("*p=%d\n", *p); //雖然輸出結果是*p = 10,但是 之前變量a佔用的內存可能被其他變量佔用
}
char* getString()
{
	char str[] = "Hello world"; //數組在棧上
	return str;
}

void test2()
{
	char *s = NULL;
	s = getString();
	printf("s = %s\n",s);
}

在這裏插入圖片描述

堆區

int *getSpace()
{
	int *p = (int *)malloc(sizeof(int) * 5);
	if (NULL == p) //NULL放前面習慣,因爲當只有一個等號時會報錯
	{
		return NULL;
	}
	//主要是連續內存空間,都能使用下標的方式方位內存
	for (int i=0;i<5;++i)  //前置++效率比較高,
	{
		p[i] = 100 + i;
	}
	//退出時在棧上的p會釋放掉,但是堆上的分配的內存還在
	return p;
}

void test3()
{
	int *ret = getSpace();
	for (int i=0;i<5;i++)
	{
		printf("%d \n", ret[i]);
	}
	free(ret);
	ret = NULL;  //釋放完後最好置成空
}
void allocateSpace(char *p)
{
	p = (char*)malloc(100);
	memset(p,0,100);
	strcpy(p,"hello world");
}

void test04()
{
	char *p = NULL;
	allocateSpace(p);  //函數的形參也是個局部變量,函數返回時也要釋放
	printf("p=%s\n",p);
}

在這裏插入圖片描述

void allocateSpace2(char **p)//爲什麼要寫兩個*,便於區分指向的數據是什麼數據
{
	char *tmp =  (char*)malloc(100);
	memset(tmp, 0, 100);
	strcpy(tmp, "hello world");
	*p = tmp;
}

void test05()
{
	char *p = NULL;
	allocateSpace2(&p);
	printf("p=%s\n", p);
}

在這裏插入圖片描述

void fun(char **p)  //對指針取地址,升一個*,變成兩個**
{
	...;
	*p = xx;
}

test()
{
	char *p =NULL;
	fun(&p);
}

在這裏插入圖片描述在這裏插入圖片描述

全局區、靜態區

//extern int a = 10; 默認外部鏈接
int a= 10;         //全局區
//靜態全部變量時內部鏈接
static int b = 20; //靜態區
//內部鏈接和外部鏈接有什麼區別:
//1.static 只能在當前文件內訪問
//2.外部鏈接,此變量可被外部訪問

//1.全局靜態變量和局部靜態變量都存儲在靜態區,在程序運行期間都是合法有效
//2.局部靜態變量符號的可見範圍僅限於當前函數內部,全局靜態變量可見範圍從定義到文件結尾
//3.頭文件不參與編譯,每一個.c文件,叫做一個編譯單元
//4.編譯器獨立編譯每個.c文件
void test01()
{
	static int c = 30;
}

常量區、字符串常量、全局const變量

  • const全局和局部變量區別?
  1. const全局變量在常量區,不能修改(直接或間接)
const int g_c = 100;
//&g_c 的類型爲const int*類型
int *p = &g_c;  //此時編譯器會報warning, 不同類型的兩個指針賦值,就會報錯
//解決方法:
int *p = (int *)&g_c; //強轉
*p = 200;  //此時編譯器會報錯,全局const放在常量區,一旦初始化,不能修改
  1. const局部變量
void test()
{
	//棧上
	const int a = 100;
	//a=200;  //編譯器報錯
	int *p = (int *)&a;  //跳過編譯器的檢查了,運行時把a的值修改了
	*p = 200; 
	printf("a=%d\n",a);  //a的值被修改
}

字符串常量區

void test()
{
	char *p = "Hello World";
	printf("%d\n",&"Hello World");
	printf("%d\n",p);
	//兩者輸出的地址是一樣的
}
  • ANSI C中規定:修改字符串常量,結果是未定義的
  • ANSI C並沒有規定編譯器的實現者對字符串的處理,例如:
    1.有些編譯器可以修改字符串常量,有些編譯器不可修改
    2.有些編譯器把多個相同的字符串常量看成一個,有些進行優化(節省空間),有些則不進行優化。
void test()
{
	char *p1 = "Hello world";
	char *p2 = "Hello world";
	printf("%d\n",p1);
	printf("%d\n",p2);
	//兩者輸出根據編譯器而不同
}

3.所以儘量不要去修改字符串常量!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章