zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【蒸滴C】C语言指针入门很难?看这一篇就够了

2023-04-18 15:49:16 时间

目录

一、前言

二、指针是什么

小结:

三、指针变量是什么

小结:

四、指针在32位机器和64位机器中的差别

32位机器:

64位机器:

小结:

五、指针和指针类型

(1)指针的意义

(2)指针的解引用

六、野指针

(1)野指针的概念

(2)野指针的成因

(3)如何规避野指针

(4)个人对野指针的一点小分享

七、指针的运算

(1)指针加减整数

(2)指针减指针

(3)指针的关系运算

八、指针和数组

(1)指针和数组的表达

小结:

(2)二级指针

(3)指针数组


一、前言

刚入门C语言的小白或多或少被指针这块内容折磨过,我自己在学校听老师讲课也是一脸模糊的样子。

不过我那时候不断提醒自己,这块内容这块内容对于初学者来说是会有点难度,我现在刚开始接触,自然会很难,但重要的是要坚持下去,并愿意从差开始

而经过了一个寒假的学习,我对指针这块内容有了比较深的理解,看完这篇博客保证你对指针的理解会更深一步!

二、指针是什么

在学习指针之前,我们先要正确认识指针

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

小结:

指针就是地址,口语中说的指针通常指的是指针变量

三、指针变量是什么

通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个
变量就是指针变量

#include <stdio.h>
int main() 
{
    int a = 10;
    //在内存中开辟一块空间

    int *p = &a;
    //这里我们对变量a,取出它的地址,可以使用&操作符。

    //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
    中,p就是一个之指针变量。

    return 0;

 }

小结:

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

四、指针在32位机器和64位机器中的差别

32位机器:

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电
平(低电压)就是(1或者0);
那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111   11111111   11111111  11111111

这里就有2的32次方个地址。
每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB ==
2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB)

在32位机器中有4G的空间进行编址

64位机器:

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
...
11111111   11111111   11111111  11111111  11111111   11111111   11111111  11111111

通过计算我们可以得出,在64位机器中:

2^64Byte == (2^32Byte)^2 == (4G)^2 == 16G

也就是说,在64位机器中有16G的空间进行编址

综上我们可以得出:

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

说到这里小蔡要提醒各位,关于在不同位数的机器中指针的大小非常重要!请各位铭记于心

小结:

指针变量是用来存放地址的,地址是唯一标示一个内存单元的
指针的大小在32位平台是4个字节,在64位平台是8个字节

五、指针和指针类型

我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?
准确的说:有的。

我在这里例举一些常见的指针类型:

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

这里可以看到,指针的定义方式是:  类型  +  *  
其实:
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。

(1)指针的意义

关于指针的用法,主要是“ 指针加减整数 ”的相关知识

首先我们来讲一讲“” 指针加整数 "代表着什么,来看看这个代码:

#include <stdio.h>
//演示实例
int main()
{
    int n = 10;
    char *pc = (char*)&n;
    int *pi = &n;
    printf("%p
", &n);
    printf("%p
", pc);
    printf("%p
", pc+1);
    printf("%p
", pi);
    printf("%p
", pi+1);
    return  0;
}

运行结果及差别如下:

 可以看出:char* 类型进行+1操作时,前后地址相差为 1 ;

而 int* 类型进行+1操作后,前后地址相差为4

由此我们可以得出:

指针的类型决定了指针向前(+)或者向后(-)走一步有多大(距离) 

(2)指针的解引用

先看下面代码

//演示实例
#include <stdio.h>
int main()
{
    int n = 0x11223344;
    char *pc = (char *)&n;
    int *pi = &n;
    *pc = 0;  //重点在调试的过程中观察内存的变化。
    *pi = 0;  //重点在调试的过程中观察内存的变化。
return 0;
}

我们来看看调试过程中 *pc 和 *pi 有什么不同之处:

 

 由此我们可以得出下面的结论:

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节

六、野指针

在刚开始接触编程的时候,我就有对 野指针 所耳闻,下面来具体讲讲野指针

(1)野指针的概念

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

(2)野指针的成因

1. 指针未初始化

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

2. 指针越界访问

#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;
}

3. 指针指向的空间释放

  这一点涉及较为后面的内容,对于C语言小白可以等到后续学习到动态内存的时候在进行学习

(3)如何规避野指针

1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放,及时置NULL

(定义指针可以这样子写,来防止出现野指针:int * p =NULL)
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性

(4)个人对野指针的一点小分享

我们可以把野指针看成一只野 D o g,不把他拴住,他就会乱啃乱咬,但我们只要把他拴住,它就不能为非作歹了。

拴住就是类似这样子:int * p =NULL

七、指针的运算

指针的运算这部分主要讲解这几点:

指针加减 整数
指针减指针
指针的关系运算

(1)指针加减整数

这一块内容前面已有讲解,这里不再赘述。

总结起来就是:指针加减整数就是表示指针向前(-)或向后(+)的意思

(2)指针减指针

这里可以用一个和strlen函数有相同功能的函数来理解 指针减指针 的意义:

int my_strlen(char *s)
{
   char *p = s;
   while(*p != '' )
       p++;
   return p-s;
}

这串代码的意思就是,传一个数组首元素地址给 my_strlen 函数,

然后创建一个 char* 类型的变量p,先把首元素地址赋给 *p,

然后开始循环,只要*p不等于,p就会进行+1操作。

这样下去知道遇见,也代表着数组的结束,这时候就返回 p-s (指针减指针)的值。

而这里的指针减指针的值,代表的意思就是p到s之间的元素个数,所以可以用来实现求字符串长度的功能

(3)指针的关系运算

这部分内容有涉及到的请况比较少,大家只要记住下面这一点就可以了:

在C语言的标准中规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

八、指针和数组

指针和数组的一起用的场景非常之多,下面来讲一讲指针和数组之间的各种关系

(1)指针和数组的表达

我们先看一个例子:

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

运行结果:

 可见数组名和数组首元素的地址是一样的。


小结:

数组名表示的是数组首元素的地址。(2种情况除外,一种是&arr,取出的是整个数组的地址;另一种是sizeof(arr),计算的是整个数组的大小)


那么这样写代码是可行的:

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]);
  for(i=0; i<sz; i++)
 {
    printf("&arr[%d] = %p  <====> p+%d = %p
", 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;
}

(2)二级指针

用大白话来讲,二级指针就是存放指针变量的地址。

下面请看我花的思路图:

 对于二级指针的运算有:

● *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa .

 **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .

(3)指针数组

这是这篇博客的最后一个内容啦,主要讲解指针数组的相关知识

先问大家一个问题,指针数组是指针还是数组?


答案:是数组。是存放指针的数组。


数组我们已经知道有整形数组,字符数组。

而指针数组是怎么样的呢?

请看下面的图: 

 

 总的来说:指针数组是数组——装着指针的数组,这点内容只要理解它是什么,并且会用就好啦

以上就是本篇博客的全部内容啦,如有不足之处,还请各位指出,期待能和各位一起进步!