qingsongzdq 2019-06-28
数据类型的作用:编译器预算对象(变量)分配的内存空间大小
int a; // 告诉编译器分配4个字节的内存
typedef起别名sizeof()测类型大小void数据类型(无类型,万能类型)
如果函数没有返回值,必须用void修饰
// 对函数返回的限定 void fun(int a);
如果函数没有参数,参数可以用void修饰
// 对函数参数的限定 int fun(void);
不能定义void类型的普通变量
void a; // error,不能确定分配内存空间的大小
万能指针
void * p; // ok, 万能指针,指针类型都是4个字节,函数参数,函数有返回值
//1. void* 可以指向任何类型的数据,被称为万能指针
void test03()
{
int a = 10;
void* p = NULL;
p=&a;
printf("a:%d\n",*(int*)p);
char c = 'a';
p=&c;
printf("c:%c\n",*(char*)p);
}
//2. void* 常用于数据类型的封装
void test04()
{
void * memcpy(void * _Dst, const void * _Src, size_t _Size);
}sizeof操作符注意事项
sizeof返回的占用空间大小是为这个变量开辟的大小,而不只是它用到的空间。所以对结构体用的时候,大多情况下就得考虑字节对齐的问题了;sizeof返回的数据结果类型是unsigned int;要注意数组名和指针变量的区别。通常情况下,我们总觉得数组名和指针变量差不 多,但是在用sizeof的时候差别很大:
sizeof返回的是整个数组的大小示例代码:
//1. sizeof基本用法
void test01()
{
int a = 10;
printf("len:%d\n", sizeof(a));
printf("len:%d\n", sizeof(int));
printf("len:%d\n", sizeof a);
}
//2. sizeof 结果类型
void test02()
{
unsigned int a = 10;
if (a - 11 < 0)
{
printf("结果小于0\n");
}
else
{
printf("结果大于0\n");
}
int b = 5;
if (sizeof(b) - 10 < 0)
{
printf("结果小于0\n");
}
else
{
printf("结果大于0\n");
}
}
//3. sizeof 碰到数组
void TestArray(int arr[])
{
printf("TestArray arr size:%d\n",sizeof(arr));
}
void test03()
{
int arr[] = { 10, 20, 30, 40, 50 };
printf("array size: %d\n",sizeof(arr));
//数组名在某些情况下等价于指针
int* pArr = arr;
printf("arr[2]:%d\n",pArr[2]);
printf("array size: %d\n", sizeof(pArr));
//数组做函数函数参数,将退化为指针,在函数内部不再返回数组大小
TestArray(arr);
}既能读又能写的内存对象,称为变量;
若一旦初始化后不能修改的对象则称为常量。
变量定义的形式:类型 标识符,标识符,…,标识符
int a = 0;修改变量的两种方式:
void test()
{
int a = 10;
//1. 直接修改
a = 20;
printf("直接修改,a:%d\n",a);
//2. 间接修改
int* p = &a;
*p = 30;
printf("间接修改,a:%d\n", a);
}程序运行之前:
C程序编译过程1)预处理:宏定义展开、头文件展开、条件编译,这里并不会检查语法
2)编译:检查语法,将预处理后文件编译生成汇编文件
3)汇编:将汇编文件生成目标文件(二进制文件)
4)链接:将目标文件链接为可执行程序
可执行程序内部已经分好3段信息,分别为代码区text、数据区data和未初始化数据区bss 3个部分(有些人直接把data和bss合起来叫做静态区或全局区)。
总体来讲说,程序源代码被编译之后主要分成两种段:程序指令(代码区)和程序数据(数据区)。代码段属于程序指令,而数据域段和.bss段属于程序数据。
程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的,程序运行期间不能改变。然后,运行可执行程序,操作系统把物理硬盘程序load(加载)到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)之外,还额外增加了栈区、堆区。
总的来说,内存分区模型:
代码区:可执行代码段,不可修改。未初始化数据区(BSS):全局未初始化,静态未初始化数据,数据的生存周期为整个程序运行过程 。
全局初始化数据区/静态数据区(data segment):全局初始化,静态初始化数据,文字常量(只读),数据的生存周期为整个程序运行过程。
栈区(stack):先进后出的内存结构,由编译器自动分配释放 ,存放函数的参数值、返回值、局部变量等
堆区(heap):容量要远远大于栈,用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
栈区
由系统进行的内存管理,主要存放函数的参数以及局部变量,函数完成后系统自动释放。
#char* func()
{
char p[] = "hello world!"; //在栈区存储 乱码
printf("%s\n", p);
return p;
}
void test()
{
char* p = NULL;
p=func();
printf("%s\n",p);
}堆区
手工申请,手工释放
char* func()
{
char* str = malloc(100);
strcpy(str, "hello world!");
printf("%s\n",str);
return str;
}
void test01()
{
char* p = NULL;
p=func();
printf("%s\n",p);
}
void allocateSpace(char* p)
{
p=malloc(100);
strcpy(p, "hello world!");
printf("%s\n", p);
}
void test02()
{
char* p = NULL;
allocateSpace(p);
printf("%s\n", p);
}
堆分配内存API:
void *calloc(size_t nmemb, size_t size); /* * 功能: * 在内存动态存储区中分配nmemb块长度为size字节的连续区域。calloc自动将分配的内存置0。 * 参数: * nmemb:所需内存单元数量 * size:每个内存单元的大小(单位:字节) * 返回值: * 成功:分配空间的起始地址 * 失败:NULL */
全局/静态区
全局静态区内的变量在编译阶段已经分配好内存空间并初始化。这块内存在程序运行期间一直存在,它主要存储全局变量、静态变量和常量。
int v1 = 10; //全局/静态区
const int v2 = 20; //常量,一旦初始化,不可修改
static int v3 = 20; //全局/静态区
char *p1; //全局/静态区,编译器默认初始化为NULL
//那么全局static int 和 全局int变量有什么区别?
void test()
{
static int v4 = 20; //全局/静态区
}
// 加深理解
char* func()
{
static char arr[] = "hello world!"; //在静态区存储 可读可写
arr[2] = 'c';
char* p = "hello world!"; //全局/静态区-字符串常量区
//p[2] = 'c'; //只读,不可修改
printf("%d\n",arr);
printf("%d\n",p);
printf("%s\n", arr);
return arr;
}
void test()
{
char* p = func();
printf("%s\n",p);
}总结:
数据区包括:堆,栈,全局/静态存储区。
全局/静态存储区包括:常量区,全局区、静态区。
常量区包括:字符串常量区、常变量区。
代码区:存放程序编译后的二进制代码,不可寻址区。
可以说,C/C++内存分区其实只有两个,即代码区和数据区。
int func(int a,int b)
{
int t_a = a;
int t_b = b;
return t_a + t_b;
}
int main()
{
int ret = 0;
ret = func(10, 20);
return EXIT_SUCCESS;
}

//1. 栈的生长方向
void test01()
{
int a = 10;
int b = 20;
int c = 30;
int d = 40;
printf("a = %d\n", &a);
printf("b = %d\n", &b);
printf("c = %d\n", &c);
printf("d = %d\n", &d);
//a的地址大于b的地址,故而生长方向向下
}
//2. 内存生长方向(小端模式)
void test02()
{
//高位字节 -> 地位字节
int num = 0xaabbccdd;
unsigned char* p = #
//从首地址开始的第一个字节
printf("%x\n",*p);
printf("%x\n", *(p + 1));
printf("%x\n", *(p + 2));
printf("%x\n", *(p + 3));
}