二维指针动态分配------C指针学习补充

目录

·指针

·定义指针

·二级指针

·内存的动态分配

(一) malloc函数开辟动态存储区

(二) calloc函数开辟动态存储区

(三) realloc函数重新分配动态存储区

(四) free函数释放动态存储区

二级指针动态申请

地址不连续性

全部一次性分配 地址连续


·指针

如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。编译系统根据程序中定义的变量类型,分配一定长度的空间。内存区的每一个字节有一个编号,这就是“地址”。

由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元,将地址形象化地称为“指针”

C语言中的地址包括位置信息(内存编号,或称纯地址)和它所指向的数据的类型信息,或者说它是“带类型的地址”。

存储单元的地址和存储单元的内容是两个不同的概念。

在程序中一般是通过变量名来引用变量的值。

直接按变量名进行的访问,称为“直接访问”方式。还可以采用另一种称为

间接访问”的方式,即将变量的地址存放在另一变量(指针变量)中,

然后通过该指针变量来找到对应变量的地址,从而访问变量。

变量名

地址

内容

i

2000

1

2003

j

2004

2

2007

k

2008

3

2011

·定义指针

#include <stdio.h>
int main()
{	int a=100,b=10;
	//定义整型变量a,b,并初始化
	int *pointer_1,*pointer_2;
	//定义指向整型数据的指针变量pointer_1, pointer_2
	pointer_1=&a;	//把变量a的地址赋给指针变量pointer_1
	pointer_2=&b;	//把变量b的地址赋给指针变量pointer_2 
	printf("a=%d,b=%d\n",a,b);	//输出变量a和b的值
	printf("*pointer_1=%d,*pointer_2=%d\n",*pointer_1,*pointer_2);
	//输出变量a和b的值
	return 0;
}

pointer_1

a

&a

100

*pointer_1

pointer_2

b

&b

10

*pointer_2

左端的int是在定义指针变量时必须指定的“基类型”。指针变量的基类型用来指定此指针变量可以指向的变量的类型。

前面介绍过基本的数据类型(int,charfloat),既然有这些类型的变量,就可以有指向这些类型变量的指针,因此,指针变量是基本数据类型派生出来的类型,它不能离开基本类型而独立存在。

在定义指针变量时要注意:

(1) 指针变量前面的“*”表示该变量为指针型变量。指针变量名则不包含“*”。

(2) 在定义指针变量时必须指定基类型。一个变量的指针的含义包括两个方面,一是以存储单元编号表示的纯地址(如编号为2000的字节),一是它指向的存储单元的数据类型(如int,char,float等)

(3) 如何表示指针类型。指向整型数据的指针类型表示为“int *,读作“指向int的指针”或简称“int指针”

(4) 指针变量中只能存放地址(指针),不要将一个整数赋给一个指针变量。

·二级指针

指向指针数据的指针变量,简称为指向指针的指针

#include <stdio.h>
int main()
{	char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer design"};
	char **p;
	int i;
	for(i=0;i<5;i++)
	{	p=name+i;
		printf("%s\n",*p);
	}
	return 0;
}

name数组

字符串

name

name[0]

Follow me

p

name[1]

BASIC

name[2]

Great Wall

name[3]

FORTRAN

name[4]

Computer design

name是一个指针数组,它的每一个元素是一个指针型的变量,其值为地址。name既然是一个数组,它的每一元素都应有相应的地址。数组名name代表该指针数组首元素的地址。name+iname[i]的地址。name+i就是指向指针型数据的指针。还可以设置一个指针变量p,它指向指针数组的元素。p就是指向指针型数据的指针变量。

定义一个指向指针数据的指针变量:

p的前面有两个*号。p指向一个字符指针变量(这个字符指针变量指向一个字符型数据)。如果引用*p,就得到p所指向的字符指针变量的值

p是指向char*型数据的指针变量,即指向指针的指针。在第1次执行for循环体时,赋值语句“p=name+i;”使p指向name数组的0号元素name[0],*p是name[0]的值,即第1个字符串首字符的地址,用printf函数输出第1个字符串(格式符为%s)。执行5次循环体,依次输出5个字符串。

指针数组的元素也可以不指向字符串,而指向整型数据或实型数据等。

·内存的动态分配

全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区是一个称为(stack)的区域。

除此以外,C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时随时开辟,不需要时随时释放。这些数据是临时存放在一个特别的自由存储区,称为(heap)区。

可以根据需要,向系统申请所需大小的空间。由于未在声明部分定义它们为变量或数组,因此不能通过变量名或数组名去引用这些数据,只能通过指针来引用。

(一) malloc函数开辟动态存储区

函数原型为 void * malloc(unsigned int size)

作用是在内存的动态存储区中分配一个长度为size的连续空间。形参size的类型定为无符号整型(不允许为负数)。此函数的值(即“返回值”)是所分配区域的第一个字节的地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的第一个字节。

指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址。如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)

(二) calloc函数开辟动态存储区

函数原型为void * calloc(unsigned n,unsigned size)

作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组

calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。这就是动态数组。函数返回指向所分配域的第一个字节的指针;如果分配不成功,返回NULL

(三) realloc函数重新分配动态存储区

函数原型为void * realloc(void *p,unsigned int size)

如果已经通过malloc函数或calloc函数获得了动态空间,想改变其大小,可以用realloc函数重新分配。

realloc函数将p所指向的动态空间的大小改变为sizep的值不变。如果重分配不成功,返回NULL

(四) free函数释放动态存储区

函数原型为void free(void *p)

作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用callocmalloc函数时得到的函数返回值。

free函数无返回值。

以上4个函数的声明在stdlib.h头文件中,在用到这些函数时应当用“#include <stdlib.h>”指令把stdlib.h头文件包含到程序文件中。

二级指针动态申请


#include <stdio.h>
#include <malloc.h>
#include <assert.h>
//编写程序,输入一个正整数n(1<n<=6),根据n生成一个n*n的方阵,然后将该方阵(行列互换)后输出
int main() {
	printf("请输入一个正整数(1<n<=6):");
	int n,i,j,temp;
	scanf("%d", &n);
	int **a=(int**)malloc(n*sizeof(int*));  //动态申请二级指针并使用 断言判断是否成功申请
	assert(a);
	for (i = 0; i < n; i++) {
		a[i] = (int*)malloc(n * sizeof(int));  //依次为每个一级申请内存,这就是比较麻烦的地方,依次判断是否申请成功
		assert(a[i]);
	}
	//赋值
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			*(a[i] + j) = i * n + j + 1;
			printf("%4d", a[i][j]);
		}
		printf("\n");
	}
	//结束的时候一定要释放内存,并且将指针指向NULL  避免成为一个野指针
	for (i = 0; i < n; i++) {
		free(a[i]); 
		a[i] = NULL;
	}
	a = NULL;
	free(a);  //释放
	return 0;
}

//首先定义了一个二维指针指向NULL这是避免成为一个野指针(未知变已知)

int** p = NULL; 

//为二维指针分配了n个指针的数组空间  (n行)

//二维指针p指向指针数组的首地址 
p = (int**)malloc(n* sizeof(int*))

然后通过for循环为指针数组的每一个指针分配了n个int

也就是一行n个  (n列)

for (int j = 0; j < nWidth; j++) {
    p[j] = (int*)malloc(nHeight * sizeof(int));
    assert(p[j]);
}

打印每一个的地址发现一个问题

for (i = 0; i < n; i++) {
		printf("%p  ", &a[i]);
}

for (i = 0; i < n; i++) {
	for (j = 0; j < n; j++) {
		printf("%p  ", &a[i][j]);
		if (j == n - 1)
			printf("\n");
	}
}


for (i = 0; i < n; i++) {
	printf("%p  ", a[i]);
}

地址不连续性

首先,是对每一行分配,所以,我们可以看到每一行的内存都是连续的,每一个都占据四个字,8个字节(64位操作系统(指针占8字节)) 所以是

+8(间隔8字节)

但是,为每一行分配内存的时候,是随机的对每一行内容进行分配内存,所以内存的位置是不确定的,所以,出现了这种情况

每一行的内存是连续的

但是下一行和上一行的内存不连续

其中每一行因为分配的是int 的元素 (64位操作系统 int占字节)

+4(间隔4字节)

因为执行了3次for循环 分配了三次,每次都是随机分配的,所以导致了行与行之间不连续

那该怎么办?

经过网上搜索找到了第二种方法

全部一次性分配 地址连续

二维数组是特殊的一位数组


#include <stdio.h>
#include <malloc.h>
#include <assert.h>
//编写程序,输入一个正整数n(1<n<=6),根据n生成一个n*n的方阵,然后将该方阵(行列互换)后输出
int main() {
	printf("请输入一个正整数(1<n<=6):");
	int n,i,j,temp;
	scanf("%d", &n);
	int **a=(int**)malloc(n*sizeof(int*));  //动态申请二级指针并使用 断言判断是否成功申请
	assert(a);
	a[0] = (int*)malloc(n * n * sizeof(int));
    assert(a[0]);
	for (int i = 1; i < n; i++) {
		a[i] = a[i - 1] + 3;
	}
	
	//赋值
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			*(a[i] + j) = i * n + j + 1;
			printf("%4d", a[i][j]);
		}
		printf("\n");
	}
	//结束的时候一定要释放内存,并且将指针指向NULL  避免成为一个野指针
	free(a[0]);
	a[0] = NULL;
	a = NULL;
	free(a);
	return 0;
}

第二种分配方法:

首先,同样是为a分配内存,现在a地址连续

但是,在第二句中,我们需要注意,是直接在 a[0] 出分配了所有需要的内存,所以,这个时候就全部分配完了,而且由于是一次性分配内存,故内存的地址肯定是连续的,运行结果也证明了这一点

今日总结到这里了 夜深了

长路漫漫终有时

道阻且长 行则将至!

p是指向

cha

*型数据的指针

变量,即指向指针的指

针。在第1次执行for循环体时,赋值语句“p=name+i;”使p指向name数组的0号元素name0],*pname0]的值,即第1个字符串首字符的地址,用printf函数输出第1个字符串(格式符为%s)。执行5次循环体,依次输出5个字符串。

指针数组的元素也可以不指向字符串,而指向整型数据或实型数据等。

  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值