C/C++指针杂记

1. 指针相关

&a 表示整个数组首地址
&a[0] 表示数组首元素a[0]的首地址
a 表示数组首元素的首地址 同&a[0]

上述值都相等,含义不同

&arr 代表整个数组的首地址;

指向整个数组的指针

arr 代表数组首元素的首地址;

指向单个元素的指针

上述两者的值相等,但数据类型不相同。

1.1 指针数组 & 数组指针

1.1.1 指针数组——int *p1[4]

因为"[]“的优先级比*要高,所以p1先与”[ ]"结合,构成一个数组的定义,数组名为p1,int*则是修饰数组的内容,即数组的每个元素。该数组包含4个指向int类型数据的指针

1.1.2 数组指针——int (*p2)[4]

因为()的优先级比[]的优先级要高,所以*和p2构成一个指针的定义,指针变量名为p2,p2是一个指针,int修饰的是数组的内容,也就是说,p2是一个指针,是指向一个包含4个int类型数据的数组

int arr[5] = {1,2,3,4,5};
int (*p1)[5] = &arr; //&arr 数组的首地址 正确赋值  匹配
int (*p2)[5] = arr; //数组arr首元素的首地址  错误赋值  两边元素类型不匹配

int *p3[5];
p3[0] = arr; //第一个指针指向数组arr
printf("%d",p3[0][2]);//数组arr的第三个元素3

1.2 指向指针的指针——int **p

可以用来定义二维数组

int** p;
p = new int* [3]; //申请了3行 列数未知
for (int i = 0; i < 3; i++)
{
	p[i] = new int[5];//每行5列
}
int x = 0;
for (int i = 0; i < 3; i++) {
	for (int j = 0; j < 5; j++) {
		p[i][j] = x++;
	}
}
delete(p);//释放内存

1.3 指针做函数参数

为了节省空间和实践,提高程序的运行效率,当一维数组作为函数参数的时候,编译器总是将它解析成一个指向其首元素首地址的指针,因此可以将一位数组的函数参数写成指针形式。

//一位数组的情况
void f(int *p){}
void f(int a[]){//里面的元素个数可有可无,推荐不写}
//二维数组的情况 a[3][5]
//可以将其理解成一个一位数组a[3],其中该一维数组中的每个元素是含有5个int类型数据的数组。
void f(int (*p)[5]){//p指向含有5个int类型数据的数组,行数不确定,通过指针的++/--来确定第几行}
void f(int a[][5]){//行数不确定,可写可不写,推荐不写,自定义行数}
//大于二维的情况
void f(int (*p)[5][5]){}
void f(int [][5][5]){}
//......

2. 内存管理

声明变量就是告诉编译器存在这么一个变量,但是编译器不为他分配任何内存空间;
定义变量则是实现这个变量,真正在内存中(堆或栈)为此变量分配空间。定义只能出现一次,声明可以出现多次。

变量定义在一个文件中,在另外的文件中进行声明使用(extern),定义和声明的类型必须一致。

:就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。

:通过new和malloc分配,由delete或free手动释放或者程序结束自动释放。动态内存的生存期人为决定,使用灵活。缺点是容易分配/释放不当容易造成内存泄漏,频繁分配/释放会产生大量内存碎片。 若程序员不释放,程序结束时可能由OS(操作系统)回收。注意它与数据结构中的堆是两回事,分配方式类似于链表。

常量区: 存放局部变量或者全局变量的值;对于全局常量,编译器一般不分配存储空间,而是放在符号表中以提高访问效率。

静态存储区:存放全局变量和静态变量(全局和局部)

代码区:函数二进制代码

C/C++不提供垃圾回收机制,因此需要对堆中的数据进行及时销毁,防止内存泄漏,使用free和delete销毁new和malloc申请的堆内存,而栈内存是动态释放
内存中的栈区主要用于分配局部变量空间,处于相对较高的地址,其栈地址是向下增长的(由高到低);而堆区则主要用于分配程序员申请的内存空间,堆地址是向上增长的(由低到高)。

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
int main() {

	/*在栈上分配*/
	int a1 = 0;
	int a2 = 0;
	int a3 = 0;
	int a4 = 0;

	printf("栈-向下生长(由高到低):\n");
	printf("a1 = 0x%08X\n", &a1);
	printf("a2 = 0x%08X\n", &a2);
	printf("a3 = 0x%08X\n", &a3);
	printf("a4 = 0x%08X\n", &a4);
	
	/*在堆上分配*/
	char* p1 = (char*)malloc(4);//申请4字节内存
	char* p2 = (char*)malloc(4);
	char* p3 = (char*)malloc(4);
	char* p4 = (char*)malloc(4);

    printf("堆-向上生长(由低到高):\n");
	printf("p1 = 0x%08X \n", p1);
	printf("p2 = 0x%08X \n", p2);
	printf("p3 = 0x%08X \n", p3);
	printf("p4 = 0x%08X \n", p4);
	
	free(p1);
	p1 = NULL;
	free(p2);
	p2 = NULL;
	free(p3);
	p3 = NULL;
	free(p4);
	p4 = NULL;

	system("pause");
	return 0;
}

运行结果

栈-向下生长(由高到低):
a1 = 0x0062FDFC
a2 = 0x0062FDF8
a3 = 0x0062FDF4
a4 = 0x0062FDF0
堆-向上生长(由低到高):
p1 = 0x009B6D70
p2 = 0x009B6D90
p3 = 0x009B6DB0
p4 = 0x009B6DD0

上述运行结果是在64位操作系统得到的,int型变量的长度为4字节,指针型变量的长度为8字节。(但是为什么4个指针变量之间增长间隔不是8呢?)

指针的释放

free(p)之后,p还要赋值为NULL,否则虽然释放了指针变量p,但此时的指针变量p本身并没有被删除,其保存的地址没有改变。但是,此时p虽然不是NULL指针,但它却指向了不合法的内存,称为“野指针”或者称为“悬垂指针”。

free(p)释放的是指针变量所指向的内存,而不是指针变量p本身。指针变量p并没有被释放,仍然指向原来的存储空间。其实,指针只是一个变量,只有程序结束时才会被销毁。释放内存空间后,原先指向这块空间的指针还存在,只不过现在这个指针指向的内存是不合法的。因此在释放指针之后一定要将该指针指向NULL,防止在后面指针又被解引用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值