随手记了笔记,等老师可以公开PPT之后对应补全,荣老师讲的太好了,十分敬佩
2021.04.01来填坑啦,但仍有一些细节的问题,没有搞清楚
基本概念
一个变量的声明有如下两个关键部分组成
- 变量类型:约定了申请多大的内存
- 变量名称:用于识别访问到这块内存
变量类型又可以分为两大类
- 非数组类型,其中包含指针类型
- 数组类型
非数组类型
- int
- double
- float
- 指针类型,下面详细介绍
- ...
sizeof(type)
求的都是变量约定占用空间的大小,因此该函数在很多时候编译器就可以计算
数组类型
int a[2]
变量类型为int [2]
,变量名称为a
float b[2][3]
变量类型float [2][3]
,变量名称为b
double c[2][3][4]
变量类型double[2][3][4]
,变量名称为c
- ...
sizeof(int[2])
是符合语法的
所有的数组类型,无论在表达上是二维、三维或者更高维度,但本质上均是特殊的一维构造类型,包含两个要素:
老师重点强调了C语言数组变量类型是一维的,没有多维之说
- 元素个数
- 元素类型
例如float b[2][3]
包含两个元素,元素类型是float[3]
指针类型
指针类型就是一个普通的非数组变量类型,任何一个变量类型,都有指向其的一个指针类型,反之依然。例如
int
、float
、double
<==> int*
、float*
、double*
int[2]
、float[2][3]
、double[2][3][4]
<==> int(*)[2]
、float(*)[2][3]
、double(*)[2][3][4]
指针变量类型本身也是一个非数组变量类型,也有其对应的指针类型
int*
、float*
、double*
<==> int(**)[2]
、float(**)[2][3]
、double(**)[2][3][4]
在之前的资料中,指针数组是指一个数组中,每个元素都是指针;数组指针是指一个指向数组的指针
1
2 char* strs[4]; // 指针数组,一个每个元素都是char*的数组
char (*pstr) [4]; // 数组指针,一个指向char[4]的指针老师强调了关于数组指针和指针数组的说法其实是不太有道理的,建议从数组类型的两个要素理解,上述的说法不值一提。
数据类型总结
关于Element Type和Corresponding Pointer Type下文将会介绍。
内存六元组模型
M = {Address, Variable_Type, Name, Size, Value, Value_Type}
M: 申明变量系统给分配的一段内存,粗略可以分为两部分:物理属性和逻辑属性,其中只有Address和Size是必不可少的,见malloc
的例子。
- 物理属性,机器知道的内容
- Address: 在内存中的第一个字节的地址,由系统分配,一旦确定无法修改
- Variable_Type:变量类型
- Name:变量名称
- Size:内存大小,必须要,且不可修改
- 逻辑属性,程序员知道的内容
- Value:内存表示的值
- Value_Type:表示值的类型
- 非数组类型,Value_Type=Variable_Type, Value是通过Value_Type去观察这段内存获得的值
- 数组类型,指向数组里元素变量类型的指针类型,Value是数组第一个元素所处内存的第一个字节编号(等于Address)
内存模型举例
int a=10
类别 值 Address 0x0028FF11 Variable Type int
Name a
Size 4 Value 10 Value Type int
int* p=NULL
类别 值 Address 0x0046A521 Variable Type int*
Name p Size 4 Value NULL
Value Type int*
int e[2]
类别 值 Address 0x0076AB11 Variable Type int[2]
Name p
Size 8 Value 0x0076AB11
和Address相等Value Type int*
int g[2][3]
类别 值 Address 0x0098B11D Variable Type int[2][3]
Name g Size 24 Value 0x0098B11D
和Address相等Value Type int(*)[3]
m_struct h
1
2
3
4struct m_struct{
int a;
float b;
}类别 值 Address 0x0085D611 Variable Type struct m_struct
Name h
Size 8 Value Invisible Value Type struct m_struct
malloc(16)
类别 值 Address 0x00351728 Variable Type N/A Name N/A Size 16 Value N/A Value Type N/A
对内存赋值
1 | int a; |
上述表达式会产生如下的过程
对内存取值
对内存取值的步骤为:定位内存 => 识别操作 => 获得返回值
定位内存
- 变量名定位
- 指针间接定位
识别操作
识别操作按照如下顺序进行识别,也就是说下面的操作是有优先级的
- 获得内存的首地址,&,返回二元组,值和类型
<0xxxxxxxxx, int*>
- 获得内存的大小,sizeof,返回二元组,值和类型
<4, size_t>
- 获得内存的表示值,除1,2 两种操作符以外,返回二元组,值和类型
<10, int>
- 获得内存的首地址,&,返回二元组,值和类型
返回值两元素
- 返回值类型
- 返回值的值
取值赋值举例
int a=10; int* p=&a
,中第二条语句的执行过程size_t c=sizeof(a)
,执行流程如下int b=a
,执行流程如下*p=20
,执行流程如下int* q=&(*p)
,执行流程如下
size_t c=sizeof(*p)
,执行流程如下int b=*p
,执行流程如下
指针变量加减操作
指针类型p,执行p+n的含义
例如*(p+1)=30
,执行过程如下
再探讨
使用指针进行定位
对指针p指向的内存空间的定位有两种方式,并且是完全等价的
*p
、*(p+n)
p[0]
、p[n]
Tips: 书上一般些int p; 我们可以写成int p,把int*写成一个整体,看成一个类型
再看int e[2]
类别 | 值 |
---|---|
Address | 0x0076AB11 |
Variable Type | int[2] |
Name | p |
Size | 8 |
Value | 0x0076AB11 和Address相等 |
Value Type | int* |
Q:Why Size=8?
A:sizeof(int) * 2
Q:Why Variable_Type=int[2]?
A:Variable Type需要能体现出变量的大小
Q:Why Value和Address一样?
A:Value指向第一个元素的下标,正好与整块变量的起始位置相同
Q:Why Type是int*
A:Value Type是数组中元素的指针类型,如此才可以
*(p+n)
或者p[n]
来定位数组中的元素Q:&e的返回值
A:
<0x0076AB11, int(*)[2]>
,被赋值的变量需要是Variable Type的指针类型,如下int (*x)[2] = &e
Q:sizeof(e)的返回值
A:
<8, size_t>
,返回的是变量e的SizeQ:e的返回值
A:
<0x0076AB11, int*>
,被赋值的变量需要是Value Type的类型,如下int* x = e
Q: 那么
e[0]
到底是个啥?&、sizeof、e[0]A:
根据定位之后找到的六元组
- &:
<0x0076AB11, int*>
- sizeof:
<4, size_t>
- e[0]:<?, int>
- &:
Q:
e=e+1
和e++
为啥会出错?A:数组的Value一定是指向数组第一个元素的地址编号,对于数组e来说,V的值必须和A相等。因此,e并不是一个常量,它的值不能更改是语法限制的,对常量修改的错误应该是:assignment of read-only variable 'e',而对e的修改会出现如下错误:
但是
int *p
是可以执行p++
的
再看int g[2][3]
int g[2][3]
类别 | 值 |
---|---|
Address | 0x0098B11D |
Variable Type | int[2][3] |
Name | g |
Size | 24 |
Value | 0x0098B11D 和Address相等 |
Value Type | int(*)[3] |
- &,
<0x0098B11D, int(*)[2][3]>
- sizeof,
<24, size_t>
- g,
<0x0098B11D, int(*)[3]>
再看g[1]
- &,
<0x0098B12F, int(*)[3]>
- sizeof,
<12, size_t>
g[1]
,<0x0098B12F, int*>
字符串
1 | char str1[6] = {'h', 'e', 'l', 'l', 'o', '\0'}; |
上述三种表达的区别?
我忘了,尴尬,在内存上没啥区别,在6元组上,第三个会和前两个有区别
数组变量作为函数参数
老师重点强调:c语言参数传递的机制是 Pass By Value
形参中:int*=int[]
,int(*)[3]=int[][3]
,数组中第一维信息的丢失是C语言所有数组类型变量内存的取值机制造成的
malloc问题
int* p = (int *)malloc(sizeof(int)*4);
,发生的流程