【C语言】初阶指针(1)

目录

前言

1. 内存、地址、指针

2. 指针变量和地址

2.1 取地址操作符(&)

2.2 指针变量和解引用操作符(*)

3. 指针变量的大小

4. 指针变量类型的意义

4.1 指针的解引用操作

4.2 指针 +- 整数

5. 指针的算术运算

5.1 指针 +- 整数

5.2 指针 - 指针

5.3 指针的关系运算

结语


前言

在C语言中,有很多操作都是直接针对内存操作的。指针亦是如此,今天就让我们就来讲解C语言这一大特点,也是难点 —— 指针和指针操作

1. 内存、地址、指针

要想理解内存、地址、指针之间的关系,我们得先理解电脑内存是怎么一回事儿?

在我们购买电脑时,会有各种电脑内存规格:8GB/16GB/32GB……,在电脑处理数据的时候,都是要在内存中读取,数据在处理好后也会放回到内存中。我们都知道,电脑需要处理的数据量是非常庞大的,那么这些内存空间要怎么设计才能更高效管理呢?

其实计算机会把内存划分为一个个的内存单元,每个内存单元的大小为 1 个字节,每个字节又对应 8 个比特位。每个内存单位元也都有一个编号,有了编号,CPU(中央处理器)就能更快速地找到一个内存空间。

在计算机中,我们把内存单元的编号也成为地址。C语言中也给地址起了一个新称号:指针

所以我们可以认为:内存单元的编号 == 地址 == 指针

2. 指针变量和地址

2.1 取地址操作符(&)

在C语言中,我们创建变量时其实都会向内存申请空间。例如,我们创建一个整型变量 a,它在内存中会占据 4 个字节,用于存放整数 1,而每个字节都有地址,我们可以来看看 a 的地址:

我们可以用取地址操作符 & 来获取变量的地址,它需要用 %p 来打印

#include <stdio.h>

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

打印结果如下:

这时候就会有小伙伴想问,不是说一个整型变量 a 在内存中会占据 4 个字节吗,问什么这里才显示一个地址?哎,问得好!实际上,我们在用 &a 取出地址时,它取出的是 a 所占 4 个字节中地址较小字节的地址。

眼尖的小伙伴也发现了,变量 a 的地址问什么会变化。这是因为在VS2022环境下,变量的地址不是永恒不变的,它是内存随机分配,因此每次打印所得到的地址也会不一样,但这也不影响我们理解内存的本质

2.2 指针变量和解引用操作符(*)

在我们使用取地址操作符(&)拿到一个地址时,我们可以把这个数值存放在指针变量当中,比如:

#include <stdio.h>

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

int* p = &a  ————> 取出 a 的地址存放到指针变量 pa 当中

而指针变量也是一种变量,它是专门用来存放地址的,存放在指针变量当中的值我们都理解为地址

我们来解析一下指针变量

int a = 1;
int* p = &a;

p 是指针变量名,在 p 的左边是 int *  * 号表示 p 是指针变量, int 则表示该指针变量指向的是整型( int )类型的对象,如图:

同理可得,当我们有一个字符类型 char 的变量 b,那么就需要 char * 类型的指针变量来存放它的地址,即 char * p = &b。

对于一个指针来说,我们把指针变量名去掉后,得到的就是指针变量类型,如 char * int * ……


我们使用指针变量存放某一变量的地址后,为的是方便后面使用,当我们想要使用它时,就得靠解引用操作符(*)来帮忙,我们先来看一个例子

#include <stdio.h>

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

运行结果如图:

在上面代码当中,我们就使用了解引用操作符 * p 的意思就是通过 p 中存放的地址,来找到它所指向的空间,即 a ,用 * p = 2 ,来让 a 的值变成 2,因此当我们再打印 a 的值时,结果就为 2

3. 指针变量的大小

指针变量,归根结底也是一种变量。而是变量就会有大小,即占有多少个字节

指针变量相比其他类型的变量,略显特殊,它是专门用来存放地址的,它的大小与所在的环境有关。在 32 位机器下,每个地址都有 32 个比特位,就需要 4 个字节才能存储,因此指针变量的大小就得是 4 个字节。同理,在 64 位机器中,指针变量的大小就为 8 个字节。我们可以在 VS2022 的 X86 和 X64 的环境下,使用 sizeof 关键字来验证一下

        

                                         X86(32位)                                                                                                    X64(64位)

我们可以看到,无论是哪种指针变量类型,得到的大小都是一样的 

总结一下:

  • 在 32 位平台下,指针变量的大小是 4 个字节
  • 在 64 位平台下,指针变量的大小是 8 个字节
  • 注意指针变量的大小与类型无关只要是指针类型的变量,在相同的平台下,大小都是相同的

4. 指针变量类型的意义

指针变量大小与类型无关,那为什么还需要各种各样的指针类型呢,哎,这么想不无道理,但创建这么多不同的指针类型还是很有必要的,我们接着往下看

4.1 指针的解引用操作

我们写两段代码,a 的值为 16 进制的 11223344,指针变量 p 我们分别设为 int * char * ,并分别进行解引用操作,监视内存我们可以发现, int * 在解引用后将 a 的四个字节全部改成 0;而 char * 在解引用后则是将 a 的较小的字节改成 0,剩下的三个字节还是不变

         

结论一:指针类型决定了对指针解引用时能有多大的权限,即一次能操作多少个字节。对于char * 类型来说,解引用就只能访问 1 个字节,而对 int * 来说,解引用可以访问 4 个字节

4.2 指针 +- 整数

当指针加减整数时,也与指针类型有关,我们可以来看一段代码

#include <stdio.h>

int main()
{
	int a = 10;
	int* p1 = &a;
	char* p2 = (char*)&a;

	printf("%p\n", &a);
	printf("%p ——> %p\n", p1, p1 + 1);
	printf("%p ——> %p\n", p2, p2 + 1);

	return 0;
}

运行结果如下:

通过观察我们可以看出, char * 类型的指针 +1 跳过了 1 个字节,而 int * 类型的指针 +1 则跳过了 4 个字节

结论二:指针类型可以决定指针向前或向后走多少个字节(而我们可以利用这个特性来实现数组的创建和打印)

小练习:使用指针创建一个数组并打印出来

#include <stdio.h>

int main()
{
	int arr[10];
	int i = 0;
	int* p = arr; //数组名即数组首元素的地址
	for (i = 0; i < 10; i++)
	{
		scanf("%d", (p + i));
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

运行结果如下:

在这里所使用的就是结论二,又因数组在内存中是连续存放的,所以只要知道第一个元素的地址,就能顺藤摸瓜地找到后面的元素

5. 指针的算术运算

5.1 指针 +- 整数

还是那个例子,用指针打印数组

#include <stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int* p = arr; //数组名即数组首元素的地址
	size_t sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i)); //这里就是指针+-整数
	}
	return 0;
}

运行结果如下: 

5.2 指针 - 指针

例子一:求数组中的元素个数

#include <stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int num = (p + 10) - p;//这里就是指针-指针
	printf("%d\n", num);
	return 0;
}

运行结果如下:

 

在同一数组中,指针 - 指针得到就是两元素相差的个数,因此得到的就是 10

例子二:我们可以写一个函数 my_strlen 来模拟 strlen ,作用是求取字符串的长度

#include <stdio.h>

int my_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
	{
		p++;
	}
	return p - s;//这里就是指针-指针
}

int main()
{
	int len = my_strlen("abcd");
	printf("%d\n", len);
	return 0;
}

运行结果如下:

字符串的最后都会有 '\0' ,我们可以用这个特性来得到字符串的长度。使用 while 循环,当 *p != '\0' 时,p 就 ++;而最后当 *p == '\0' 时,就会跳出循环,返回 p - s 的值,即返回字符串长度,这样我们就能得到字符串的长度了

5.3 指针的关系运算

#include <stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;//arr是数组首元素的地址
	int i = 0;
	size_t sz = sizeof(arr) / sizeof(arr[0]);
	while (p < arr + sz)//指针的大小比较
	{
		printf("%d ", *p);
		p++;
	}
	return 0;
}

运行结果如下:

 

结语

今天我们一起学习了指针的一些基础知识,包括指针变量,指针类型等等;如有总结不到位的地方还请多多谅解,若有出现纰漏,希望大佬们看到错误之后能够在私信或评论区指正,博主会及时改正,共同进步!

欢迎各位在评论区友好讨论。如果觉得不错的话,麻烦您点个赞吧,十分感谢!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值