您现在的位置是:首页 >技术交流 >【C生万物】 指针篇 (初级)网站首页技术交流
【C生万物】 指针篇 (初级)
欢迎来到 Claffic 的博客 💞💞💞 👉 专栏:《C生万物 | 先来学C》👈
前言:
面对C语言,很多童鞋都会高呼:指针难,指针难,对于我来说,指针是解决问题的一大杀器,是C/C++的灵魂,先不考虑难不难的问题,上了车再说。
目录
Part1:何为指针
1.概念
在初始C语言的时候就提到了,指针就是地址,具有指向作用。
这里有指针理解的两个要点:
• 指针是内存中一个最小单元的编号,也就是地址;• 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
总结一下,还是指针就是地址,口语中说的指针就是指针变量。
理解:
内存中的每个字节都是有标号的,称为地址:
(地址以十六进制展示)
考考你,如何取到指针变量呢?
用取地址符号&,在操作符篇已经讲过了。
指针变量:
我们可以通过 & (取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量。
#include <stdio.h>
int main()
{
int a = 10; // 在内存中开辟一块空间
int* p = &a; // 使用&操作符,取出变量a的地址
return 0;
}
a 变量占用4个字节的空间,这里是 将 a 的4个字节的第一个字节的地址存放在p变量中
p就是一个指针变量。
2.编址和指针大小
不知道你注意了吗,上方展示的是一个字节编一个地址,那么为什么这么做呢?
任何软件上的行为都可以归结到硬件上:
对于32位的机器,假设有32根地址线,
每根地址线在寻址的时候会产生高电压(1)或低电压(0),
32根地址线产生的地址就有:
00000000 00000000 00000000 0000000000000000 00000000 00000000 00000001... ...11111111 11111111 11111111 1111111011111111 11111111 11111111 11111111
计算一下,这里有2^32个地址,
每个地址标识一个字节,就可以给4GB的空间进行编址。
(一位0/1为一个比特位,1Byte == 8bit)
• 在 32 位的机器上,地址是 32 个 0 或者 1 组成二进制序列,那地址就得用 4 个字节的空间来存储,所以一个指针变量的大小就应该是4 个字节。• 在64 位机器上,如果有 64 个地址线,那一个指针变量的大小是 8 个字节,才能存放一个地址。
总结:
指针变量是用来存放地址的,地址是唯一表示一个内存单元的指针的大小在 32 位平台是 4 个字节,在 64 位平台是 8 个字节
Part2:指针使用
1.类型
我们知道变量是有类型的,那么指针有类型吗?
答案是肯定的,
如:
p 没有指定类型,所以会爆红。
正确情况应该是:
以下是一些常见的指针类型:
char* pc = NULL;
int* pi = NULL;
short* ps = NULL;
long* pl = NULL;
float* pf = NULL;
double* pd = NULL;
可见,指针的定义方式是 type + * + name
变量的类型与指针的类型是对应的,
如 int* 指针存储 int 类型的变量,char* 指针存储 char 类型的变量。
2.解引用
指针的解引用符号也是 * ,是由地址找到变量的操作。
以上只是笼统的说法,实际上这种说法并不准确。
看下面这段代码:
pc 和 pi 是不同的,区别在于访问的位数不同
指针的类型决定了对指针解引用的时候有多大的权限(能操作几个字节)。
Part3:野指针
1.概念
野指针就像一条野狗,它面向的位置是随机的
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
2.成因
2.1指针未初始化
#include <stdio.h>
int main()
{
int* p; // 局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
此处就像随机找个房间闯入... ...
2.2指针越界访问
在数组中存在越界访问:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 1; i <= 11; i++)
{
*(p++) = i; // 当i等于11时,数组越界访问
}
return 0;
}
当指针指向的范围超出数组arr的范围时,p就是野指针。
2.3指针指向的空间释放
试想,这有一块空间,有个指针指向它,突然这块空间被释放了,
指针不见了空间,那它不就变野了吗?
以上是笼统的说法,具体到动态内存开辟篇讲解
3.如何规避
这里总结几种规避野指针的方法:
• 指针初始化• 小心指针越界• 指针指向空间释放,及时置NULL• 避免返回局部变量的地址• 指针使用之前检查有效性
敲代码时养成好习惯。
对于检查有效性,这里详细解释下:
#include <stdio.h>
int main()
{
int* p = NULL;
int a = 10;
p = &a;
if (p != NULL)
{
*p = 20;
}
return 0;
}
例如这段代码中,要改变 a ,提前用 p 保存了 a 的地址,在访问之前检查一下 p 是否为空,确保指针的有效性。
Part4:指针运算
1.指针 +- 整数
用下面这段代码测试:
#include <stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("%p
", &n);
printf("%p
", pc + 1); // char*类型的指针
printf("%p
", pi);
printf("%p
", pi + 1); // int*类型的指针
return 0;
}
运行结果:
指针 + 1,地址走了多大由指针类型决定。
运用这一点,我们可以这样遍历数组:
int main()
{
float values[5];
float* vp;
for (vp = &values[0]; vp < &values[5];)
{
*vp++ = 0; // 后置++,先使用后++
}
return 0;
}
2.指针 - 指针
在计算数组长度的场景下可以利用指针 - 指针
模拟 strlen 函数:
int my_strlen(char *s)
{
char *p = s;
while(*p != '