c语言-指针(1)

本文详细讲解了内存地址、指针变量的取地址和解引用操作,探讨了const修饰指针的作用,以及指针运算(+整数、-指针)和野指针的产生原因及避免方法。
摘要由CSDN通过智能技术生成

目录

1.内存和地址

2.指针变量

2.1取地址操作符(&)

2.2解引用操作符(*)

2.3指针变量的大小

3.指针变量类型的意义

3.1指针的解引用

3.2指针+-整数

4.const修饰指针

4.1const修饰变量

4.2const修饰指针

4.2.1const放在*左边的情况

4.2.2const放在*右边的情况

4.2.3const在*左右都放的情况

5.指针运算

5.1指针+-整数

5.2指针-指针

5.3 指针的关系运算

6.野指针

6.1造成野指针原因  

6.1.1指针未初始化

6.1.2指针越界访问

6.1.3指针指向的空间被释放

6.2如何避免野指针


1.内存和地址

要学习指针,当然要先了解计算机的内存和地址的概念。

内存是计算机用来存储数据和程序的地方,内存被分为一个个内存单元来存储数据和程序。内存以字节为单位存储数据,每个内存单元的大小取一个字节。一个字节能放8个比特位并且每个内存单元(字节)都有一个唯一的地址。c语言给地址起了一个新的名字:指针。可以理解为:地址==指针。

2.指针变量

指针变量顾名思义也就是存放指针(地址)的变量,存放在指针变量中的值就是地址。

2.1取地址操作符(&)

在c语言创建变量就是向内存申请一块空间。

#include <stdio.h>
int main()
{
	int a = 10;  //创建了整型变量a,向内存申请4个字节,用于存放整数10。
	return 0;
}

每个字节都有自己的地址,上图中每个字节的地址分别是:

1.0x00AFFA18

2.0x00AFFA19

3.0x00AFFA1A

4.0x00AFFA1B

c语言中用&操作符得到地址:

&a取出的是a所占4个字节中地址较⼩的字节的地址。

2.2解引用操作符(*)

#include <stdio.h>
int main()
{
	int a = 5;
	int* pa = &a;
	return 0;
}

定义pa的类型是int *。*说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int)

类型的对象。可以参考下面这张图来理解这段代码。
创建整形变量a向内存申请一块大小为4个字节的内存空间存放数字10,这块内存空间的地址为0x00AFFA18,随后创建指针变量pa存放0x00AFFA18(也就是整形变量a的地址)。
c语言通过解引用操作符(*),找到地址所指向的空间。
pa得到a的地址,对pa解引用找到该地址所指向的内存空间(也就是a所在的内存空间),此时其实*pa就是a,所以对*pa操作就是对a进行操作。

2.3指针变量的大小

指针变量是存放地址的,故指针变量的大小就是地址的大小。

x86环境下打印指针的大小:

x64环境下打印指针的大小:

32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。
比较容易混淆的是,错把指针变量的大小与指针的类型挂钩。

3.指针变量类型的意义

指针变量的⼤⼩和类型⽆关,只要是指针变量,在同⼀个平台下,⼤⼩都是⼀样的,为什么还要有各 种各样的指针类型呢?

3.1指针的解引用

对比,下面两段代码,观察内存的变化

 代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。

结论:指针变量的类型不同,对指针解引用操作时的权限也不同,也就是指针类型决定了对指针解引用能操作几个字节。int *类型的指针能操作4个字节,char *类型的指针能操作1个字节。

3.2指针+-整数

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
	int a = 5;
	char* pc = (char *)&a;
	int* pi = &a;

	printf("&a   = %p\n",&a);
	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;
}	

 上面这段代码的运行结果如下:


根据运行结果可以发现,char*类型指针+1跳过一个字节,int*类型指针+1跳过4个字节。

结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

4.const修饰指针

4.1const修饰变量

 

在C语言中,关键字"const"修饰一个变量n具有了常属性(不能被修改了),n是不是常量呢?虽然a是不能被修改的,但是本质上还是变量,我们称作常变量。上图中n=10;会引发编译错误。

备注:在C++ 中const修饰的变量就是常量

本质上n还是一个变量只不过被const修饰后,在语法上加了限制,只要我 们在代码中对n就⾏修改,就不符合语法规则,就报错,致使没法直接修改n。 但是如果我们绕过n,使⽤n的地址,去修改n就能做到了,虽然这样做是在打破语法规则。

可见虽然n被const修饰了,不能直接改变n的值,但是我们可以通过指针间接修改n的值。但是我们是想n的值是不被改变的。有什么办法可以同时限制通过指针也不能改变n的值吗?

4.2const修饰指针

4.2.1const放在*左边的情况

可见当const放在*左边的时候,p的值能修改,*p的值不能修改

4.2.2const放在*右边的情况

int a = 0;
int *const p = &a;

可见当const放在*右边的时候,p的值不能改,*p的值能改

4.2.3const在*左右都放的情况

可见当const在*左右都放的时候,p和*p的值都不能改变

总结

const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。

const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指 向的内容,可以通过指针改变。

5.指针运算

5.1指针+-整数

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}	

数组在内存中是连续存放的,得到首元素的地址,就能陆续找到后面元素的地址了。

5.2指针-指针

前提条件:两个指针指向同一块空间,只有两个指针指向同一块空间才能相减!!!

两个指针相减的得到的数字的绝对值是两个指针之间元素的个数

5.3 指针的关系运算

也就是指针和指针比较大小

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr/*&arr[0]*/;
	int sz = sizeof(arr) / sizeof(arr[0]);
	while (p < sz + arr)		
	{
		printf("%d ", *p);
		p++;
	}
	return 0;
}	

6.野指针

指针变量指向的内存地址是未知的或者无效的

6.1造成野指针原因  

6.1.1指针未初始化

#include <stdio.h>
int main()
{
	int* p;		//指针未初始化,默认为随机值
	*p = 20;	
	return 0;
}	

6.1.2指针越界访问

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	for (int i = 0; i <= 10; i++)
	{
		*(p++) = i;	 //当p指针指向的范围超过数组的范围,p就是野指针了
	}
	return 0;
}	

6.1.3指针指向的空间被释放

#include <stdio.h>

int* test()
{
	int n = 0;
	return &n;
}
int main()
{
	int* p = test();
	return 0;
}	

函数test()中返回了一个局部变量n的地址。一旦test()函数执行完毕,n的内存空间会被释放,但指针p却继续指向这块内存空间,导致指针p指向的内存已经被释放。

野指针可能会导致程序崩溃、内存泄露、数据损坏、安全漏洞等等不可预估的行为。因此在开发时我们要避免造成野指针。

6.2如何避免野指针

  • 对指针进行初始化,如果不知道指针指向哪里,可以给指针赋值null。
  • 小心指针越界,指针指向那块空间,就只能访问哪块空间,不能越界。
  • 指针不再使用时,及时置为null,使用之前,检查指针的有效性
  • 避免返回局部变量的地址

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值