资讯专栏INFORMATION COLUMN

【C语言】指针篇——你必须要掌握的指针基础知识

番茄西红柿 / 3058人阅读

摘要:一指针概述指针是个变量存放内存单元的地址编号。即存放指针变量的地址的指针二级指针指向的空间的值是一个一级指针。总结指针是语言非常重要的一部分内容繁多不易懂本文仅介绍了一些基本知识后续还会深入了解指针。


前言

指针是C语言的重点和难点,熟练的掌握指针能够更好的理解计算的存储方式,同时简化代码,增强程序的效率。

一、指针概述

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

指针的创建

在定义指针变量的时候,在变量前面加上’ * ',代表这个变量是一个指针,再往前面加上一个类型名,就代表指针的类型,称为XX指针。

指针的初始化:

  • 使用&(取地址操作符)可以获得变量的地址,将其赋值给已经定义好的指针变量,需要它们的类型相同,类型不同的时候可以使用强制转换。
  • 如果暂时不知道需要存放什么地址的时候,可以先让它指向NULL((void*)0),NULL本质就是0,0这个地址是不允许存储的。
#include int main(){	int a = 10;//在内存中开辟一块空间	int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。				//将a的地址存放在p变量中,p就是一个指针变量。	return 0;}

指针的大小

指针可以是不同的类型,char*、int*、float*、double*、long*等我们熟知的类型,那指针在内存中占多大的空间呢?

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电/负电(1或者0)
那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000
……
111111111 111111111 111111111 111111111
共有232个地址,每一个地址能指向一个内存单元(一字节),那么有232个内存单元。

我们知道一个内存单元的大小为一字节,一根地址线是32位01组成的,那么要存放指针(地址),也就需要32个bit,也即4字节。所以在32位平台下不论是什么类型的指针都是4个字节。

同理,在64位平台下有64根地址线,每个地址是64位,所以需要8个字节。

如何使用指针

使用’ * '操作符对指针进行解引用,可以获取到指针指向空间的值
我们来运行下方代码。

#include int main(){	int n = 0x11223344;	char* pc = (char*)&n;//将n的地址(指针)强制转换成char* 类型	int* pi = &n;	*pc = 0;//改变它指向空间的值	*pi = 0;	return 0;}

我们开始调试代码,查看n的内存,最左边是地址,右边是以16进制显示内存里存放的数据,而n也是以16进制存进去的,两个16进制代表一个字节,如下图:

当执行完语句 *pc=0;

因为pc指向的是char* 类型,只能访问一个字节,所以只能修改一个字节。
再执行*pi = 0;

此时n的内存数据全部变成了0,说明int*类型的指针可以访问4个字节。

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

二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是 二级指针
即存放指针变量的地址的指针,二级指针指向的空间的值是一个一级指针。

int main(){	int a = 20;	int* p = &a;	int** pp = &p;	printf("%d/n", *p);//解引用即可拿到指针指向空间里面的值	printf("%d/n", *p); //对二级指针,需要两次解引用才能拿到最开始的值	return 0;}


二、野指针

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

形成野指针的原因

  1. 指针未初始化
#include int main(){	int *p;//局部变量指针未初始化,默认为随机值	*p = 20;//往一个随机的地址(不属于本程序的空间)里面放入数据,是不允许的	return 0;}
  1. 指针越界访问
    指针想要访问不属于本程序的空间,造成越界访问,程序出错,在使用数组的时候最容易发生。
#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;}
  1. 指针指向的空间释放
    当动态开辟的内存被释放后,此时的指针就属于野指针,它指向的位置它不能正常访问。关于动态内存分配,后续会细讲。
int main(){	//开辟一个整形空间	int* p = (int*)malloc(sizeof(int));	//释放该空间	free(p);	//此时p即为野指针,因为它指向的空间已经无法访问,一般需要将其置空	//将其指向空指针,防止后续调用出错	p = NULL;	return 0;}

如何规避野指针

  • 指针初始化
  • 小心指针越界
  • 指针指向空间释放即使置NULL
  • 指针使用之前检查有效性

三、指针的基本运算

关于指针的运算一般有两个,指针+指针,语义是合法,但没有意义。

  • 指针± 整数
  • 指针-指针

指针± 整数

对int和char类型的指针分别进行+1操作,%p以16进制打印

int main(){	int n = 10;	char* pc = (char*)&n;//将n的地址(指针)强制转换成char* 类型	int* pi = &n;	printf("&n:    %p/n", &n);	printf("pc:    %p/n", pc);	printf("pc + 1:%p/n", pc + 1);	printf("pi:    %p/n", pi);	printf("pi + 1:%p/n", pi + 1);	return 0;}


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

指针-指针

int main(){	int arr[10] = { 0 };	//取出数组第一个元素和最后一个元素的地址	int* start = arr;	int* end = &arr[9];	printf("%d/n", end - start);	return 0;}


总结:
指针-指针等于它们之间相差的类型数据的个数。即本例的第10个元素和第1个元素之间相差9。

四、指针和数组

一起来看一下数组名是什么

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


可见数组名和数组首元素的地址是一样的。

结论: 数组名表示的是数组首元素的地址。也是一个指针

那么我们就可以用一个指针来代替数组名,如下代码:

int main(){	int arr[10] = { 0 };	int* p = arr;	int i = 0;	int sz = sizeof(arr) / sizeof(arr[0]);//计算数组元素个数	//对数组赋值	for (i = 0; i < sz; i++)	{		*(p + i) = i;	}	//打印数组数据	for (i = 0; i < sz; i++)	{		printf("%d ", *(p + i));	}	return 0;}


注意:
数组名在下面两种情况下不是首元素的地址

  1. sizeof(数组名) - 这里的数组名不是首元素的地址,是表示整个数组的,这里计算的是整个数组的大小,单位还是字节
  2. &数组名 - 这里的数组名不是首元素的地址,是表示整个数组的,拿到的是整个数组的地址
int main(){	int arr[10] = { 0 };	int sz = sizeof(arr);	printf("sizeof(arr)计算的是整个数组的大小:%d/n", sz);	printf("数组首地址:      %p/n", arr);	printf("数组首元素地址:  %p/n", &arr[0]);	printf("数组的地址:      %p/n", &arr);	printf("数组首地址+1:    %p/n", arr + 1);	printf("数组首元素地址+1:%p/n", &arr[0] + 1);	printf("数组的地址+1:    %p/n", &arr + 1);	//数组名确实是首元素的地址	//但是有2个例外:	//1. sizeof(数组名)  - 这里的数组名不是首元素的地址,是表示整个数组的,这里计算的是整个数组的大小,单位还是字节	//2. &数组名 - 这里的数组名不是首元素的地址,是表示整个数组的,拿到的是整个数组的地址	//	return 0;}

五、指针数组

存放指针的数组。

int main(){	int a = 1;	int b = 2;	int c = 3;	int* arr[10] = { &a,&b,&c };	for (int i = 0; i < 3 ; i++)	{		printf("%d ", *(arr[i]));	}	return 0;}


总结

指针是C语言非常重要的一部分,内容繁多不易懂,本文仅介绍了一些基本知识,后续还会深入了解指针。

希望能对大家学习指针有所帮助!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/121276.html

相关文章

  • 单链表介绍和基本操作(C语言实现)【保姆级别详细教学】

    单链表的基本操作【超详细备注和解释】 先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力 看完之后别忘记关注我哦!️️️ 强烈建议本篇收藏后再食用 文章目录 单链表基本介绍基本结构与顺序表的区别以及学习单链表的必要性 单链表的实现结点的定义以及头指针的创建单链表的遍历(打印接口的实现)【重点】开辟结点接口尾插接口尾删接口头插接口...

    王陆宽 评论0 收藏0
  • 白话Java I/O模型

    摘要:因为所有的数据从最底层讲是字节,那么就可以使用字节流这个概念去指代数据动态转移这个过程。而数据的转移,就是把一堆字节流从运往。创建内存中的中转区域,然后将上面的文件的字节流直接接入到这个。然后再从把字节流输出到对应的。 I/O的很多操作和使用,其实并不是一个非常直观的概念,特别是打开文件、创建buffer。这对于终端用户来讲是个非常奇葩和奇怪的过程。我只是想要从一个文件里读取内容,从过...

    VincentFF 评论0 收藏0
  • 开学了,计算机大学生们,送你们一经书,希望你们四年不负年华!

    摘要:作为十几年的老开发者,今天我来分享一下,我个人认为的大学计算机相关专业该怎么学,希望你们的四年能够不负年华。粉丝专属福利九关于考研有能力去考研的,我建议去尝试一下考研,理由有以下几点第一,毕业就工作的人,前三年还处于摸索和定性的阶段。 ...

    duan199226 评论0 收藏0
  • 从小白程序员一路晋升为大厂高级技术专家我看过哪些书籍?(建议收藏)

    摘要:大家好,我是冰河有句话叫做投资啥都不如投资自己的回报率高。马上就十一国庆假期了,给小伙伴们分享下,从小白程序员到大厂高级技术专家我看过哪些技术类书籍。 大家好,我是...

    sf_wangchong 评论0 收藏0
  • 开始学习机器学习之前你必须要了解知识有哪些?机器学习系列入门

    摘要:进入当前程序的学习系统的所有样本称作输入,并组成输入空间。结束语注意这篇文章仅仅是我接下来的机器学习系列的第一篇,后续还会有更多的内容。 往期回顾:统计学习方法第...

    leoperfect 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<