C语言指针(三)

动态内存分配

传统内存分配

传统数组的缺点:
  1. 数组长度必须事先指定,且只能是长整数,不能是变量
    例子:
    int a[5]; //OK
    int len = 5; int a[len]; // error
  2. 传统形式定义的数组,该数组的内存程序员无法手动释放, 在一个函数运行期间系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会释放
  3. 数组的长度一旦定义,其长度就不能更改,或数组的长度不能在函数运行的过程中动态的扩充或缩小
  4. A函数定义的数组,在A函数运行期间可以被其它函数使用,但A函数运行完毕之后,A函数中的数组将无法再被其它函数使用。传统方式定义的数组不能跨函数使用

动态内存分配举例——动态数组的构造

malloc的介绍

malloc 是 memory(内存)allocate(分配)的缩写

int main(void)
{
	int i = 5; // 分配了4个字节  静态分配  3行
	int *p = (int *)malloc(4); // 4行
	/*
		1. 要使用malloc函数,必须添加malloc.h这个头文件
		2. malloc函数只有一个形参,并且形参是整型
		3. 4表示请求系统为本程序分配4个字节的空间
		4. malloc函数只能返回第一个字节的地址
		5. 4行分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
		6. p本身所占的内存是静态分配的,p所指向的内存是动态分配的
	*/
	*p = 5; // *p代表的就是一个int变量,
		//只不过*p这个整型变量的内存分配方式和3行的i变量的分配方式不同
	free(p); // free(p)表示把p所指向的内存给释放掉  p本身的内存是静态的,
	//不能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放
}
# include <stdio.h>
# include <malloc.h>

void f(int *);
 
int main(void)
{
	int *p = (int *)malloc(sizeof(int)); // sizeof(int) 返回值是int所占的字节数
	*p = 10;
	
	printf("%d\n", *p);	// 10
	f(p);
	printf("%d\n", *p);	// 200
	
	return 0; 
} 

void f(int * q)
{
//	 *p = 200; //error,这是因为在函数f内部不能操作主函数中的变量p 
//	 q = 200; //error,这是因为变量q为指针变量,定义的时候系统会为它分配一块内存,
					//q的值就是这个内存的指针,所有必能再修改了 
//	 **q = 200; //error,因为 *q 为 *p 的一份拷贝或者是一个副本,所有 *q 代表的是,
			//动态内存的4个字节的值,所有 *q就是一个常量 ** 常量是一个错误的语法 
	*q = 200; // OK,因为 *q 和 *p 是一样的。 
	// free(q); // 把q所指向的内存释放掉 ,本语句必须注释掉,
				//否则会导致13行代码出错。 
}

在这里插入图片描述

动态内存分配举例——动态一维数组的构造

定义一个动态的数组,数组的大小由用户输入,元素由用户输入

# include <stdio.h>
# include <malloc.h>

int main(void)
{
	int a[5]; // 如果int占4个字节的话,则本数组空包含有20个字节,每个字节被当做了一个int变量来使用 
	int len;
	int * pArr;
	int i;
	
	// 动态的构造一维数组 
	printf("请输入你要存放的元素个数:");
	scanf("%d", &len);
	pArr = (int *)malloc(sizeof(int)*len); 
	// 类似于 int pArr[len]; 动态的构造一维数组,数组的名字为pArr,因为它存放了数组的第一个元素
	// 本行动态的构造了一个一维数组,该数组的长度为len,该数组的数组名为pArr,该数组的每个元素是int类型
	// int * 表示 把返回的第一个字节的地址 当做了 int类型地址,所有元素的类型就为int类型 
	
	// 对一维数组进行操作   如:对动态一维数组进行赋值 
	for (i=0; i<len; i++)
		scanf("%d", &pArr[i]);	
	
	// 对一维数组进行输出 
	printf("一维数组的内容是:\n");
	for (i=0; i<len; i++)
		printf("%d\n", pArr[i]);
		
	// free(pArr); // 释放掉动态分配的数组 
	printf("%#X\n", pArr); // 输出动态一维数组的首地址,你会发现每次运行此值都不一样 
	
	return 0;
} 

在这里插入图片描述

动态扩充或缩小动态内存的大小
使用realloc函数,realloc(动态数组名,内存大小),假设pArr原来的大小为100字节

  1. 扩充 realloc(pArr, 150) ,保留原来100个字节,往他后面继续添加50个字节
  2. 缩小realloc(pArr, 50) ,把数组后面50个字节去掉

动态内存和静态内存的比较

  1. 静态内存是由系统自动分配,由系统自动释放
  2. 静态内存是在栈分配的
  3. 动态内存是由程序员手动分配,手动释放
  4. 动态内存是在堆分配的

多级指针

在这里插入图片描述
解释 int、int *、int **、 int ***;int 表示整型,int * 表示,他是一个指针变量,存放int类型的地址;int ** 表示,它是一个指针变量,存放 int * 类型的地址;int *** 表示,他是一个指针变量,用来存放 int ** 类型的地址。
所以 int * p; int ** q = &p;

修改g函数 p 变量的值

# include <stdio.h>

void f(int **);
void g(void);

int main(void)
{
	
	g();
	return 0;
}

void f(int ** q) // 修改g函数 p 变量的值
{
	**q = 20;
	// *q 就是 p
}
void g(void)
{
	int i = 10;
	int *p = &i;
	
	printf("%d\n", *p); //10
	
	f(&p); // p是int *类型 , &p 是 int ** 类型
	
	printf("%d\n", *p); // 20 
}

跨函数使用内存

静态变量不能跨函数使用

# include <stdio.h>

void f(int **);

int main(void)
{
	int *p;
	f(&p);
	printf("%d\n", *p);
	
	return 0;	
} 

void f(int ** q)
{
	int i = 5;
	// *q等价于p q和**q都不等价于p
	// *q = i; // error 因为 *q = i; 等价于 p = i;这样写错误
	*q = &i; // OK , p = &i 
}

要想修改p的值,就必须把p的地址传到函数f里,所有f函数的形参就必须为int ** 类型,则*q = p,所有 *q = &i,推出p = &i,所以 *p = i;这句话看上去是正确的,但是呢,当执行完f函数,函数就会释放掉里面的变量空间,所有此时p指向的变量i就不存在指向了,因为程序运行并不会报错,还会输出5,这让用户很容易参数一种假象, *p = 5 是正确的这种错误想法。
注意:静态变量不能跨函数使用

动态内存可以跨函数使用

# include <stdio.h>
# include <malloc.h>

void f(int **);

int main(void)
{
	int *p;
	f(&p);
	printf("%d\n", *p);
	
	return 0;
} 

void f(int ** q)
{
	// *q <==> p
	*q = (int *)malloc(sizeof(int)); //动态分配了一块内存,大小为sizeof(int) ==> 4字节或8字节,该空间能存放int型的变量 
	// 把该动态内存的首字节地址存放在 *q中,也就是 主函数的 p中。 
	** q = 20;
}

在主函数中调用函数f,会申请一块动态内存供p使用,要手动释放

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值