数组和指针

1.1 数组和指针关系

数组名是操作当前数组对应的名称,同时数组名是【指针常量】,存储当前数组在内存中的空间首地址。

1.2 案例代码

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    /*
    数组名是一个指针常量
    */
    printf("arr = %p\n", arr);

    /*
    数组的内存空间首地址,同时也是数组中下标为 0 的元素内存
    空间首地址。
    */
    printf("&arr[0] = %p\n", &arr[0]);

    /*
    当前操作得到的结果为 1,也就是数组中下标为 0 的元素数据内容
    可以认为 int arr[10] 等价理解为 int *arr

    *arr 根据当前 arr 指针存储的地址,访问对应的内存空间,读取
    4 个字节内存,将 4 个字节中所有的二进制数据内容当做 int 类型
    数据处理
    */
    printf("*arr = %d\n", *arr);

    /*
    假设数组内存空间首地址以为 0xACB0
    请问数组中下标为 6 的元素空间首地址多少
        0xACB0 + 6 * 4 ==> 0xACB0 + 24
                       ==> 0xACB0 + 0x18
                       ==> 0xACC8
    */
    printf("&arr[6] = %p\n", &arr[6]);

    return 0;
}

1.3 数组下标和地址的关系

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    printf("arr = %p\n", arr);
    printf("&arr[5] = %p\n", &arr[5]);

    /*
    【重点】
        指针加上一个整数,可以认为是当前地址数据 + n * sizeof(目标元素数据类型)
        当前 arr 是一个存储数据类型为 int 类型数组,对应指针数据情况为
        int *arr 目标数据类型为 int 类型

        假设 arr ==> 0xACB0
        arr + 4 ==> 0xACB0 + 4 * sizeof(int)
                ==> 0xACB0 + 4 * 4
                ==> 0xACB0 + 0x10
                ==> 0xACC0
    计算得到数组中下标为 4 的元素内存空间首地址。
    */
    printf("arr + 4 = %p\n", arr + 4);
    /*
    获取当前数组中下标为 4 的元素数据
    */
    printf("*(arr + 4) = %d\n", *(arr + 4));

    return 0;
}

/*
*arr + 1 = ?
    *arr 取值数组中下标为 0 的元素 1
    1 + 1 ==> 2

arr + 2 = ?
    0xACB0 + 2 * 4 ==> 0xACB8

*(arr + 2) = ?
    3

*arr + *(arr + 5) = ?
    1 + 6 ==> 7
*/

1.4 地址减法

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    /*
    地址减法得到的是两个地址对应元素的个数差。
    【要求】必须是同源地址。例如: 来自同一个数组,同一个结构.
    */
    printf("&arr[5] - &arr[1] = %d\n", &arr[5] - &arr[1]);

    return 0;
}

1.5 数组作为函数参数的深度剖析

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

void show_value(int arr[], int capacity);

int main(int argc, char const *argv[])
{
    int arr[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    show_value(arr, 10);

    return 0;
}

void show_value(int arr[], int capacity)
{
    /*
    注意
        1. 数组作为函数的参数,提供给函数内部的数据实际上是
        数组在内存空间的首地址
        2. 在仅具备地址的情况下,无法操作后续的数组元素,因为
        无法知晓操作的内存边界,要求必须提供 capacity 数组容
        量限制操作范围
        3. 数组 capacity 需要进行必要的合法性判断
            3.1 必须大于 0
            3.2 同时小于
                32位 int 最大值
                64位 long 最大值
    */
    printf("__INT32_MAX__ : %d\n", __INT32_MAX__);
    // printf("arr[10] = %d\n", arr[10]);
}
// 【墙裂推荐】
for (int i = 0; i < capacity; i++)
{
    arr[i] = 2 * i + 1;
}

// 【依托答辩】
for (int i = 0; i < capacity; i++)
{
    *(arr + i) = 2 * i + 1;
}

2. 指针作为函数参数【重点】

#include <stdio.h>

void my_swap1(int n1, int n2);

void my_swap2(int *p1, int *p2);

int main(int argc, char const *argv[])
{
    int n1 = 100;
    int n2 = 20;

    my_swap1(n1, n2);

    /*
    n1 = 100, n2 = 20; 数据未发生改变
    因为当前函数所需参数为 int 类型,在 main 函数中
    n1 和 n2 提供给函数的只是变量存储的数据内容,并
    没有将 main 函数中的变量提供给函数使用。
    【数值传递】
    */
    printf("n1 = %d, n2 = %d\n", n1, n2);

    /*
    void my_swap2(int *p1, int *p2);
    当前函数所需参数是两个指向内存空间为 int 类型的指针,
    要求用户提供的是满足类型要求的地址,实际参数是&n1 和
    &n2。相当于将 n1 和 n2 在内存中的空间首地址提供给函
    数使用,函数内部可以根据地址直接访问对应的内存空间操
    作数据
    【地址传递】
    */
    my_swap2(&n1, &n2);
    printf("n1 = %d, n2 = %d\n", n1, n2);

    return 0;
}

void my_swap1(int n1, int n2)
{
    n1 += n2;
    n2 = n1 - n2;
    n1 -= n2;
}

void my_swap2(int *p1, int *p2)
{
    *p1 += *p2;
    *p2 = *p1 - *p2;
    *p1 -= *p2;
}