C语言作为一门较为好上手的高级计算机语言,我相信任何一个开始学习编程的人都是先从他开始入手的,但是其中的指针曾叫人叫苦不迭。本文章旨在全面梳理C语言指针的知识点,内容非常宏大且精细,希望可以给看到本篇文章的人带来全新的指针认识。
本文为初阶版,我会尽快创作出高阶版的指针,喜欢本文的可以点个关注
1. 什么是指针
先来看看定义
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中的另一个地方的值。由于通过地址能找到所需的变量单元,所以可以说,地址指向该变量单元。因此,将地址形象化的称为‘指针’。意思是通过它能找到以他为地址的内存单元。
int a=10;
int *p=&a;
p是指针变量,存放内存单元的地址(整形占四个字节,存放首地址,其他类型亦是如此)
&a是a的地址,也就是指针存入p
所以说&a或p唯一指向a
地址是如何产生的呢?
地址就是数据在内存中的存储位置的“门牌号”
计算机有32位和64位的配置,已32位为例:
就是有32根地址线,可以在寻址时产生32个电信号(正或负),将电子信号转换为数字信号就是32位的01序列(64位就是64位的01序列),地址编号就是由此产生的,但是为了便于观察,我们会用16进制来进行表示。
由此也可以推断出32位机器的指针大小为4字节(32bit==4byte),64位机器的指针为8字节。
2. 指针的类型有哪些
int *p1;
char *p2;
double *p3;
float *p;
…………
这些指针虽然指向的数据类型不同,但本身大小都一样,
那么为什么要给出这么多指针类型呢?难道不能只设置一种类型指向所有吗?
比如pointer *p;
接下来我们看看指针类型有什么用呢?
以上两个图片我们可以看到
int* 型的指针解引用访问了四个字节的内容
char* 型的指针解引用只访问了一个字节的内容
由此可以看出指针类型的第一个作用:决定了指针的访问权限,即指针向后方访问几个字节
所以我们想要访问几个字节就用相对应字节数类型的指针进行存储。
从上图可看出不论是什么类型的指针存放的都是元素起始地址。
但不同类型的指针+/-1所改变的距离不同,这就是指针类型的第二个作用
所以我们如果用char*指针就可以把arr[10]当成四十个空间来使用。
3. 野指针
p得到地址时,地址指向的空间已经释放了,所以这个时候的p就是野指针。
野指针:指针指向的位置是不可知的。(随机的、不正确的、无明确限制的)
野指针形成原因共一下几种可能:
- 指针未初始化
#include<stdio.h>
int main()
{
int *p;
//此时p未初始化,里面是随机值,乱用会很危险
*p = 20;
return 0;
}
- 指针越界访问
#include<stdio.h>
int main()
{
int arr[10] = {
0 };
int *p = arr;
int i;
for (i = 0; i < 11; i++)
{
*(p + i) = i;
}
return 0;
}
- 指针指向的空间释放
那么如何规避野指针呢?
- 指针初始化
int *p=NULL;//不知道要指向谁就置成空指针
or
int a=1;
int *p=&a;//知道要指向谁
- 防范指针越界
- 指针指向空间释放后就及时置成空指针
- 指针使用前检验有效性
指针置成空指针时是无法使用的
要学会使用断言(assert)判断指针是不是空指针
#include<stdio.h>
#include<assert.h>
int main()
{
int *p=NULL;
assert(p!=NULL);
//上式为假会报错
return 0;
}
4. 指针的运算
- 指针+/-整数
指针加减整数上面其实已经提到了,*(p+i)就是一种应用
再介绍一种
#include<stdio.h>
int main()
{
int arr[10] = {
0 };
int *p = arr;
int i;
for (i = 0; i < 10; i++)
{
*p++ = i;
}
p = arr;//注意++对p产生了实际效果,所以要重置
for (i = 0; i < 10; i++)
{
printf("%d ", *p++);
}
return 0;
}
- 指针 - 指针
由此可知,指针-指针得到的值的绝对值是两者之间元素的个数
但是必须是两个指针指向同一块连续的空间,两个数组的话会造成不确定结果 - 指针的关系运算
其实际就是指针之间进行比较
#define N 5
int main()
{
float arr[N];
float *p;
for (p = &arr[5]; p > &arr[0];)
{
*--p = 0;
}
return 0;
}
5. 指针和数组
数组是指针吗?
指针式数组吗?
答案统统都是No,数组是一堆相同类型元素的集合,指针只是一个变量
他们之间的关系是数组可通过指针来访问
数组名表示的是数组首元素的地址,所以就可以将其存入指针中
在说指针越界那里已有举例,在此就不再过多赘述了。
强调一点:(p+i)==&arr[i]
6. 二级指针
一个int型的变量的地址可以存放到一个指针变量里边,那么这个指针作为一个变量是否也可以存放在一个指针变量里边呢,这就涉及到二级指针的概念了。
int main()
{
int a = 1;
int *p1 = &a;//一级指针
int **p2 = &p1;//二级指针
//p2->&p1,*p2->&a,**p2->a
//以此类推,可以得到以下式子,但使用几率不大
int ***p3 = &p2;
int ****p4 = &p3;
int *****p5 = &p4;
return 0;
}
7. 指针数组
指针数组是指针还是数组呢?
答案是数组。
int main()
{
//整形数组:存放整形元素的数组
int arr1[10] = {
0 };
//指针数组:存放元素为指针的数组
int a, b, c, d, e;
int *arr2[5] = {
&a, &b, &c, &d, &e };//存放整形指针的数组
char *ch[5] = {
NULL };//存放字符指针的数组
//介绍一下使用方法
char *c[5] = {
"hehe","haha","ohhhh"};
int i=0;
for(i=0;i<3;i++)
{
printf("%s ",c[i]);
}
return 0;
}
转载:https://blog.csdn.net/weixin_53451597/article/details/115023846