C语言指针基础

这篇具有很好参考价值的文章主要介绍了C语言指针基础。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

本期介绍内容:

一、指针是什么?

二、指针和指针类型

指针类型的意义:

三、野指针

3.1什么是野指针?

3.2野指针的成因

1.指针未初始化

2.指针越界访问

3.指针指向的那块空间已经释放(还给操作系统了)

3.3如何规避野指针

四、指针运算

4.1指针加减整数

4.2指针-指针

4.3指针的关系运算

五、指针和数组

六、二级指针

6.1什么是二级指针?

七、指针数组 


前言

我们经常听到很多大佬都说指针是C语言中的精髓、指针很重要,但对于很多初学者都觉得指针很难、不都不知道指针是什么?指针到底指向哪里??指针和地址是什么关系?本期小编将带您弄清楚指针的基本问题!

本期介绍内容:

1.指针是什么

2.指针和指针类型

3.野指针

4.指针运算

5.指针和数组

6.二级指针

7.指针数组

一、指针是什么?

1.指针是内存中一个最小的单元编号,也就是地址(唯一标识一个内存单元)。

2.我们一般所说的指针是指针变量,是用来存放内存地址的变量。

也就是说:指针就是地址,口语中的指针是指针变量

为了更好地管理内存,计算机把内存分为了若干的内存单元,每一个内存单元都有一个唯一的编号也就是地址。由于该地址(编号)指向该内存空间因此也形象的把地址(编号)称为指针!

OK,画个图理解一下:

C语言指针基础,c语言,开发语言

指针变量:我们知道地址在内存中是以十六进制的形式存储,通过&操作符就可以取出变量的地址,取出来要一个变量接收与存储,这个变量就是指针变量!

举个栗子:

int main()
{
	int a = 3;
	int* pa = &a;
	return 0;
}

int* pa 的*号说明pa是一个指针变量(口语中是指针),前面的int说明pa指向对象的类型是一个整型。后面的&a取出a的地址赋值给pa此时pa就指向a;

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

到这里我们就基本了解清楚了指针是什么以及指针和地址的关系,指针和指针变量的关系!

通上面的介绍我们知道指针唯一标识一块内存单元。但又有一些新的问题出现了:例如

一个内存单元的大小是多大呢?

还有地址是十六进制的数他又是如何进行编址的呢?

指针变量的大小是多少?

下面我们一起来分析一下:

通过了解计算机组成原理发现一个内存单元给一个字节较为合适(既不浪费也不会不够)。

解释:我们知道C语言中char类型的变量占一个字节,如果说每一个内存单元大于1个字节,char类型的变量开辟空间的时候就会出现浪费的情况,因此综合下来一个内存单元为一个字节刚好!

另外,它的编址是由地址线产生的高低电频来进行的

对于32位平台(x86)机器,假设有32根地址线(物理电线),那么假设每根地址线在寻址的时候都会产生高电平(高电压)和低电频(低电压)就可以把电信号转换换为数字信号也就是0和1(这块是电路方面的数电和模电小编了解过一二,它里面还介绍了加法器,二极管等等有兴趣的可以看看);

那么32根地址线就会是下面这中情况:

C语言指针基础,c语言,开发语言

这里就是2^32个地址。而我们上面知道每一个地址标识唯一的一个内存单元,我们就可计算一下2^32个地址可以给多大的空间进行编址(2^32Byte == 2^32/1024KB == 2^32/1024/1024MB == 2^32/1024/1024/1024GB == 4GB)也就是说2^32个地址可以为4GB的内存空间编址。64位平台的机器同理,感兴趣的可以算一算!

32位平台的机器上,地址是32个0或1组成的二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小是4个字节

64位平台的机器上,地址是64个0或1组成的二进制序列,那地址就得用8个字节的空间来存储,所以一个指针变量的大小是8个字节

举个栗子验证一下:

int main()
{
	printf("%d\n", sizeof(int*));
	printf("%d\n", sizeof(char*));
	printf("%d\n", sizeof(float*));
	printf("%d\n", sizeof(double*));
	printf("%d\n", sizeof(long*));
	return 0;
}

32位平台(x86):

C语言指针基础,c语言,开发语言

64位平台(x64):

 C语言指针基础,c语言,开发语言

这里还要注意一点sizeof返回值是size_t实际上是unsigned int

C语言指针基础,c语言,开发语言 打印的时候要用%zd否则会报警告! ! !

总结

指针变量是用来存放地址的,地址是唯一标识一个内存单元的。

指针的大小在32位平台上是4个字节,在64位平台上是8个字节。

二、指针和指针类型

我们都知道,变量有不同的类型,有整型、浮点型,字符型等,那么指针变量有没有类型呢?

答案是:有的!

我们将一个变量的地址取出来(&)存到pa中,pa就是一个指针变量,给他的类型是什么呢?

答案是:指针指向的那个变量的类型加*即:type + *

举个栗子:

int main()
{
	int a = 3;
	int* pa = &a;
	char b = 'a';
	char* pb = &b;
	float f = 1.2f;
	float* pf = &f;
	double d = 13.4;
	double* pd = &d;
	return 0;
}

这里int*类型的指针存放的是int类型变量的地址,char*的指针存放的是char类型的地址...上面我们知道指针类型要么都是4个字节要么8个字节,那为什么还要分类型呢?

指针类型的意义:

先说结论:

(1)指针类型可以决定指针解引用的时候的访问权限(可以访问多好个字节)

(2)指针类型可以决定指针+/-的步长

例如:int类型指针+1跳过4个字节,char类型的指针+1跳过1个字节,那+/-n 就是跳过n *sizeof(type)个字节!!!

解释(1):

int main()
{
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;
	return 0;
}

关于这段代码解释两点,首先a存的这个数(16进制)不会溢出,十六进制的1位相当于二进制的4位,int类型的a32个二进制位所以11223344刚好把a放满!其次,会发现他在内存中是倒着存的,这个是大小端字节序的问题,后面会专门有一期介绍!

C语言指针基础,c语言,开发语言这是执行到*pa = 0;发现变成了00 00 00 00

C语言指针基础,c语言,开发语言

 我们把int *改为char *再来看看:

int main()
{
	int a = 0x11223344;
	char* pa = &a;
	*pa = 0;
	return 0;
}

C语言指针基础,c语言,开发语言

一开始还是和int*的一样!我们接着往下看:

C语言指针基础,c语言,开发语言

我们发现只改掉了 一个字节的内容!这就完美的证明了上面的结论1!

解释(2):

int main()
{
	int a = 3;
	int* pa = &a;
	char* pc =(char*) &a;
	printf("%p\n", pa);
	printf("%p\n", pc);

	printf("%p\n", pa+1);
	printf("%p\n", pc+1);
	return 0;
}

我们分析,前两个打印的是一样的,主要看后两个:

C语言指针基础,c语言,开发语言

前两个果然一样(都存的是第一个字节的地址),后面是因为一个是int类型一次访问4个字节,另一个是char类型一次访问一个字节 ,访问权限不一样步长也就不一样!

三、野指针

3.1什么是野指针?

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

3.2野指针的成因

1.指针未初始化
int main()
{
	int* p;
	*p = 10;
	return 0;
}

此时,p是一个指针变量未初始化,也是局部变量(在函数内)由前面的函数栈帧的创建与销毁可知,局部变量未初始化是随机值(0hccccc),把随机的一块内存给改成10,这多少有点危险!

2.指针越界访问
int main()
{
	int arr[10] = { 1 };
	int* p = arr;
	int i = 0; 
	for (i = 0; i <= 10; i++)
	{
		printf("%d ", arr[i]);
	}
		return 0;
}

arr数组只有十个元素,下标0--9,你直接访问10将是一个随机值!

3.指针指向的那块空间已经释放(还给操作系统了)
int* test()
{
	int a = 3;
	return &a;
}

int main()
{
	int* p = test();
	printf("%d\n", *p);
	return 0;
}

这里a 是在test函数里面创建的,当return &a后test的函数栈帧就已经销毁了(还给操作系统了),当下面在去解引用的时候就指向随机位置了!

3.3如何规避野指针

(1)指针初始化

(2)小心指针越界

(3)指针指向的空间释放及时置为NULL(NULL空指针实际上就是0)

(4)避免返回局部变量的地址

(5)指针使用之前检查其有效性(if判断和断言)

C语言指针基础,c语言,开发语言

举个栗子:

#include<assert.h>
int main()
{
	int* a = NULL;//指针初始化
	int p = 8;
	int* pp = &p;
	assert(pp);
	*pp = 20;
	printf("%d ", *pp);

	int b = 12;
	int* pb = &b;
	if (pb != NULL)
	{
		*pb = 20;
	}
	printf("%d\n", *pb);
	return 0;
}

四、指针运算

4.1指针加减整数

指针加减整数的结果我们知道还是一个指针,我们可以通过指针的加减的偏移量来访问指针指向的变量的值(例如数组)!

举个栗子:

int main()
{
	int arr[10] = { 1 };
	int* pp = &arr[0];
	for (int i = 0; i < 10; i++)
	{
		*pp++ = 0;
	}

	int* p = arr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

这个栗子就很好的展现了指针加一个整数去通过其偏移量来访问元素的值!

补充:

这里我们可以看到在打印的时候是以*(p + i)打印的,我们在没有了解指针的时候是以arr[i]打印的,而p里面存的是arr数组的首元素的地址,也就是说*(arr + i) 和arr[i]是等价的!而由加法的交换律我们可以变形一下: *(arr + i)  == *(i + arr)而*(arr + i)  ==arr[i] 我们可以推断 *(i + arr) == i[arr]是否也成立呢?

验证一下:

C语言指针基础,c语言,开发语言

答案是成立的!这里小编验证这个不是为了说以后建议大家写代码就这样写,这样写多少有点太装了!!!这里介绍这个主要是为了再次说明一下:下标引用操作符[ ] 它的两个参数一个是数组名一个是索引(下标),他和 + 一样,a + b == b + a 一个道理!

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;
}

考虑一下答案是多少?

C语言指针基础,c语言,开发语言

答案是9!为什么是9呢?下面我来画个图解释一下 :

C语言指针基础,c语言,开发语言我们通过对数组和内存的了解画出了上面的这个图!并得出结论:

指针减指针得到的绝对值(可能是低地址减高地址也有可能是高地址减低地址,所以取绝对值)是两指针之间的元素个数!并且注意的是:指针-指针的前提是两指针指向同一块内存!!否则无意义!

介绍到这里我就想到了一个与这个高度贴合的题(模拟实现strlen 指针-指针)当然模拟实现strlen 的方式有三种这里我将介绍这一种(后面会一一把其他两种介绍):

int  MyStrlen(char* str)
{
	char* p = str;
	while (*str)
	{
		str++;
	}

	return str - p;
}

int main()
{
	char str[] = "abcdef";
	int len = MyStrlen(str);
	printf("%d ", len);
	return 0;
}

strlen 是求字符串长度(‘\0’之前)的函数!当然我们这仅仅是一个雏形,基本功能已经实现,已经能精准的测量出字符串的长度了,后面还有优化的点等到后面介绍了断言和const了再来优化!

看一下结果:

C语言指针基础,c语言,开发语言

4.3指针的关系运算

我们知道地址有大小,而指针的关系运算就是比较指针的大小!

OK!举个栗子:

int main()
{
	int arr[5] = { 1 };
	int* p = NULL;
	for (p = &arr[5]; p > &arr[0]; )
	{
		*--p = 0;
	}
	return 0;
}

这段代码不是您是否看的懂?我们一起来看一看:

C语言指针基础,c语言,开发语言 此时聪明的你肯定能想到优化方案:

int main()
{
	int arr[5] = { 1 };
	int* p = NULL;
	for (p = &arr[4]; p >= &arr[0]; p--)
	{
		*p = 0;
	}
	return 0;
}

OK,这个方案的确看起来好多了!但,这样写的代码对吗?

答案是:在有的编译器上是不一定对的会报越界的警告!

C语言标准规定:

允指向数组元素的指针与指向数组左后一个元素后面的那个内存中位置的指针比较,但不允许与指向数组第一个元素的那个内存位置的指针比较!

什么意思我们画个图理解一下:

C语言指针基础,c语言,开发语言

虽然现在部分编译器对上面的第二段代码不会报错但有的编译器会,为了以后不会出现类似的问题还请大家遵循标准!!!

我们现在来看看他为什么不对:

C语言指针基础,c语言,开发语言

我们发现当你初始化到&arr[0] 的空间时候还是成立的,那就先初始化再调整!p--就到数组前面的一块空间去了!

五、指针和数组

我们说:指针是指针,数组是数组,一般无关系!只有数组名和首元素地址有关!

OK,我们先来看个栗子:

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	return 0;
}

他的结果是什么呢?

C语言指针基础,c语言,开发语言

哦吼~他两的结果一样,所以可以得出结论:数组名一般情况下表示数组首元素的地址!两种情况除外(1.sizeof(数组名)计算的是整个数组的大小 2.&数组名取出的是整个数组的地址)。这块在数组和操作符都介绍过,这里不在多介绍了!

既然数组名是首元素的地址也就是指针,那么能不能用指针的偏移量来不访问数组元素呢?

答案是肯定的:

C语言指针基础,c语言,开发语言

六、二级指针

我们知道指针变量里面存的是变量的地址,不知您有没有想过对指针变量取地址该存到哪里?

6.1什么是二级指针?

其实指指针变量也是变量,他也有地址(和普通变量一样)&后就得用指针变量存起来,而这个指针变量里面存的就是指针的地址(指针),也就是说一个指针变量里面存的是另一个一级指针变量的地址这样的指针变量就叫二级指针

举个栗子:

int main()
{
	int a = 3;
	int* pa = &a;
	int** ppa = &pa;
	return 0;
}

 ppa就是一个二级指针!!  int** ppa后面的一颗*说明paa是一个指针变量!前面的int*则说明ppa指向的对象是(一级)指针类型!

理解到这里就可以以二级指针类比三级指针了:三级指针就是里面存的是二级指针变量地址的指针变量!

int main()
{
	int a = 3;
	int* pa = &a;
	int** ppa = &pa;
	int*** pppa = &ppa;
	*(*(*pppa)) = 10;
	printf("%d ", a);
	return 0;
}

我我们这里pppa就是一个三级指针,*pppa指向ppa,*ppa指向pa,*pa指向a!然后赋值10;

C语言指针基础,c语言,开发语言

七、指针数组 

我们经常听说的两个东西:一个是指针数组一个是数组指针很多人都搞不清楚这个东西,其实数组指针本质是指针!而指针数组本质是数组!下面小编带您look一look一下指针数组,数组指针会在后面的指针进阶里面讲!本期仅仅在这里提一下,后面重点对比!!!

指针数组本质是一个数组!是存放指针的数组!

我们知道一个整型或字符行的数组,里面都存放的是相对应类型或比该类型小的元素,那指针数组里面存的应该就是地址!

举个栗子:

int main()
{
	int arr1[] = { 1,2 };
	int arr2[] = { 3,4 };
	int arr3[] = { 5,6 };
	int* arr[] = { arr1,arr2,arr3 };
	return 0;
}

此时的arr就是一个指针数组!画个图理解一下:

C语言指针基础,c语言,开发语言

看到这里就很应该很清楚了!不知您看到这个图的时候有没有一点感觉很熟悉? 这个是不是和二维数组很相似。arr管理的三个一维数组,我们前面说过二维数组是数组的的数组!是不是和这个很相似?那我们能不能访问呢?

int main()
{
	int arr1[] = { 1,2 };
	int arr2[] = { 3,4 };
	int arr3[] = { 5,6 };
	int* arr[] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");

		for (int j = 0; j < 2; j++)
		{
			printf("%d ", *(*(arr + i) + j));
		}
		printf("\n");
		
	}
	return 0;
}

看结果,他这两种打印结果一样:

C语言指针基础,c语言,开发语言

OK,本期指针基础就分享到这里!好兄弟我们下期再见! 文章来源地址https://www.toymoban.com/news/detail-519346.html

到了这里,关于C语言指针基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C语言基础入门】二级指针、一维数组与指针、二维数组与指针

    在学习C语言的过程中,理解指针的概念是非常重要的。指针提供了一种直接访问内存地址的方式,使得我们可以更加灵活地管理数据和内存。在本文中,我们将介绍C语言中的二级指针、一维数组与指针,并通过通俗易懂的语言和示例代码来帮助读者理解这些概念。 二级指针

    2024年02月05日
    浏览(57)
  • C语言基础之——指针(上)

    前言:小伙伴们又见面啦!本期内容,博主将展开讲解有关C语言中指针的上半部分基础知识,一起学习起来叭!!! 目录 一.什么是指针 二.指针类型 1.指针的解引用 2.指针+-整数 三.野指针 1.野指针成因 (1)指针未初始化  (2)指针越界访问 2.如何规避野指针 四.总结 指针

    2024年02月11日
    浏览(49)
  • C语言基础之——指针(下)

    前言:本篇文章将继续讲解有关指针的剩余基础知识。 学无止境,一起加油叭!! 目录 一.指针运算 1.指针 +- 整数  2.指针的关系运算  3.指针 - 指针 二.指针与数组 三.二级指针 四.指针数组 总结 指针运算包括以下三种: 指针 +- 整数 指针 - 指针 指针的关系运算 我们尝试用

    2024年02月11日
    浏览(35)
  • 【C语言基础】:深入理解指针(三)

    指针系列回顾 : 【C语言基础】:深入理解指针(一) 【C语言基础】:深入理解指针(二) 一、冒泡排序 冒泡排序的核心思想就是:两两相邻的元素进行比较。 可以看到,这段代码对arr数组进行了排序,但这个代码还有一些缺陷,那就是无论数组内部的元素是否有序,他都会循

    2024年03月10日
    浏览(41)
  • C语言-基础语法学习-3 二级指针

    当涉及到多级指针时,C语言的灵活性和强大的指针功能可以得到充分的发挥。二级指针是指指向指针的指针,也被称为指向指针的引用。 使用二级指针可以实现以下功能: 动态内存分配:通过二级指针可以动态地分配内存块,并将其地址传递给其他函数或模块进行操作。这

    2024年02月12日
    浏览(45)
  • 【C语言初阶】带你轻松掌握指针基础知识完结篇——野指针,指针运算,指针和数组,二级指针

    君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,这里是君兮_,今天继续给大家更新0基础入门C语言的内容,我们这次主要更新的依然是初阶指针的基础知识 废话不多说咱们直接开始吧!! 概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有

    2024年02月16日
    浏览(47)
  • C语言 指针声明和定义 - C语言零基础入门教程

    目录 [一.指针简介] [1.内存] [2.内存地址] [3.指针声明] [二.指针类型] [三.声明并初始化一个指针] [1.声明指针并直接初始化 – 推荐] [2.先声明指针在初始化 – 不推荐] [四.查看指针地址和指针的值] [五.NULL 指针 – 空指针] [六.重点总结] [七.猜你喜欢] 零基础 C/C++ 学习路线推荐

    2024年03月22日
    浏览(43)
  • 逍遥自在学C语言 | 指针的基础用法

    在C语言中,指针是一项重要的概念,它允许我们直接访问和操作内存地址。 可以说,指针是C语言一大优势。用得好,你写程序如同赵子龙百万军中取上将首级;用得不好,则各种问题层出不穷,有种双拳难敌四手的感觉。 本文将介绍指针的基础知识,包括指针的定义、初始

    2024年02月09日
    浏览(44)
  • 【C语言初阶】带你轻松掌握指针基础知识(1)——指针的定义,类型,大小

    君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,这里是君兮_,最近刚回家有点懒,从今天开始恢复更新并开始更新新的刷题系列,我们先继续更新0基础入门C语言的内容,今天给大家带来的是指针方面的内容,但由于是初阶,一些高级的用法我们放在进阶篇再讲

    2024年02月12日
    浏览(44)
  • 【C语言】指针超级无敌金刚霹雳进阶(但不难,还是基础)

    点击这里访问我的博客主页~~ 对指针概念还不太清楚的点击这里访问上一篇指针初阶2.0 上上篇指针初阶1.0 谢谢各位大佬的支持咯 今天我们一起来学习指针进阶内容 指针变量有 字符指针变量 , 数组指针变量 和 函数指针变量 char* 叫做字符指针 一般这么来使用: 通过指针存

    2024年03月09日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包