1.1 数据类型本质分析
1.1.1 数据类型概念
- 类型相同的数据有相同的表示方式,存储格式以及相关的
- 程序中使用的所有数据必定属于某一种数据类型
1.1.2 数据类型的本质思考
- 思考数据类型和内存的关系? 这和数据类型的本质有关系
- C/C++为什么会引入数据类型?为了方便的表示现实世界中的人和物
1.1.3 数据类型的本质
注意:一定要站在C/C++编译器的角度思考问题
1.1.3.1 本质
- 数据类型可理解为创建变量的模具(模子);是固定内存大小的别名。
- 数据类型的作用:编译器预算对象(变量)分配的内存空间大小
- 程序举例,如何求数据类型的大小sizeof(int *)
1.1.3.2 案例分析
int main31()
{
int a; //告诉c编译器分配4个字节的内存
int b[10] ; //告诉c编译器分配40个自己内存
// b+1 &b+1 结果不一样 //b &b所代表的数据类型不一样
//b 代表的数组首元素的地址
//&b代表的是整个数组的地址
printf("b:%d, b+1:%d, &b:%d, &b+1:%d \n", b, b+1, &b, &b+1);
printf("sizeof(b):%d \n", sizeof(b)); //40
printf("sizeof(a):%d \n ", sizeof(a)); //4
printf("sizeof(b):%d \n", sizeof(&b)); //4 (取地址就是地址占用的字节数)
//
return 0;
}
运行结果(VS2015中):
运行结果(64位的Linux中)
注意64位中,输出地址的时候不能使用%d,而是需要使用%ld,否则会有警告信息
,
警告信息如下:
1.1.4 数据类型的封装
- 1、void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。
- 2、用法1:数据类型的封装
int InitHardEnv(void **handle);
典型的如内存操作函数memcpy和memset的函数原型分别为
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num ); - 3、用法2: void修饰函数返回值和参数,仅表示无。
如果函数没有返回值,那么应该将其声明为void型
如果函数没有参数,应该声明其参数为void
int function(void)
{return 1;} - 4、void指针的意义
C语言规定只有相同类型的指针才可以相互赋值
void指针作为左值用于“接收”任意类型的指针
void指针作为右值赋值给其它指针时需要强制类型转换
int *p1 = NULL;
char *p2 = (char *)malloc(sizoeof(char)*20); --强制类型转换 - 5、不存在void类型的变量
C语言没有定义void究竟是多大内存的别名 - 6、数据类型的封装其实就是为了在被调用函数不想让上层应用知道,函数内部的实现。
1.2 变量本质分析
1.2.1 变量本质
1、程序通过变量来申请和命名内存空间 int a = 0
2、通过变量名访问内存空间
(一段连续)内存空间的别名(是一个门牌号
)
3、修改变量有几种方法?
- 1、直接
- 2、间接。内存有地址编号,拿到地址编号也可以修改内存;于是横空出世了!(编程案例)
*((int *)12345678) =20;
//这句代码的理解
//把12345678这个地址解释成int* ,然后对4个字节的地址空间进行赋值
- 3、内存空间可以再取给别名吗?(C++中的引用)
4、数据类型和变量的关系
- 通过数据类型定义变量
5、总结及思考题
1 对内存,可读可写;2通过变量往内存读写数据;3 不是向变量读写数据,而是向变量所代表的内存空间中写数据。问:变量跑哪去了?
变量名放到了代码区
思考1:变量三要素(名称、大小、作用域),变量的生命周期?
思考2:C++编译器是如何管理函数1,函数2变量之间的关系的?
====》引出两个重要话题:
内存四区模型
函数调用模型
1.3内存四区
内存四区分为:堆,栈,全局区,代码区
- 堆区 Malloc/new free/delete ,操作系统管理、
- 栈区 程序局部变量
- 全局区,常量和全局变量,操作系统管理
- 代码区 操作系统管理
1.3.1 静态区
经典语录:
指针也是一种变量
指针变量和它所指向的内存空间变量是不同的变量
指针变量指代的是指针中的值
指针所指向的内存空间变量是指指针中的值所指向的内存空间
指针中的变量的值是一个地址,这个地址指向一段内存空间
不断改变指针的值,就是不断改变指针所指向的内存空间
- 静态区代码示意图
1.3.2 堆栈区理解
- 代码示例
//堆
char *getMem(int num)
{
char *p1 = NULL;
p1 = (char *)malloc(sizeof(char) * num);
if (p1 == NULL) return NULL;
return p1;
}
//栈
//注意 return不是把内存块 64个字节,给return出来,而是把内存块的首地址(内存的标号0xaa11) ,返回给 tmp
// 理解指针的关键,是内存. 没有内存哪里的指针
char *getMem2()
{
char buf[64]; //临时变量 栈区存放
strcpy(buf, "123456789");
//printf("buf:%s\n", buf);
return buf;
}
int main()
{
char *tmp = NULL;
tmp = getMem(10);
if (tmp == NULL)
{
return 0;
}
strcpy(tmp, "111222"); //向tmp做指向的内存空间中copy数据,不要理解成向tmp这个变量中copy数据,tmp变量32平台就是4个字节
//tmp = getMem2(); //失败
tmp = 0xaa11;
return 0;
}
- 堆栈区示意图
1.3.3 有关野指针的内存四区
int main()
{
char buf[100];
//byte b1 = new byte[100];
int a = 10; //分配4个字节的内存 栈区也叫临时区
int *p;//分配4个字节的内存
p = &a; //cpu执行的代码,放在代码区
*p = 20; //
{
char *p2 = NULL; //分配4个字节的内存 栈区也叫临时区
p2 = (char *)malloc(100); //内存泄露概念
if (p2 != NULL)
{
free(p2);
//p2 = NULL; 若不写,实验效果,分析原因
}
if (p2 != NULL)
{
free(p2);
}
}
return 0;
}
1.4 函数调用
1.4.1 调用模型
1.4.2 理论
-
main 函数中可以在栈分配内存,堆上分配内存,全局区分配内存 可以被fa,fb使用
-
fb 申请的内存
-
fb 中在栈上分配的内存,不能被fa和main函数使用
-
fb中malloc的内存,可以被main函数使用
-
fb中全局区分配“abcdgb”,内存可以被fa,main函数使用
==========================
如何建立正确的程序运行内存布局图
内存四区模型&函数调用模型
函数内元素
深入理解数据类型和变量“内存”属性
一级指针内存布局图(int ,char)
二级指针内存布局图(int ** char **)
函数间
主调函数分配内存,还是被调用函数分配内存
主调函数如何使用被调用函数分配的内存(技术关键点:指针做函数参数)
======》学习指针的技术路线图
===============================
压死初学者的三座大山:(数组类型,数组指针,数组类型和数组指针类型的关系) 后面总结
参考一 : 狄泰软件课程
参考二 : 传智扫地僧老师课程
如有侵权:请联系邮箱 1986005934@qq.com