C语言指针全归纳-初级版

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就是野指针。

野指针:指针指向的位置是不可知的。(随机的、不正确的、无明确限制的)

野指针形成原因共一下几种可能:

  1. 指针未初始化
#include<stdio.h>
int main()
{
	int *p;
	//此时p未初始化,里面是随机值,乱用会很危险
	*p = 20;
	return 0;
}
  1. 指针越界访问
#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;
}
  1. 指针指向的空间释放

那么如何规避野指针呢?

  1. 指针初始化
int *p=NULL;//不知道要指向谁就置成空指针
or
int a=1;
int *p=&a;//知道要指向谁
  1. 防范指针越界
  2. 指针指向空间释放后就及时置成空指针
  3. 指针使用前检验有效性
    指针置成空指针时是无法使用的
    要学会使用断言(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;
}
要明白什么是指针,必须先要弄清楚数据在内存中是如何存储的,又是如何被读取的。 如果在程序中定义了一个变量,在对程序进行编译时,系统就会为这个变量分配内存单元。编译系统根据程序中定义的变量类型分配一定长度的空间。内存的基本单元是字节,一字节有8位。每字节都有一个编号,这个编号就是“地址”,它相当于旅馆的房间号。在地址所标示的内存单元中存放的数据,就相当于在该旅馆房间中居住的旅客。 大家一定要弄清楚“内存单元的地址”和“内存单元的内容”这两个概念的区别,即“房间号”和“房间内所住客人”的区别。在程序中一般是通过变量名来对内存单元进行存取操作的。其实程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。这种按变量地址存取变量的方式称为直接访问方式。 还有一种间接访问的方式,即变量中存放的是另一个变量的地址。也就是说,变量中存放的不是数据,而是数据的地址。就跟寻宝一样,可能你按藏宝图千辛万苦找到的宝藏不是金银珠宝,而是另一张藏宝图。按C语言的规定,可以在程序中定义整型变量、实型变量、字符型变量,也可以定义这样一种特殊的变量,它是存放地址的。 由于通过地址能找到所需的变量单元,所以可以说,地址“指向”该变量单元。如同一个房间号指向某一个房间一样,只要告诉房间号就能找到房间的位置。因此在C语言中,将地址形象地称为“指针”,意思就是通过它能找到以它为地址的内存单元。 所以,一个变量的地址就称为该变量的指针指针就是地址,而地址就是内存单元的编号。它是一个从零开始的、操作受限的非负整数。
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值