指针进阶
内存四区
在系统为程序开辟内存时,将内存区域划分为4块,分别为:
(1)栈区
(2)堆区
(3)全局静态常量区(全局区)
(4)代码区
栈区:存放函数的形参、局部变量等。由编译器自动分配和释放,当函数执行完毕时自动释放。
堆区:用于动态内存的申请与释放,一般由程序员手动分配和释放,若程序员不释放,则程序结束时由操作系统回收。
全局静态常量区(全局区):用于动态内存的申请与释放,一般由程序员手动分配和释放,若程序员不释放,则程序结束时由操作系统回收。
代码区:存放可执行的代码,一般为CPU 执行的机器指令。(由编译器翻译后的指令放在代码区)
结构体指针变量的使用
结构体指针变量:是一个保存结构体变量地址的指针变量,我们对这个指针变量进行解引用,就可访问到这个结构体变量的数据以及更改数据。
结构体指针变量的定义格式:指针指向的类型 * 指针名 = NULL;
结构体指针
通过结构体指针引用结构体变量及其结构体成员:
结构体指针引用符:‘->’
结构体指针引用成员的基本格式:结构体指针名->结构体成员名
结构体指针数组
- 方式一:通过for循环输出成员的名字
- 方式二:通过p = p->next,来获取成员名字并输出
动态内存分配
什么是动态内存分配:
指用户可以根据自己的需要,向系统申请所需大小的内存空间;由于没有声明部分来定义它们是为变量的地址还是为数组的地址,所以只能通过指针来引用它们。
要使用动态内存开辟,需要包含stdlib.h头文件
怎样建立内存的动态分配:
①malloc函数:malloc(int size);
用于分配一个大小为size的内存区域。
例如:char *p=(char *)malloc(100)
②calloc函数:calloc(unsigned n,int size);
用于分配n个大小为size的连续内存区域,可以为一维数组开辟动态内存空间。
例如:int *p=(int *)calloc(10,sizeof(int));
③realloc函数:realloc(void * p,unsigned int size);
用于改变已经通过malloc函数或calloc函数开辟的内存空间的大小。
例如:realloc(p,sizeof(int)*6);
④free函数:void free(void * p);
用于释放指针变量所指向的动态内存空间。
例如:free( p );
注意:动态内存需要手动申请,在对堆区申请,申请完后需要自己释放。
- 例1:malloc(申请的内存大小) ;单位:字节 ;返回的申请的内存的首地址,默认返回void*类型
- malloc不会初始化内存数据
- 例2:calloc(多少个元素, 每个元素大小),会初始化内存
- 例3:realloc(首地址, 修改之后的大小),
扩容
扩容方法:
1、先申请更大的内存空间,拿一个临时指针变量进行管理把原来的数据拷贝到新的大内存中来
2、释放p所指向的旧的内存
3、free(内存的首地址)
4、p指向新的内存
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int *p = (int*)malloc(25 * sizeof(int));
//需要对内存做初始化 memset:逐字节初始化 需要引入string.h
//memset(从哪个地方开始复制,值,复制多少字节)
memset(p, 0, 25 * sizeof(int));
for (int j = 0; j < 25; j++)
{
p[j] = j + 1;
}
//1、先申请更大的内存空间,拿一个临时指针变量进行管理
int *temp = (int*)malloc(50 * sizeof(int));
//2、把原来的数据拷贝到新的大内存中来
for (int j = 0; j < 25; j++)
{
temp[j] = p[j];
}
//3、释放p所指向的旧的内存
free(p); //不会把p指针变量释放
//4、p指向新的内存
p = temp;
for (int j = 25; j < 50; j++)
{
p[j] = j + 1;
}
for (int j = 0; j < 50; j++)
{
if (j % 5 == 0 && j != 0)
{
printf("\n");
}
printf("%d\t", p[j]);
}
printf("\n");
return 0;
}
多重指针
1、什么是多重指针
指针变量也是有其对应地址的,那么既然有地址,就可以用另一个指针变量指向它的地址,也就是指向指针的指针,简称多重指针。
2、多重指针的定义
定义双重指针(二级指针)基本格式:数据类型 **指针变量名;
定义三重指针(三级指针)基本格式:数据类型 ***指针变量名;
依次类推:四级指针、五级指针……
3、多重指针的使用
如有以下定义:
int a = 10,*p,**q,***r; //定义整型变量a、指针p、双重指针q、三重指针r
则可以有以下赋值语句:
p=&a; //使一级指针p指向变量a的地址
q=&p; //使双重指针q指向一级指针p的地址
r=&q; //使三重指针r指向双重指针q的地址
*p=20; //使用一级指针p给变量a赋值
**q=30; //使用二级指针q给变量a赋值
***r=40; //使用三级指针r给变量a赋值
双重指针作为函数参数
一般来说函数的形参无法改变实参,除非形参是指针类型的。那么如果实参是一个指针,想要在一个函数中改变一个指针的指向应该怎么做?
例如:若定义了以下函数fun,如果p2是该函数的形参,要求通过p2把动态分配存储单元的地址通过函数参数传回主调函数,则形参p2应当怎样正确定义。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//申请动态内存
void fun(int**p2) //二级指针变量可以接收一级指针变量的地址
{
//p2=&buff buff相当于*p2
int *p = (int*)malloc(10 * sizeof(int));
(*p2) = p;
}
int main()
{
int *buff = NULL; //buff指向申请的动态内存
fun(&buff);
return 0;
}
双重指针可用于在函数中改变一级指针的指向。