C语言——对于简单指针性质的介绍

本文介绍了指针在32位和64位机器上的不同表示,以及指针类型的差异。不同类型的指针在解引用时访问的字节数量不同,且指针进行整数加减时的步长由其类型决定。文章还讨论了野指针的问题,包括未初始化、越界访问和指向已释放空间的风险,并提出了规避策略。最后,概述了指针的加减运算和关系运算的应用。
摘要由CSDN通过智能技术生成

       在前面c语言简介的文章中提到,内存单元的大小是一个字节,指针(或者说地址)标记着每一个内存单元。对于32位和64位的机器,他们的编址方式是不同的,对于32位机器:

        32位机器中存在着32根地址线,每根地址线可以表示一个二进制位,由此可得,32根地址线可以表示的不同地址的数量是2^32,又因为:一个字节等于8个比特位,而且32根地址线的情况下,1个地址是由32个二进制位来表示的,所以,一个地址的大小就是4个字节。因此,32位机器中,存储一个地址(指针变量)需要4个字节大小的空间

       同理,对于64位机器,其可以表示的不同地址的数量是2^64个,存储一个内存单元所对应的地址(指针变量)需要8个字节大小的空间 

文章大体内容导读:

目录

1.指针的类型:

1.1 不同指针类型的意义:

1.1.1 不同类型指针访问字节的差异

1.1.2 不同类型指针进行整数加减时的差异

2.1 指针未初始化:

2.2 指针越界访问:

 2.3指针所指向的空间释放:

2.3 如何规避野指针:

3.指针运算:

3.1 指针运算——指针加减整数

 3.2 指针运算——指针和指针运算:

3.3指针的关系运算


1.指针的类型:

        在前面指针简介的文章中,只是提及了指针的概念、大小、简单应用,并没有对指针的类型进行展开介绍,和变量有整型,浮点型等。指针也存在不同的类型。

通过下面对不同类型指针的打印,便可以得到不同类型指针所占空间的大小:\

#include<stdio.h>
int main()
{
	printf("%d ", sizeof(int*));
	printf("%d ", sizeof(short*));
	printf("%d ", sizeof(long*));
	printf("%d ", sizeof(char*));
	printf("%d ", sizeof(float*));
	printf("%d ", sizeof(double*));
	return 0;
}

(注:计算机是64位计算机)

得到的结果如下:

可以看到,不同类型的指针,在64位计算机的中所占的空间大小都是8个字节,这里就会产生一个问题,既然不同类型的指针所占空间的大小都是一样的,那么设置这些不同类型的指针还有什么实际意义?

1.1 不同指针类型的意义:

1.1.1 不同类型指针访问字节的差异

对于不同类型指针的意义的探讨,将通过下面的若干例子进行说明:

如图, 将十六进制数字11223344(0x代表16进制)赋给4字节大小的a变量,因为在十六进制中,1个十六进制位等于4个二进制位,所以11223344这8个十六进制位就等于32个二进制位,又因为一个字节=8个二进制位,所以可以得出,0x11223344这一串数字,恰好可以占用4个字节大小的空间,也就是可以恰好填满变量a

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

当对上述代码在内存中进行监视时,可以发现:

注:此时程序仅仅运行到箭头所指的行所对应的代码

对变量a赋值的十六进制数字,也就是:11223344,但是可以看到,输入的内容在内存中的存储是倒叙的,对于为什么倒叙,将在后续文章进行解释,本文不说明。

当把上述程序完全运行后,可以通过内存窗口看到:

内存中存储的值全部变成0

接下来,将上述代码中变量a的类型从int改为char,即:

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

 此时,如果再从内存窗口监视上面更改过的代码(此时代码完全运行)

 

此时发现,当指针变量类型是char时,内存中只有第一个字节所对应的内容变成了00。通过对char和int类型指针变量的对比可以发现:

不同类型的指针,在进行解引用操作时,可以访问的的字节的数量是不同的,例如:对于Int类型的指针,在进行解引用操作时,可以访问4个字节,而char类型的指针在进行解引用时,只能访问1个字节

(注:指针在进行访问时,访问的内容所对应的地址是由低到高的顺序)

对于其他类型的指针,例如short float double 分别可以访问 2 4 8 个字节的内容,因此对于不同类型的指针,其可以访问内存内容的大小 = sizeof(type) (其中type代表不同的类型)

1.1.2 不同类型指针进行整数加减时的差异

 上面已经通过例子探讨出不同类型的指针变量的一个意义——不同类型指针变量可以访问内容的大小是不同的,下面,将通过另一个例子来探讨不同类型指针变量的另一个意义:

(注:为了方便查看指针地址的变化,下列代码将在x86环境下进行运行

int main()
{
	int a = 0x11223344;
	printf("%p ", &a);
	printf("%p ", &a + 1);

	return 0;
}

通过对上述代码对a和a+1的地址进行打印,可以得到下面的结果:

 即a的地址是:     00EF9FC   

     a+1的地址是:00EFFA00,由十六进制的运算可以得到,上面两个地址的差值恰好为4.

对上述代码进行更改,即把变量a的类型由Int改为char 

int main()
{
	char a = 0x11223344;
	printf("%p ", &a);
	printf("%p ", &a + 1);

	return 0;
}

执行上述代码,可以得到下面图中的结果:

此时,a的地址是:   001CE823

          a+1的地址是:001CF824

 两个地址之间的差值是1.

对比两种类型的指针变量的地址所对应的结果,得到不同类型指针变量的另一个性质,即:

不同类型的指针,决定了指针进行+、-n操作时的步长。

对于int整型变量,进行加n操作时,可以跳过4n个字节

对于char类型变量,进行加n操作时,可以跳过n个字节

所以,对于不同类型的变量进行+、-n操作时,可以跳过的字节= n * sizeof(type)

2.野指针:

2.1 指针未初始化:

给定如下代码:

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

如果对运行上述代码,会发现编译器显示如下错误:

这是因为,上述代码并没有对指针变量p进行初始化,对于未初始化的变量,编译器会给其赋随机值。这种未初始化的指针,就叫做野指针 ,也可以理解为,指向位置是不可知的指针。

2.2 指针越界访问:

给定下面代码:

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

 假设,用下面的方框代表创建的数组,方框下面的数字代表对数组的编号:

可以看到,在进入for循环后,指针变量pa先进行解引用,再++,也就是指针变量pa先对编号为0的数组进行赋值,赋值后指针变量移向编号为1的数组,再下次循环中,再次解引用,再++,以此类推,但是,当循环到pa加到10的时候,会发现,前面创建的数组的下标范围是0~9,没有编号为10的空间,这就造成了指针越界访问,例如下图 (红色方框代表超出的范围)

 2.3指针所指向的空间释放:

给定如下代码:

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

上述代码,想通过test函数返回值返回变量a的地址,再将a的地址赋给指针变量p,再打印p。但是

test函数内部的变量a是一个局部变量,局部变量在函数test运行结束后,就会自动销毁,因此,指针变量*p不能拿到变量a的地址。

2.3 如何规避野指针:

    1. 对明确知道指针可以初始化的地址时,及时对指针初始化。

    2.对不明确知道指针初始化的地址时,将指针暂时初始化为NULL(即空值),在后续代码书写时,如果得知了该指针可以初始化的地址,对该指针初始化。

    3.小心指针越界访问

    4.避免返回局部变量

    5.在使用指针之前,检查其有效性

3.指针运算:

3.1 指针运算——指针加减整数

   在文章探讨不同类型的指针变量的性质时,曾说到过指针加减整数的运算规律,这里不再过多探讨,只给出一个应用:

    例:在不适用数组下标的情况下,赋值并打印数组:

代码如下:

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

在前面介绍数组的文章中曾提到,数组名= 数组首元素的地址,所以,可以创建指针变量pa来存储数组首元素的地址,再通过对指针进行++,完成赋值并打印指针的目的,结果如下:

 3.2 指针运算——指针和指针运算:

给定下面代码:

int main()
{
	int arr[10] = { 0 };
	printf("%d ",&arr[9] - &arr[0]);
	return 0;
}

结果如下:

 

这里直接给出结论:指针-指针得到数值的绝对值的是两个指针之间的元素个数。

但是需要注意:指针-指针的条件是:指针和指针位于同一空间

3.3指针的关系运算

即进行指针之间的大小比较运算,例如:

int arr[10] = {0};
int*p = arr;
arr[5] > arr[0]

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

起床写代码啦!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值