初级指针详解

指针(一)

在这里插入图片描述

指针

什么是指针?

计算机中,所有的数据都是存放在存储器中的,不同的数据类型占有的内存空间的大小各不相同。内存是以字节为单位的连续编址空间,每一个字节单元对应着一个独一的编号,这个编号被称为内存单元的地址。比如:int 类型占 4 个字节,char 类型占 1 个字节等。系统在内存中,为变量分配存储空间的首个字节单元的地址,称之为该变量的地址。地址用来标识每一个存储单元,方便用户对存储单元中的数据进行正确的访问。在高级语言中地址形象地称为指针。

地址与指针

指针相对于一个内存单元来说,指的是单元的地址,该单元的内容里面存放的是数据。在 C 语言中,允许用指针变量来存放指针,因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。

总结:

  1. 指针是内存中的一个最小单元的编号,也就是地址
  2. 平时口语中说的地址,通常指的是指针变量,是用来存放内存地址的变量。

关于内存我们通过下面这张内存分布图来简要理解

在这里插入图片描述

内存是用来存放数据的,为了更好的存储读取数据,把内存划分成一个一个的字节,所以字节是内存的最小组成单位。内存就好比我们现实生活中的酒店,一个个字节就是酒店中的房间,每个字节相应的地址,就是房间的门牌号。有一天张三在酒店开了房邀请其好友王五去配他通宵打王者。此时张三必须把酒店的位置及其房间的门牌号一同告知王五,否则张三就要孤独的一个玩游戏了。同理我们把数据存放到内存中,我们要得到它在内存中的地址,才能通过接接引得到数据并且进行操作。

指针变量

指针变量是一把打开字节房间的钥匙,我们可以通过&取出变量的地址,得到了地址我们会把它放到一个载体中也就是指针变量,这样可以便于我们理解与操作,毕竟拿字母去操作远远比拿一串地址去使用方便多。

代码示范使用指针变量

#include <stdio.h>
int main()
{
    
    
 int a = 10;//在内存中开辟一块空间
 int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
    //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
中,p就是一个之指针变量。
 return 0;
}

总结:

指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。

这里存在一个问题就是地址是如何编址的?在现实就拿我所在的这栋宿舍,住在2楼的房间,门牌号第一位数字是一 ,一共三位数,后面两位是根据该层楼有多少个房间,分布在哪里,会事先规定某一个是201房间然后依次得出所有的房间号。那么计算机又是如何编址的?经过计算机科学家的计算与权衡,最终发现一个字节给一个对应的地址是比较合适的。而对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0); 那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

11111111 11111111 11111111 11111111

这里就有2的32次方个地址。 每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 232/1024/1024MB==232/1024/1024/1024GB == 4GB) 4G的空闲进行编址。 同样的方法,那64位机器,如果给64根地址线,那能编址多大空间,自己计算。

这里我们就明白:

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地 址。

总结:

  • 指针是用来存放地址的,地址是唯一标示一块地址空间的。
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节。

指针和指针类型

我们来讨论一下指针的类型,我们都知道变量有不同的类型,整型,浮点型。那么是否浮点型有没有类型呢?答案是有的C语言内置类型都有其相应的指针。比如int a=90;,那么会有 整型指针去存放整型a在内存中所开辟空间的地址。

先来看看一段代码

int num = 10;
p = &num;

要将&num(num的地址)保存到p中,我们知道p就是一个指针变量,那它的类型是就是整型

下面给出指针变量相应的类型

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

注意:NULL为空指针,其数值为0.

这里可以看到,指针的定义方式是: type + *。

其实: char* 类型的指针是为了存放 char 类型变量的地址。 short* 类型的指针是为了存放 short 类型变量的地址。 int* 类型的指针是为了存放 int 类型变量的地址。

指针类型的意义

指针±整数

上代码来看看

#include <stdio.h>
//演示实例
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);//pc为字符指针 一个char就只一个字节 pc+1意味着地址加1
	printf("%p\n", pi);
	printf("%p\n", pi + 1);//pi为字符指针 一个int就只一个字节 pi+1意味着地址加4
	return  0;
}

结果图
在这里插入图片描述
结论:总结:指针的类型决定了指针向前或者向后走一步有多大(距离)。

指针的解引用

代码演示

#include<stdio.h>
int main()
{
    
    
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;
	/*char* pa = &a;
	*pa = 0;*/
	return 0;
}

调试所获得的内存信息

在这里插入图片描述

在这里插入图片描述
图一:由于是字符指针去存储整型变量的地址,但是在32位平台下指针大小都是4个字节,所以pa能放下a的的地址,当解引用赋值为0,只能改变一个字节的存储,也就是只能向前走一步。

图一:由于是整型指针去存储整型变量的地址,但是在32位平台下指针大小都是4个字节,自然pa能放下a的的地址,当解引用赋值为0,能改变4个字节的存储,能向前走四步,改变其数值。

结论:

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

  • 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

野指针

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

白话:
野指针就是指向内存中一块不确定或者我们没有权限访问的地方

野指针成因

  • 指针未初始化
#include <stdio.h>
int main()
{
    
     
 int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;
 return 0;
}

  • 指针越界访问
#include <stdio.h>
int main()
{
    
    
    int arr[10] = {
    
    0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   {
    
    
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
   }
    return 0;
}

如何规避野指针

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性

代码演示

#include <stdio.h>
int main()
{
    
    
    int *p = NULL;
    //....
    int a = 10;
    p = &a;
    if(p != NULL)
   {
    
    
        *p = 20;
   }
    return 0;
}

指针和数组

我们看一个例子:

#include <stdio.h>
int main()
{
    
    
 int arr[10] = {
    
    1,2,3,4,5,6,7,8,9,0};
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}

运行结果:

在这里插入图片描述
可见数组名和数组首元素的地址是一样的。

结论:数组名表示的是数组首元素的地址。(除了sizeof运算时和数组的取地址)
既然数组名和首元素的地址都是一样,都是地址,那么我们可以把它存到指针变量里去

int arr[10] = {
    
    1,2,3,4,5,6,7,8,9,0}; 
int *p = arr;//p存放的是数组首元素的地址

既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个就成为可能。

例如:

#include <stdio.h>
int main()
{
    
    
    int arr[] = {
    
     1,2,3,4,5,6,7,8,9,0 };
    int* p = arr; //指针存放数组首元素的地址
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
    
    
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p + i);
    }
    return 0;
}

运行结果:

所以 p+i 其实计算的是数组 arr 下标为i的地址。 那我们就可以直接通过指针来访问数组。

如下:

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

二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是 二级指针 。

对于二级指针的运算有:
  • *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa .
int b = 20;
*ppa = &b;//等价于 pa = &b;
  • **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

指针数组

指针数组是指针还是数组?

答案:是数组。是存放指针的数组。 数组我们已经知道整形数组,字符数组。

int arr1[5];
char arr2[6];

那指针数组是怎样的?

int* arr3[5];//是什么?

arr3是一个数组,有五个元素,每个元素是一个整形指针。

在这里插入图片描述

结束语:

​ 创作不易,跪求三连。感谢阅读,尽情指错。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_60308100/article/details/123458310