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全局和局部變量區別?
- const全局變量在常量區,不能修改(直接或間接)
const int g_c = 100;
//&g_c 的類型爲const int*類型
int *p = &g_c; //此時編譯器會報warning, 不同類型的兩個指針賦值,就會報錯
//解決方法:
int *p = (int *)&g_c; //強轉
*p = 200; //此時編譯器會報錯,全局const放在常量區,一旦初始化,不能修改
- 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.所以儘量不要去修改字符串常量!