关于程序员:掌握C语言指针轻松解锁代码高效性与灵活性中

92次阅读

共计 5224 个字符,预计需要花费 14 分钟才能阅读完成。

✨✨ 欢送大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C 语言学习
贝蒂的主页:Betty‘s blog

1. 引言

后面给大家介绍了一些指针的基本概念,明天就让咱们持续深刻指针的世界,和贝蒂一起战胜指针大魔王吧

2. 二级指针

指针变量也是变量,是变量就有地址,那咱们就把寄存 指针变量地址 的指针称为 二级指针。

可能了解起来有点绕,咱们能够通过上面示意图演示一下

代码如下:

    int a = 10;
    int* pa = &a;// 一级指针,寄存 a 的地址
    int** ppa = &a;// 二级指针,寄存指针变量 p 的地址
  • 不能间接把 &&a 赋值给 ppa 哦,因为 && 在 C 语言中是且的意思”

(1)对 ppa 解援用,找到 pa,也就是说 *ppa==pa

(2)对 pa 解援用,找到 a,也就是说 **ppa==a

    int* b = *ppa;// 找到 a 的地址
    int c = **ppa;// 找到 a 
  • 顺次内推咱们能够衍生出三级指针,四级指针。

3. 数组与指针的关系

3.1 数组名的了解

咱们在后面学习数组时就明确,数组名是首元素地址,然而解说的不够深刻,明天就让咱们深刻理解一下吧~

首先让咱们察看一下如下代码

#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    //&arr[0],arr,&arr 的区别
    printf("&arr[0] = %p\n", &arr[0]);// 首元素地址
    printf("arr = %p\n", arr);// 一维数组数组名
    printf("&arr = %p\n", &arr);// 对整个数组取地址
    return 0;
}

从后果来说 &arr[0],arr,&arr 到底有什么区别呢?

让咱们再看看上面这段代码

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

输入后果:

  1. &arr[0]与 arr+ 1 都是跳过 4 个字节,相当于跳过 1 个整型元素。
  2. &arr+ 1 跳过 40 个字节,相当于 10 个整型,也就是整个数组。

    总结:arr 与 &arr[0]都是首元素地址,指向数组第一个元素。&arr 以首元素地址示意,然而指向的是整个数组。

3.2 sizeof 与数组名

咱们晓得 sizeof 实际上是获取了数据在内存中所占用的 存储空间 ,单位是 字节

让咱们看看上面这段代码吧

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

输入后果:40

不晓得大家有没有纳闷? 如果数组名是首元素地址的话,咱们晓得在 32 位机器上大小为 4,在 64 位机器上大小为 8。那为什么是 40 呢?

其实数组名就是数组⾸元素 (第⼀个元素) 的地址,然而有 两个例外
• sizeof(数组名),sizeof 中独自放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节
• & 数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)

3.3 数组与指针等价关系

假如有一个一维数组和一个二维数组

int arr[5]={1,2,3,4,5};int arr[3][3]={{1,2,3},{4,5,6},{7,8,9}}

咱们要拜访它的每个元素,有哪些办法呢~

  1. 数组拜访
    int arr1[5] = {1,2,3,4,5};
    for (int i = 0; i < 5; i++)
    {printf("%d", arr1[i]);
    }
int arr2[3][3] = {{1,2,3},{4,5,6},{7,8,9} };
for (int i = 0; i < 3; i++)
{for (int j = 0; j < 3; j++)
    {printf("%d", arr2[i][j]);
    }
    printf("\n");
}
  1. 指针拜访
for (int i = 0; i < 5; i++)
{printf("%d", *(arr1+i));
}
for (int i = 0; i < 3; i++)
{for (int j = 0; j < 3; j++)
    {printf("%d", *(*(arr2 + i) + j));
    }
}

通过对下面代码的察看,咱们能够总结如下法则

  1. arr[i]与 *(arr+i)等价。
  2. arri 与 ((arr+i)+j) 等价。

3.4 指针数组

(1) 指针数组的概念

指针数组顾名思义就是寄存指针的数组,数组中每个元素都是指针,寄存的都是 地址

int*parr1[5];// 寄存五个整型指针变量
char*parr2[5];// 寄存五个字符指针变量
float*parr3[5];// 寄存五个浮点数指针变量

代码示例

    int arr1[] = { 1,2,3};
    int arr2[] = { 4,5,6};
    int arr3[] = { 7,8,9};
    // 将每个数组的首元素地址都存进去
    int* parr[3] = {arr1,arr2,arr3};

示意图:

(2) 指针数组的了解

int main()
{int arr1[] = {1,2,3};
    int arr2[] = { 4,5,6};
    int arr3[] = { 7,8,9};
    int* parr[3] = {arr1,arr2,arr3};
    printf("%p\n", parr);// 打印指针数组首元素地址,也就是打印寄存 arr1 空间的地址
    printf("%p\n", parr[0]);//arr1 数组首元素地址
    printf("%p\n", *parr);//arr1 首元素地址
    printf("%d\n", **parr);// 相当于对 arr1 首元素地址解援用,指的的是 1
    printf("%d\n", *parr[0]);// 也相当于对 arr1 首元素地址解援用,为 1
    printf("%d\n", *parr[1]);// 相当于对 arr2 首元素地址解援用,为 4
    return 0;
}

输入后果:

012FFE30
012FFE6C
012FFE6C
1
1
4

(3) 模仿二维数组

通过上述咱们对 指针数组 的了解,咱们能够间接来模拟出二维数组。

代码如下:

int main()
{int arr1[] = {1,2,3};
    int arr2[] = { 4,5,6};
    int arr3[] = { 7,8,9};
    // 将每个数组的首元素地址都存进去
    int* parr[3] = {arr1,arr2,arr3};
    int i = 0;
    int j = 0;
    for (i = 0; i < 3; i++)
    {for (j = 0; j < 3; j++)
        {printf("%d", parr[i][j]);
        }
        printf("\n");// 换行)
    }
    return 0;
}
  • 模仿的二维数组并不是真正的二维数组,因为二维数组在内存中是间断存储的,而模仿进去的数组内存存储并不间断

3.5 数组指针

(1) 数组指针的概念

同理,指针数组的实质是一个数组;那么数组指针的实质就是个指针,指向一个数组的指针。

int(*parr1)[5];// 指向一个有五个元素的整型数组
char(*parr2)[5];// 指向一个有五个元素的字符数组
float(*parr3)[5];// 指向一个有五个元素的浮点数数组

(2) 数组指针的了解

int main()
{int arr[5] = {1,2,3,4,5};
    int(*parr)[5] = &arr;
    // 对数组名取地址代表整个数组的地址
    printf("%p\n", parr);// 整个数组的地址个别用数组首元素地址示意
    printf("%p\n", parr[0]);// 相当于 *(parr+0)==arr,首元素地址
    printf("%p\n", *parr);// 首元素地址
    printf("%d\n", **parr);// 相当于对首元素地址解援用,指的的是 1
    printf("%d\n", *parr[0]);// 也相当于对首元素地址解援用,为 1
    printf("%d\n", *parr[1]);// 等价于 *(*(parr+1)),parr+ 1 跳过一个数组大小的地址,越界拜访
    return 0;
}

输入后果:

012FF6F0
012FF6F0
012FF6F0
1
1
-858993460(越界拜访,随机数)

示意图:

3.6 指针数组与数组指针的区别

可能有许多小伙伴区别不分明指针数组与数组指针,然而如果写成指针的数组,数组的指针,可能更好了解。接下来让咱们具体分析一下吧?

首先咱们要分明一个优先级程序:()>[]>*

  1. 在 int*parr[]中,parr 先与 [] 联合(数组),而 parr 后面申明的变量类型是 int*。所以这是一个数组,数组中每个元素的类型是 int* 的指针,这一类咱们统称为 指针数组
  2. 在 int(*parr)[]中,parr 先与 联合(指针),而后除开(parr)是一个 int []的数组类型。所以这是一个指针,这个指针指向的是一个数组,这一类咱们称为 数组指针。

3.7 字符串

咱们先看一下上面这段代码

char arr1[]="im betty";
char arr2[]={'a','b','c','\0'};

这是一种常见的字符串的示意模式,以 ’\0’ 作为其结尾标记。

然而还有另外一种示意模式,代码如下

    //const 能够省略
    const char* p1 = "im betty";
    const char* p2 = "abc";

咱们晓得 const 润饰在 * 前,不能扭转指针变量所指向的值,所以这个字符串是不能扭转的,这种字符串咱们称为 常量字符串

  • 实质上是将字符串中的首元素地址寄存进指针变量。

晓得这些之后,让咱们来看一道题吧

输入什么?

#include <stdio.h>
int main()
{char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    const char* str3 = "hello bit.";
    const char* str4 = "hello bit.";
    if (str1 == str2)
        printf("str1 and str2 are same\n");
    else
        printf("str1 and str2 are not same\n");
    if (str3 == str4)
        printf("str3 and str4 are same\n");
    else
        printf("str3 and str4 are not same\n");
    return 0;
}

输入后果:

为什么会呈现这种后果呢,那是因为这⾥ str3 和 str4 指向的是⼀个同⼀个常量字符串。C/C++ 会把常量字符串存储到独自的 ⼀个内存区域 (常量区),当⼏个指针指向同⼀个字符串的时候,他们理论会指向同⼀块内存。然而⽤雷同的常量字符串去初始化不同的数组的时候就会开拓出 不同的内存块,每个数组地址就会不同。所以 str1 和 str2 不同,str3 和 str4 雷同。

3.8 数组传参

(1) 一维数组传参

咱们在之前学习函数时候就讲过一维数组传参,让咱们来温习一下吧。

代码如下

void print(int arr[])// 写成数组的模式
{
    int i = 0;
    for (i = 0; i < 10; i++)
    {printf("%d", arr[i]);
    }
}
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    print(arr);// 将数组传递给 print 函数
    return 0;
}

咱们传参是传的数组名,咱们晓得 数组名是首元素的地址,既然是地址,天然就能用指针来承受,所以就有了另外一种写法。

void print(int*p)// 用指针来接管
{
    int i = 0;
    for (i = 0; i < 10; i++)
    {printf("%d",*(p+i));
    }
}

(2) 二维数组的传参

先让咱们看看个别二维数组是如何传参的吧

void print(int arr[][3])// 行能够省略,列不能够
{
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        int j = 0;
        for (j = 0; j < 3; j++)
        {printf("%d", arr[i][j]);
        }
        printf("\n");
    }
}
int main()
{int arr[3][3] = {{1, 2, 3}, {4,5,6}, {7,8,9}};
    print(arr);// 将数组传递给 print 函数
    return 0;
}

那么指针接管如何写呢,还是 int* p 吗,咱们晓得二维数组能够看成把每一行当做一个元素的一维数组,数组名首元素地址天然是第一行元素的地址,所以要用数组指针来接管哦~

代码如下:

void print(int(*p)[3])// 明确元素个数
{
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        int j = 0;
        for (j = 0; j < 3; j++)
        {printf("%d",*(*(p+i)+j));
        }
        printf("\n");
    }
}

正文完
 0