零基础玩转C语言系列第七章——初识指针

目录

一、什么是pointer

1.指针理解的两个要点:

2.指针变量

二、指针类型

三、野指针

1.野指针成因

2.如何避免野指针

四、指针运算

1.指针+-整数

2.指针-指针

3.指针的关系运算

五、二级指针

六、指针数组


【前言】

指针可以说是C/C++中最难学透的部分了,没有之一!为了更好地阐述指针的一些重要内容,笔者也是把录播反复看了好几遍,才把指针初阶整理出来,所以,下面的都是重点哦,还来康康吧。

  

一、什么是pointer

1.指针理解的两个要点:

  1. 指针是内存中的一个最小内存单元的编号,也就是地址(指针就是地址)
  2. 平时我们口语说的指针,通常指的是指针变量是用来存放内存地址的变量。  

【注意】:内存单元 == 编号 == 地址 == 指针

再强调一遍,指针就是地址,通常口语中所说的指针大多指的是指针变量。 

在内存中:

内存被划分成一个个小的内存单元,一个基本的内存单元的大小是一个字节。

2.指针变量

我们可以通过&变量,取出变量的内存起始地址,把地址存放到一个变量当中,这个变量就是指针变量。 

#include<stdio.h>

int main()
{
	int a = 10;//定义整型变量a,会在内存中开辟一块空间

	int* p = &a;//*告诉我们p是一个指针变量,int表示p指向的对象的数据类型是int

	return 0;

	return 0;
}

指针变量,用来存放地址的变量(注意:存放在指针变量中的值都被当作了地址来处理

那这里的问题是:

  1. 一个小的内存单元到底是多大?-->答案是一个字节
  2. 如何编址呢?

这里就主要探讨如何编制:

对于32位机器来说,假设有32根地址线,每根地址线在寻址的时候会产生高电平和低电平,也就是电信号1/0,那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010

...      ...      ...      ...


01111111 11111111 11111111 11111111
10000000 00000000 00000000 00000000
10000000 00000000 00000000 00000001

...      ...      ...      ...

11111111 11111111 11111111 11111101
11111111 11111111 11111111 11111110
11111111 11111111 11111111 11111111

这里就会有2^32个地址编号,因为一个内存编号管理一个字节大小的空间注意这里不要将指针大小和指针管理一块空间的大小混淆!指针大小在32位平台是4个字节,在64位平台是8个字节,而指针也就是一块内存单元编号,可以管理一个字节的空间)所以32位机器可以管理2^32个字节的内存空间,换算成GB也就是:2^32Byte == 2^32 / 1024KB == 2^32 / 1024 / 1024MB == 2^32 / 1024 / 1024 / 1024GB == 4GB,同样的方法算出,对于64位机器,可以管理8GB大小的内存空间。

【总结】

  1. 指针是用来存放地址的,指针是唯一表示一块内存空间的;
  2. 指针的大小在32位平台是4个字节,在64位平台是8个字节。 

二、指针类型

问题引入:指针类型都是4/8个字节,那还分不同指针类型干什么?

回答这个问题,那这里就要好好说道说道指针类型的意义了(注意哦,非常重要!!!)

意义一:

指针类型决定了指针在解引用的时候一次能访问几个字节(也叫指针的权限)。 

int* —— 4个字节

char* —— 1个字节

double* —— 8个字节

 【注意】:需要注意的是,不要和指针类型的大小混淆咯!

意义二:

指针类型决定了指针向前或向后走一步的步长(+-整数),单位是字节!

三、野指针

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

1.野指针成因

  1. 指针未初始化;
  2. 指针越界访问;
  3. 指针指向的空间已经释放(特别易错)。

具体讲解:

<1>指针未初始化

比如:int* p;//局部变量指针未初始化,默认是随机值,这是不可取的。

<2>指针越界访问

//最常见的就是数组越界访问 
#include<stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int i = 0;
	for (i = 0; i < 12; i++)//数组越界
	{
		arr[i] = 1;
	}

	return 0;
}

<3>指针指向的空间已经释放

#include<stdio.h>

int* test()
{
	//局部变量a是具有生命周期的,一旦出了这个范围就会被销毁,内存空间的使用权限被收回了
	int a = 100;
	return &a;//返回a的地址
}

int main()
{
	int* p = test();
	//指针p用来接收a的地址,但是test函数调用完毕后,分配给a的内存空间也就被收回了
	//此时p接受的地址是不正确的,导致p成为了野指针
	printf("%dn", *p);

	return 0;
}

2.如何避免野指针

  1. 指针初始化;
  2. 小心指针越界;
  3. 指针指向的空间释放需要即时置为NULL(也就是说,指针不想用了就将它置为NULL);
  4. 避免返回局部变量的地址(野指针的成因3);
  5. 指针使用之前需要检查其有效性。

具体讲解:

<1>指针初始化

比如:int* p = NULL;//指针的使用需要初始化,如果不知道指针指向谁,可以先初始化成NULL

<2>小心指针越界

主要是在数组中,注意数组下标很容易导致越界访问

<3>指针指向的空间释放需要即时置为NULL(也就是说指针不想用了就将它置为空)

#include<stdio.h>

int main()
{
	int a = 10;
	int* pa = &a;
	//通过指针pa将a的值改为20
	*pa = 20;
	printf("%dn", a);//输出20

	//pa完成它的任务之后,在整个工程中都不想用它了,需要即时将它置为NULL
	pa = NULL;//可能在学校教学中很少这样写,但是在企业级的代码规范中是这样要求的哟
	return 0;
}

<4>避免返回局部变量的地址

#include<stdio.h>

int* test()
{
	//局部变量a是有生命周期的,所以a一旦出了test()函数范围就会被销毁
	//也就是说定义a时分配给a的内存空间的使用权限不再是a的了,已经被系统收回
	int a = 100;
	return &a;//返回a的地址(出test函数之后这块地址不再是a的了)
}

int main()
{
	//原本是定义指针变量p,用来接收a的地址
	//但是a调用test函数结束后,a的地址被系统收回了
	//所以指针p指向的位置是不可知的、不正确的
	//这是最容易出错的典型!一定要小心哦
	int* p = test();
	printf("%dn", *p);
	return 0;
}

<5>指针使用之前要检查其有效性

//还用上面的那道题目,通过指针pa将a的值改为20

#include<stdio.h>

int main()
{
	int a = 10;
	int* pa = &a;//此时pa指向的是a的地址,对pa进行解引用操作等同于对a进行操作

	//需要通过对指针pa进行解引用操作,将a的值更改为20
	//但是在使用pa之前,最好要判断其有效性
	//可能你会觉得下面这个步骤有点画蛇添足了,但是请相信笔者,这种习惯在上千行的程序中会很受用
	//更不要说在之后进入企业中面对上十万行的项目了,所以请铁汁现在就养成良好的编程习惯!
	if (pa != NULL)
	{
		*pa = 20;
	}
	printf("%dn", a);
	return 0;
}

四、指针运算

1.指针+-整数

这里就要重提指针类型的意义了,前面说过指针类型的意义二:指针类型决定了指针向前或向后走一步的步长(+-整数),单位是字节。具体看在内存中的操作:

#include<stdio.h>

int main()
{
    //定义一个整型数组
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	
	
	return 0;
}

#include<stdio.h>

int main()
{
    //定义一个字符数组
	
	char arr[] = {'a','b','c',''};
	
	return 0;
}

 

好,指针+-整数就讲到这里咯,大家下去自己动手看看short型、double型指针+-整数是什么样的效果。

2.指针-指针

注意哦,使用指针-指针运算的时候,前提是两个指针指向的是同一块内存空间,那何为指向同一块内存空间呢?最典型的例子就是数组。

指针-指针其实表示的是两个指针中间的元素个数,注意哦,不是中间有几个字节。

 

指针-指针的实践:自定义函数实现strlen函数的功能

#include<stdio.h>
#include<assert.h>

int my_strlen(char* arr)
{
	assert(arr);
	char* end = arr;
	while (*end != '')
	{
		end++;
	}
	return end - arr;
}

int main()
{
	char arr[] = "abc";
	int len = my_strlen(arr);
	printf("%dn", len);
	return 0;
}

3.指针的关系运算

指针的关系运算,也就是指针比较大小,指针怎么比较大小呢,前面我们说过,指针就是地址,所以指针比较大小比较的是地址的大小,也就是说比较的是地址的高低。

这个很简单,在这里就不说了。

五、二级指针

指针变量也是变量,是变量就需要在内存中开辟空间,故而也就会有地址,所以指针变量的地址存放到二级指针中。 

 

六、指针数组

指针数组是数组,是用来存放指针变量的数组! 说简单点,它和整型数组、字符数组等别无二致,只不过数组类型不同而已。 

#include<stdio.h>

int main()
{
	int a = 0;
	int b = 0;
	int c = 0;

	int* pa = &a;
	int* pb = &b;
	int* pc = &c;
    
    //定义一个指针数组
	int* arr[] = { pa,pb,pc };

	return 0;
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>