Skip to content Skip to footer

【C语言】深入理解指针(1)什么是地址&指针

目录

前言

(一)内存与地址

从实际生活出发

地址

内存

内存与地址关系密切

(二)指针变量

(1)操作符

指针变量与取地址操作符

指针变量与解引用操作符

(2)指针的大小

(3)指针的运算

i,指针 +- 整数

ii,指针-指针

iii, 指针的关系运算

(4)指针的类型的意义

void* 指针

(5)const修饰指针

(三)野指针

(1)野指针成因

(2) 如何规避野指针

前言

C语言是一种直接操作内存的编程语言,我们可以直接访问和操作计算机内存中的地址空间。

而C语言中存在的指针类型,指针指向的就是内存中的地址。我们可以通过指针来访问和修改内存中存储的数据。

因此,深入理解指针,并且理解内存,对于编写高质量的程序和调试程序故障都非常有帮助。

在理解指针之前,我们先引入内存与地址的概念:

(一)内存与地址

从实际生活出发

地址

从实际生活中的问题出发——假如你要去上课,但是学校里教学楼那么多,如果想要上课,就得挨个教室去找,这样当你找到上课的教室,估计也下课了。

在实际生活中,我们是如何解决找东西效率低这一问题呢?

——我们可以将东西排好序,提前熟悉它们的位置,甚至编好号。

我们将每栋教学楼的每一个房间一一编号,这样,你要去上课,只要有了楼号和房间号,就可以快速找到房间,找到教室。

于是,C语言借鉴生活中的实例,从而有了地址的概念。

内存

我们知道计算上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数 据也会放回内存中。

我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何高效的管理呢?

其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节。

一个字节有多大呢?

常见内存单位

1byte(字节) = 8bit

1KB = 1024byte

1MB = 1024KB

1GB = 1024MB

1TB = 1024GB

1PB = 1024TB

(一个内存单元的大小取为1字节是比较合适的,如果取得过大,那么计算机的内存就会小很多,如果过大,那么内存无法被高效利用)

内存与地址关系密切

每个内存单元也都有⼀个编号(这个编号就相当于教学楼的门牌号),有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。

生活中我们把门牌号也叫地址,在计算机中我们 把内存单元的编号也称为地址。

C语⾔中给地址起了新的名字————指针

所以我们可以理解为:

内存单元的编号 == 地址 == 指针

(二)指针变量

(1)操作符

指针变量与取地址操作符

我们对内存和地址有了初步的了解,我们再回到C语言,在C语言中创建变量其实就是向内存申请空间:

#include

int main()

{

int a = 10;

return 0;

}

⽐如,上述的代码就是创建了整型变量a,内存中申请4个字节,用于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:

0x007DF784 0x007DF785 0x007DF786 0x007DF787

上图中,出现了一个陌生的操作符—— &(取地址操作符)

作用:取出变量的地址

比如,我们可以将a的地址放在pa中,并用%p 打印出来:

但是,我们发现打印出的只有一个地址, 并且与第一次看到的a的地址不同!

原因在于:

1.每一次运行代码,编译器都会重新给变量分配内存空间,这也就解释了为什么第二次打印的地址与第一次不同。

2.同时虽然整型变量占用4个字节,我们只要知道了第⼀个字节地址,顺藤摸瓜访问到4个字节的数据也是可行的。

在上图中,又出现了一个陌生的操作符

指针变量与解引用操作符

我们通过取地址操作符(&)拿到的地址是⼀个数值,如:0x006FFD70,这个数值有时候也是需要 存储起来,⽅便后期再使用的,那我们把这样的地址值存放在哪里呢?

答案是:指针变量中。

我们可以通过创建一个指针变量,来存储变量的地址:

#include

int main()

{

int a = 100;

int* pa = &a;

*pa = 0;

}

*pa 的意思就是通过pa中存放的地址,找到指向的空间, *pa其实就是a.

于是,解引用操作符的作用就是通过地址找到对应的变量。

(2)指针的大小

我们知道,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后 是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。

如果是32位机器,那么指针的大小是4个字节。

如果是64位机器,指针的大小是8个字节。

int main()

{

printf("%zd\n", sizeof(char *));

printf("%zd\n", sizeof(short *));

printf("%zd\n", sizeof(int *));

printf("%zd\n", sizeof(double *));

return 0;

}

如图,演示:

注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。

既然指针的大小与类型无关,那么指针类型存在的意义是什么?

在讨论这个问题之前,我们先引入指针的运算的概念

(3)指针的运算

指针的基本运算有三种,分别是:

• 指针+- 整数

• 指针-指针

• 指针的关系运算

i,指针 +- 整数

#include

//指针+- 整数

int main()

{

int arr[10] = {1,2,3,4,5,6,7,8,9,10};

int *p = &arr[0];

int i = 0;

int sz = sizeof(arr)/sizeof(arr[0]);

for(i=0; i

{

printf("%d ", *(p+i));//p+i 这⾥就是指针+整数

}

return 0;

}

1.数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸瓜就能找到后面的所有元素。

2.指针加减整数,表示跳过当前指针类型的元素的个数。

ii,指针-指针

//指针-指针

#include

int my_strlen(char *s)

{

char *p = s;

while(*p != '\0' )

p++;

return p-s;

}

int main()

{

printf("%d\n", my_strlen("abc"));

return 0;

}

1.图中实现的是strlen函数的模拟实现

2.指针-指针 表示两个指针变量之间的元素的个数

iii, 指针的关系运算

//指针的关系运算

#include

int main()

{

int arr[10] = {1,2,3,4,5,6,7,8,9,10};

int *p = &arr[0];

int i = 0;

int sz = sizeof(arr)/sizeof(arr[0]);

while(p

{

printf("%d ", *p);

p++;

}

return 0;

}

1.指针的关系的比较,实际上就是地址大小的比较。

(4)指针的类型的意义

1.指针的类型决定了,对指针解引用的时候有多大的权限(⼀次能操作几个字节)。

//代码2

#include

int main()

{

int n = 0x11223344;

char* pc = (char*)&n;

*pc = 0;

return 0;

对于n的地址:

*pc = 0;之前

之后:

2.指针的类型决定了指针向前或者向后一步有多大(距离)。

#include

int main()

{

int n = 10;

char *pc = (char*)&n;

int *pi = &n;

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* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。 这就是指针变量的类型差异带来的变化。

void* 指针

在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为无具体类型的指针(或者叫泛型指 针),这种类型的指针可以用来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进行指针的+-整数和解引用的运算。

``更具体来说,什么样的指针类型就决定了什么样的对数据的运算方法。``

即使是一个float*类型的指针,赋值给void*指针,强转为int*或者double*再解引用仍然可以使用,但是数值的内容是未定义的!

示例:

int a = 10;

float b = 1;

void* any = &a;

cout<

any = &b;

cout<

*(float*)any = 1.2;

cout<<*(float*)any<

//什么样的指针类型,决定了什么样的对数据的运算方法

cout<<*(int*)any<

cout<<*(double*)any<

结果:

(补充时间:2025年3月25日)

(5)const修饰指针

见详解

const修饰指针https://mp.csdn.net/mp_blog/creation/editor/134341320

(三)野指针

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

(1)野指针成因

1. 指针未初始化

2. 指针越界访问

3. 指针指向的空间释放

( 3的实例 )

#include

int* test()

{

int n = 100;

return &n;

}

int main()

{

int*p = test();

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

return 0;

}

(2) 如何规避野指针

1.指针初始化为NULL;

2.指针进行数组运算时小心越界;

3.指针变量不再使用时,及时置NULL,指针使用之前检查有效性;

4.避免返回局部变量的地址;

5.assert断言;

完~

未经作者同意禁止转载

Copyright © 2088 上届世界杯冠军_u20世界杯八强 - longxinwl.com All Rights Reserved.
友情链接