C语言指针详解(基础概念+案例解释)(一)

本文详细介绍了指针、地址和内存的基础概念,包括指针作为内存地址的表示,指针变量的使用,以及指针运算(如指针加减整数和指针相减)。同时探讨了野指针的形成原因和如何避免野指针,强调了初始化和边界检查的重要性。
摘要由CSDN通过智能技术生成

一:指针基本概念:

(一)基础概念:地址和内存:

地址和内存:

地址类似于一个编号,一个标识,它相对来说具有唯一性。

内存是计算机内部的一块儿空间。

例如:学生宿舍的编号就是地址,宿舍本身就是一个内存。通过宿舍编号能够找到学生宿舍,如图,里面的编号就是地址,通过每个地址就能找到一个对应的小格子(内存)。

地址就是内存的编号,通过地址能够查找到相应空间的内存。

(二)指针相关基本概念理解:

指针:可以说指针就是地址,地址就是指针,把他们当作一个相同的概念来理解。

指针变量:用来存储指针(地址)的变量。它和其他变量(int ,char,short,float,double,long,long...)是一回事儿,表示形式:(其他变量类型) 加 *   变量名字。举例:int * ,char * ,float*等等。

&操作符:&用来获取空间的地址,也就是上面说的编号。

*(解引用操作符):用来找到指针变量指向的内容。

指针类型:去掉变量名剩下的部分。

有了上面的简单解释之后看代码和注释:

//vs环境下

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

int main()
{
	int num = 12;
	char* p = &num;//去掉p得到char*,char*就是指针的类型,p是指针的名字。//这里&得到了num这个内存空间的地址,将它存在p里面
	
	printf("%d\n",*p);//*找到p指向的地址里面的内容
	printf("%d\n",num);

	int size = sizeof(p);
	printf("指针变量的大小是:%d\n", size);
	//sizeof(int),sizeof(char),我们知道int类型的大小是4个字节,char类型的大小是个字节等等。这里同理可以计算指针类型变量的大小
	//指针变量的大小取决于环境32位4,64位是8,我使用的是64位
	return 0;
}

运行:

二:指针运算:

(一)指针加减整数:

指针加减整数会移动指针指向的位置,移动的位置大小取决于指针类型。

例如:char*p指向的是char类型的变量,char的大小是1字节,所以当p+1的时候跳过一个char类型变量的空间;同理int*p时p+1跳过一个int类型变量也就是4字节,其他类型同理。

看代码和注释:

#include <stdio.h>
int main()
{
	int n = 10;
	char* pc = (char*)&n;//强制类型将int*转换成char*
	int* pi = &n;
	//d%p打印地址
	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	return 0;
}

运行结果:

可以看到char*类型的指针变量跳过了一个字节,int*类型的跳过了4个字节

(二)指针相减

指针减指针得到的是两指针指向的地址之间的元素

代码示例看注释理解:

#include <stdio.h>

int main()
{
	char* p = "abcdefghi";//p指向的是字符串的首元素地址,后面详细讲解
	
	p = p + 5;//指针p现在指向的是字符串第五个字符后面的位置(指向f)
	char* ptr = p;
	p = p - 5;//将指针p重新指向初始位置a的地址
	printf("%s\n%s\n", p, ptr);//现在是指针p和otr各自指向的位置打印的字符串

	printf("%d\n", (ptr - p));//两之相减得到的结果

	return 9;
}

两指针不能够相加,相加是非法的,可以自己试一试,会报错的。

三:const修饰指针:

const是限定修饰的值不可以修改。如const int num = 12;这里num = 0;这种操作编译器就会报错,自己可以试一试。但是使用指针可以绕过去修改cost修饰的变量的值。看下图:

不可修改。

#include <stdio.h>

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

可以修改,原来的0被修改成了12。

(1)const在指针前面:

当我们试图修改num指向的值的时候,发现编译器报错了。

#include <stdio.h>
int main()
{
	const short* num = 12;
	printf("%hd\n", num);
	printf("%p\n", num);//num被修改之前的地址
	//*num = 0;这里是不被允许的
	num = NULL;//将指针变量num的值置为空
	printf("%p\n", num);//num被修改之后的地址
	return 0;
}

所以:const修饰指针变量在*的前面的时候其对应地址可以修改,但是指针指向的内容不可以修改.

(2)const在*后面:

注意:因为上面的代码,可能有些同志会将代码写成这样:这样编译器不会报错,但是确实错的,因为const已经限制了num后面的值12,12是一个整型,然而前面的short*是指针变量,应该修饰的是指针,前后不对应,是错的。

还是刚刚的代码,我们发现在修改其地址的时候编译器是会报错的,所以不能够修改其地址,我们试一下能不能修改它的

int main()
{
	short m = 12;
	short* const num = &m;
	printf("num修改之前的值为:%hd\n", *num);
	*num = 0;
	printf("num修改后的值为:%hd\n", *num);

	//printf("%p\n", num);//num被修改之前的地址
	//num = NULL;//将指针变量num的值置为空
	//printf("%p\n", num);//num被修改之后的地址
	return 0;
}

运行:

(3)*前后都有const时,地址和值都不可以修改。可以自己试一下。

四:野指针

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

野指针的成因:

(1)未初始化指针:

#include <stdio.h>

int main()
{
	int* ptr;
	printf("%p\n", ptr);

	return 0;
}

当我们执行上述代码的时候:

(2)指针越界访问:

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

上述代码指针随着循环的进行,指向arr[10]后面就是随机的了,前面arr中的10个元素都是初始化为0的,指针指向的位置相当于是知道的,但是后面指向10个元素之后的地址是没有初始化的,由系统随机分配,根据指针的定义,指针指向的地址是随机的,那他就是野指针。这个代码i<=12程序会崩掉,但是i<=11就不会,可以自己去尝试一下。

(3)指针指向的空间被释放:

#include <stdio.h>
char* ret()
{
	char ch = 10;
	char* ptr = &ch;
	return  ptr;
}
int main()
{
	char* ch = ret();
	printf("%p", ch);

	return 0;
}

上面代码每次得到的结果都不同:

得到的地址是随机的。

解释:因为ret()函数内局部变量的值和空间随着函数的执行完毕就会被销毁,所以返回的地址也是一个无效的,随机的地址,将它赋给ch,ch打印出来的就是随机的地址。

如何规避野指针:

(1)记得给指针初始化;

(2)注意指针的越界访问;

(3)注意指针指向的空间变化。

欢迎评论指正不对或者不恰当之处。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值