建立正确程序运行内存的布局图(印象图)
内存四区模型图;
函数调用模型图;
数据类型的本质:固定大小内存块的别名
对于数组变量b[10];
b+1,与&b+1结果不一样;
b代表的是数组首元素的地址
&b代表的是整个数组的首地址
保证指针所指向的内存空间可以被修改(常量区的变量不能被修改)
指针做函数参数,形参有多级指针的时候,站在编译器的角度,只需要分配4个字节的内存(32bit平台)
当我们使用内存的时候才关心指针所指向的内存是一维的还是两维的
指针也是一种数据类型,指针的数据类型是指向的内存空间的数据类型
含义:指针步长(p++),根据所指内存空间的数据类型来确定。
野指针产生的原因:
指针变量与它指向的内存空间是两个不同的概念,释放指针所指的内存空间了但是指针变量本身没有重置为NULL;
避免方法:定义指针的时候初始化为Null,释放指针所指的内存空间以后将指针变量重置为Null;
地址0开始的内存空间是操作系统保护的空间,应用程序不能随便的往里面写;
间接赋值的工程意义:
函数调用时,形参传给实参,用实参取地址传给形参,在被调用函数里面用*p(间接赋值)来改变实参,把运算结果传出去。
间接赋值需要满足三个条件:
1)两个变量(通常是一个实参,一个形参);
2)建立关系,实参取地址赋给形参指针;
3)*p 形参间接修改实参的值。
主调函数和被调函数的特点:
a)主调函数可以把堆区,栈区,全局数据内存地址传给被调用函数;
b)被调用函数只能返回堆区,全局数据;
内存分配:指针作为函数参数是有输入和输出的特性。
应用指针必须与函数调用相结合(指针做函数参数)
编号 |
指针函数参数 内存分配方式(级别+堆栈) |
主调函数实参 | 被调函数形参 | 备注 | |||||||||
01 | 一级指针作输入 |
|
|
|
|
||||||||
02 | 一级指针作为输出 | 栈 | 使用 | 结果输出 | 常用 | ||||||||
03 | 二级指针作为输入 |
|
|
|
|
||||||||
04 | 二级指针作为输出 | 堆 | 使用 | 分配 | 常用,但不建议使用,转化为02 | ||||||||
05 | 三级指针作为输出 | 堆 | 使用 | 分配 | 不常用 |
数组变量的变量名是个常量指针,是为了保证在析构内存的时候,保证所有的数组空间都被释放;
指针作为函数参数(输入、输出时),需要考虑参数是否能被写入。
如果不能被写入:则实参改为数组形式
c语言中没有你不知道的,只有你不会调
java语言中国没有你不会调的,只有你不知道的
在C语言中 Const是个伪命题,不是我们想象的那样。
例如 const int a=100;
int *p =&a;
*p = 1;
则此时a的值为1;
const char * p //修饰的是指针所指向的内存空间变量
char* const p //修饰的是指针变量
函数调用时,用n级指针(形参)改变n-1级指针(实参)的值
数组名是数组首元素的地址,它是一个常量;
数组首元素的地址与数组的地址相等,
int a[10];
printf("得到整个数组的地址:%d\n",&a);
printf("得到首元素的地址:%d\n",a);
二维数组参数退化的问题
二维数组可以看作为一维数组
二维数组中的每个元素是一个一维数组
二维数组参数中第一维的参数可以省略
void f(int a[5]) =======>>> void f(int a[]) ======>> void f(int* a);
void f(int a[3][5]) ======>> void f(int a[][5]) =====>> void f(int (*a)[3]);//指向数组的数组指针
数组参数 等效的指针参数
一维数组 char a[30] char*
指针数组 char* a[30] char** a
二维数组 char a[10][30] char (*a)[30]