C语言学习笔记(7)指针

指针

  • 指针是什么
  • 指针和指针类型
    • 指针类型的意义
    • 指针的解应用
  • 野指针
    • 野指针成因
    • 如何避免野指针
  • 指针运算
  • 指针和数组
  • 二级指针

指针是什么

在计算机科学中,指针( Pointer )是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。

指针是个变量,存放内存单元的地址(编号)。

#include 
int main()
{
	int a = 10;//在内存中开辟 一块空间
	int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
				//将a的地址存放在p变量中,p就是一一 个之指针变量。
	return 0;
}

总结:指针就是变量,用来存放地址的变量。( 存放在指针中的值都被当成地址处理)。

  1. 一个小的单元到底是多大? ( 1个字节)
  2. 如何编址?

经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。
对于32位的机器,假设有32根地址线,那么假设每根地址线是产生一个电信号正电/负电 ( 1或者0 )
那么32根地址线产生的地址就会是:
C语言学习笔记(7)指针_第1张图片
这里就有2的32次方个地址。

每个地址标识一个字节,那我们就可以给 (232Byte ==232/1024KB==232 /1024/1024MB==232/1024/1024/1024GB == 4GB) 4G的空闲进行编址。

这里我们就明白:

  1. 在32位的机器 上, 地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
  2. 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

总结:

  • 指针是用来存放地址的,地址是唯一 标示一块地址空间的。
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节。

指针和指针类型

指针类型的意义

  1. 指针类型决定了:指针解引用的权限有多大
  2. 指针类型决定了,指针走一步,能走多远(步长)
    C语言学习笔记(7)指针_第2张图片

指针的解应用

指针类型决定了指针进行解引用操作时,能访问的空间大小

int* p:p可访问4个字节
char
p:p可访问1个字节
float
p:p可访问4个字节
double
p:*p可访问8个字节

参考如下代码

int main()
{
	int arr[10] = { 0 };
	int *p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	return 0;
}

内存情况
C语言学习笔记(7)指针_第3张图片
把int类型指针改为char型指针时

int main()
{
	int arr[10] = { 0 };
	char* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	return 0;
}

内存情况
C语言学习笔记(7)指针_第4张图片

野指针

概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

野指针成因

1.指针未初始化

int main()
{
	int *p;
	*p = 20;
	return 0;
}

这里的p就是一个野指针

p是一个局部的指针变量,局部变量不初始化的话,默认是随机值。*p = 20非法访问内存了。

2.指针越界访问

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 11; i++)
	{
		*p = i;
		p++;
	}
	return 0;
}

当指针指向的范围超出数组arr的范围时,p就是野指针

3.指针指向的空间释放

int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int*p = test();
	*p = 20;
	return 0;
}

test是创建的临时函数,使用过后就被销毁了,这时指针访问的就是一个已被销毁的变量,此时的指针就变为了野指针

如何避免野指针

1.指针初始化

  • 当前不知道p应该初始化为什么地址的时候,直接初始化为NULL
int main()
{
	int *p = NULL;
	return 0;
}
  • 知道初始化的值时,将指针初始化为已知地址
int main()
{
	int a = 10;
	int* ptr = &a;
	return 0;
}

2.小心指针越界
3.指针指向空间释放即使置NULL
4.指针使用之前检查有效性

指针运算

1.指针 + - 整数

指针前后移动

C语言学习笔记(7)指针_第5张图片

2.指针 - 指针

指针间的距离

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", &arr[9] - &arr[0]);
	return 0;
}

在这里插入图片描述

指针和指针相减是有前提的: 两个指针指向同一块空间

需避免如下代码

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	char c[5];
	printf("%d\n", &arr[9] - &c[0]);
	return 0;
}

练习:用指针模拟实现strlen函数

int my_strlen(char* str)
{
    char* start = str;
	while (*str != '\0')
	{
		str++;
	}
	return str - start;
}
int main()
{
	int n = my_strlen("abc");
	printf("%d\n", n);
	return 0;
}

指针和数组

数组名表示的是数组首元素地址

看下面代码

int main()
{
	int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("&arr[%d]=%p<==>p+%d=%p\n", i, &arr[i], i, p + i);
	}
	return 0;
}

C语言学习笔记(7)指针_第6张图片
所以p+i其实计算的是数组arr下标为i的地址。

小知识

arr[i]不但可以写为p+i还可以写为i[arr],p[i],i[p]

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d[arr]=%d ", i, i[arr]);
		printf("p[%d]=%d ", i, p[i]);
		printf("%d[p]=%d ", i, i[p]);
		printf("\n");
	}
	return 0;
}

C语言学习笔记(7)指针_第7张图片

二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?这就引出了二级指针。
C语言学习笔记(7)指针_第8张图片
对于二级指针的运算

  • *ppa通过对ppa中的地址进行解引用,这样找到的是pa,*ppa其实访问的就是pa。
	int b = 20;
	*ppa = &b;//等价于pa = &b;
  • **ppa先通过*ppa找到pa,然后对pa进行解引用操作: *pa ,那找到的是a。
	**ppa = 30;
	//等价于*pa = 30;
	//等价于a = 30;

你可能感兴趣的:(C语言,c语言,学习,开发语言)