【指针大放送】初篇

上期我们讲到预备篇,今天给大家带来指针初篇的内容。

初篇

  • 1.什么是指针
  • 2. 指针和指针类型
    • 2.1指针的解引用
    • 2.2指针+-整数
  • 3.野指针
    • 3.1野指针成因
    • 3.2如何规避野指针
  • 4.指针运算
    • 4.1 指针+-整数
    • 4.2 指针-指针
    • 4.3指针的关系运算
  • 5.指针与数组
  • 6.指针数组

1.什么是指针

预备篇已经很清楚地讲解了指针的前生今世
现在来回顾一下

1.内存被分为一个个内存单元,每个单元大小是一字节
2.每个内存单元都有自己的编号,也就是地址,指针。
3.地址存储的话,放在指针变量中
4.每个内存单元都有自己的地址来唯一标识
5.在32位机器上,地址的大小是4字节大小,指针变量也就是4字节
同理,64位机器上,地址的大小是8字节大小,指针变量也就是8字节

2. 指针和指针类型

我们已经了解指针类型有
char* short* int * float * 等等
而且这些指针变量的大小都为4字节(32位机器下)
那么大小既然都一样,
又为什么创建这么多类型,
而不创建一个统一的类型呢?
指针类型的意义是什么呢?

2.1指针的解引用

这里我们看这样一份代码

#include 
int main()
{
    int a=0x11223344;
    int* pa=&a;//指针为int*类型
    *pa=0;
    return 0;
}
#include 
int main()
{
    int a=0x11223344;
    char* pa=&a;//指针为char*类型
    *pa=0;
    return 0;
}

这两份代码唯一的不同是用来存放指针的类型不一样
那么类型不一样会不会影响存放呢,答案是显然的,
地址是4字节大小,指针变量也是4字节,故没有影响
那我们在VS内存中来看一下代码运行结果
【指针大放送】初篇_第1张图片
可以看到类型为int*时,一次将4个字节都赋值为0
分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分
【指针大放送】初篇_第2张图片
神奇的出现了,类型为char*时,赋值却只赋值了一字节为0
总结

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

2.2指针±整数

来看这样一份代码

#include 
//演示实例
int main()
{
int n = 10;
char *pc = &n;
int *pi = &n;
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}

来看运行结果

【指针大放送】初篇_第3张图片
可以看到,
pc与pc+1相差1字节,pi与pi+1相差4字节
总结

指针的类型决定了指针向前或者向后走一步有多大(距离)。

3.野指针

概念

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

3.1野指针成因

1.指针未初始化

#include 
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}

在这里插入图片描述

2.指针越界访问

#include 
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}

【指针大放送】初篇_第4张图片

3.指针指向的空间释放

int* test()
{
	int a = 0;
	return &a;
}
int main()
{
	int* p = test();
	printf("%p", p);
	return 0;
}

此时编译器虽然编译通过,但是此时指针指向的空间已被销毁,成为野指针

3.2如何规避野指针

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性、

在第一条中
1.初始化知道明确的值时,应明确初始化

int a=0;
int *p=&a;

2.不知道初始化什么值时

int *p=NULL;

4.指针运算

指针± 整数
指针-指针
指针的关系运算

4.1 指针±整数

前边已经介绍过,直接实战

#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}

【指针大放送】初篇_第5张图片
到这可能会有同学奇怪,&values[5]不是野指针吗,怎么可以用来比较,不要急,我们继续看

4.2 指针-指针

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

观察上式,得到结果为9,则可以得到结论

指针-指针为元素个数

【指针大放送】初篇_第6张图片

4.3指针的关系运算

观察如下代码

for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}

【指针大放送】初篇_第7张图片

简化后

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}

【指针大放送】初篇_第8张图片

注意

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证
它可行。

标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较。

这也就解释了虽然4.1中&values[5]时虽然为野指针,但不妨碍比较

5.指针与数组

【指针大放送】初篇_第9张图片

这张图可以很好的解释两者之间的关系

6.指针数组

我们知道,数组是存放一类元素的集合
例如

int arr1[10];
char arr2[10];
......

那么指针数组应该就是存放指针变量的数组

int* arr3[10];//指针数组的格式
char* arr3[10];

那么指针数组可以用来干什么呢,目前这里我们可以用来模拟二维数组

int main()
{
	//使用指针数组,模拟一个二维数组
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };

	//指针数组
	int* arr[] = { arr1, arr2, arr3 };

	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

【指针大放送】初篇_第10张图片
欢迎小伙伴们的留言噢

你可能感兴趣的:(c语言,开发语言,算法,c++)