C语言——指针

本文详细介绍了C语言中的指针,包括指针作为地址的概念,地址运算符`&`和解引用运算符`*`的使用,指针类型的特性和作用,以及指针与数组的关系。文中还提到了二级指针、野指针的问题及避免方法,并探讨了指针运算的规则,如指针与整数的加减运算和指针间的减法用于计算元素个数。此外,通过实例展示了如何利用指针计算字符串长度。
摘要由CSDN通过智能技术生成

前言

指针(pointer)是一个值为内存地址的变量(或数据对象),也可以说,指针就是地址

地址运算符:&

解引用运算符:* ;注意禁止解引用未初始化的指针

指针类型决定了指针解引用的权限有多大;决定了指针走一步有多大,指针加 1 指的是增加一个存储单元

一、指针

1.指针就是地址

内存怎么编号:

32 位——32 根地址线——通电下,0/1 组成二进制序列——2 的 32 次方个内存单元

一个内存单元就是一个字节(1 byte),那么 2 的 32 次方 byte 换算后就是 4GB

这也同时解释了为什么 32 位操作系统所支持的最大内存为 4GB

为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址

2.地址运算符 &

用地址运算符 & 给出变量的存储地址

& 取一个变量的地址,只会拿出这个变量所占空间的第一个字节的地址

这里来调试观察一下一个 int 型变量的地址

在这里插入图片描述

将 8 赋给变量 num(int型,占4字节),这时 &num 取到的地址为 0x004FF760,也就是 num 的四个字节中第一个字节的地址(存放着8)

3.解引用运算符 *

*:解引用运算符,访问指针指向的地址存放的数据

指针变量用于存放地址(指针),同时内存会给指针变量开辟一个空间

例如:

int main()
{
	int a = 8;
	int* pa = NULL;//1、声明 int* 型变量 pa,并初始化为 NULL
	pa = &a;//2、通过取地址符&,获取 a 的地址,然后赋值给指针变量 pa
	printf("%d", *pa);//3、解引用 pa,获取指针指向的内容
	
    //打印结果为 8
	return 0;
}

注意不能解引用未初始化的指针,如

int* pa;
*pa = 3;//错误,这时 pa 成了野指针

取地址符 & 和解引用运算符 * 的共同点:

运算符的优先级都相同,都为2,且都是右结合性的

二、指针类型

1.不同类型的指针的大小是相同的,占4字节

在这里插入图片描述

2.指针类型决定了指针解引用的权限有多大(能操作几个字节)

int 型指针解引用访问 4 个字节,char 型指针解引用只访问 1 个字节**

int main()
{
	int i = 0x11223344;
	int* pi = &i;
	*pi = 0;
	printf("%#x\n", i);

	int a = 0x11223344;
	char* pc = &a;
	*pc = 0;
    //char* 型指针变量,访问 int 型变量 a 的第一个字节的数据,即 44
	printf("%#x\n", a);

	return 0;
}

在这里插入图片描述

3.指针类型决定了指针走一步有多大

int* 型指针走一步即 4 字节,char* 的指针走一步即 1 字节

指针加 1 指的是增加一个存储单元

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	char* pc = arr;

	//int*
	printf("%p\n", p);
	printf("%p\n\n", p + 1);//走 4 字节

	//char*
	printf("%p\n", pc);
	printf("%p\n", pc + 1);//走 1 字节

	return 0;
}

在这里插入图片描述

三、数组和指针的关系

数组和指针的关系十分密切

可以用指针表示数组的元素和获得元素的值

arr + 2 == &arr[2];
*(arr + 2) == arr[2];

体现了C语言的灵活性

可以这样理解 *(arr + 2) :到内存的 arr 位置,偏移 2 个存储单元,访问那里的值

注: 指针加 1 指的是增加一个存储单元;对数组而言,这意味着加 1 后的地址是下一个元素的地址,而不是下一个字节的地址

四、二级指针

在这里插入图片描述

int main()
{
	int a = 8;
	int* p = &a;
	int** pp = &p;

	printf("a = %d\n", a);
	printf("*p = %d\n", *p);
	printf("**pp = %d\n", **pp);

	return 0;
}

在这里插入图片描述

五、野指针

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

1.指针未初始化

int main()
{
    int* p;
    *p = 20;
    //非法访问内存
    //这时,p就是野指针
    return 0;
}

这里局部指针变量没有初始化,则默认是随机值

这种情况如何避免野指针:

当不指定 p 应该初始化为什么地址的时候,直接初始化为NULL,例如 int p = NULL;

2.指针越界访问

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

这里指针指向的范围超出了数组 arr 的范围;超出时,p 成为野指针

3.指针指向的空间释放

int main()
{
    int a = 0;
    int* p = NULL;
    p = &a;
    *p = 20;
    
    {
        int b = 30;
        p = &b;
    }//b的生命周期结束,b的内存销毁(注意不代表粉碎)
    
    *p = 50;//b的内存已销毁,这时p指向的是一个无效的地址,p这时成为野指针
    printf("%d\n", a);//20
    printf("%d\n", *p);//50,虽然b对应的内存已经销毁,但仍然存在
    return 0;
}

这种情况如何避免野指针:

指针指向空间释放时及时指向NULL

六、指针运算

1.指针和整数的加减运算

int i = 1;
int arr[5] = { 1,2,3,4,5 };
int* p = &arr[0];
//*p == arr[0] == 1

//p + i == arr[1] == 2
//++ p == arr[1]

//*(arr + 1) == arr[1]
//不能对 arr 进行自增(++)自减(--)的运算

假设一个存储整形数据的数组 arr 长度为 5,int* 型指针 p 指向 arr 数组的首元素

对于指针变量

  • 指针和整数的加减法运算,如 p + i == arr[1],指针是按对应大小的存储单元偏移的;视指针所指向类型的大小为单位
  • 指针变量的自增(++)自减(–)运算也是同样的运算过程,如 ++ p == arr[1]

对于数组名(数组名是首元素的地址)

  • 数组名加上一个整数后 * 运算就可以访问其他元素(不越界),如 *(arr + 1) == arr[1]
  • 但不能对数组名自增(++)自减(–)的运算,因为数组名不等价于指针变量,自增和自减对数组名来说是非法的

2.指针 - 指针

<1>指针 - 指针 得到的是两个指针之间的元素个数

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

批注: 指针和指针相减的前提是两个指针指向同一块空间,即同一个数组中的元素

<2>例:用指针和指针相减的方法创建一个函数求字符串长度

思路: 字符串传参传的是首字母的地址,通过循环语句找到字符串末尾’\0’的地址,将这个地址减去首字母的地址即可求得字符串长度

int My_strlen(char* p)
{
    char* p1 = p;
    while (*p != '\0')
    {
        p++;
    }
    return p - p1;
}
int main()
{
    int len = My_strlen("Hello");
    //字符串传参传的是首字母的地址
    printf("%d\n", len); //5
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

青春无限坑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值