C/C++ 指针和数组

数组是一种用于存储多个相同类型元素,C语言中一种非常重要的数据结构。它是一种线性数据结构,可以按顺序访问和操作数组中的元素。

1. 数组存储原理

C 数组的存储原理可以通过以下几个方面来理解:

  1. 连续内存分配:C 数组的元素在内存中是连续存储的。这使得通过数组索引来访问元素非常高效,因为可以通过简单的指针算术运算来确定元素的内存地址;
  2. 内存布局:数组的内存布局通常是从低地址到高地址的顺序。即:第一个元素存储在数组的开始地址,最后一个元素存储在数组的结束地址;
  3. 元素大小:数组的元素类型确定了每个元素占据的内存空间大小。例如,如果数组元素类型为 int,则每个元素通常占据 4 个字节;
  4. 数组大小:声明数组需要指定数组的大小,这样系统才能为数组分配足够的内存空间;
  5. 数组没有边界检查机制。如果超出数组边界访问元素,可能会导致内存越界访问,这是一种未定义行为,可能会导致程序崩溃或产生意料之外的结果。

注意: 多维数组在内存中也是使用线性的连续内存存储元素。

2. 数组下标和指针

在C语言中,数组和指针之间有着紧密的关系。

  1. 数组名作为指针:在大多数情况下,当使用数组名时,它会被隐式地转换为指向数组第一个元素的指针。例如,对于数组 int arr[5];,可以将 arr 视为指向 arr[0] 的指针;
  2. 指针和偏移量:可以使用指针算术来在数组中移动,通过对指针进行加法或减法操作来指向数组中的不同位置。例如,arr + 2 将返回指向 arr[2] 的指针。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int main() {

    int arr[] = {10, 20, 30, 50, 50};
    // 打印 arr 存储的地址,数组首元素地址
    printf("arr 值: %ld,arr[0] 地址: %ld\n", arr, &arr[0]);
    // 下标和指针元素访问
    printf("arr[2] = %d,*(arr + 2) = %d\n", arr[2], *(arr + 2));
    

    return 0;
}

程序运行结果:

arr 值: 140732676908768,arr[0] 地址: 140732676908768
arr[2] = 30,*(arr + 2) = 30

注意: 数组名的指向是不允许修改的。

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


int main() {

    int arr[] = {10, 20, 30, 50, 50};
    // 下面代码报错
    // arr = NULL;

    // 可以将 arr 赋值给 p 指针,修改 p 指针指向
    int* p = arr;
    p += 1;
    *p = 200;

    printf("arr[1] = %d\n", arr[1]);


    return 0;
}

3. 数组指针类型

int arr[] = {10, 20, 30, 50, 50};

我们知道数组名 arr 表示指向数组首元素 arr[0] 的指针。那么对数组名取地址得到的结果是什么呢?

我们自然会想到,arr 是 int* 类型指针,对 arr 取地址得到结果自然是二级指针 int** 类型。那么,真的是这样的吗?

int* 类型的步长是 4 字节,int** 类型的步长也是 4 字节。我们可以打印是否是这样的:

#include <stdio.h>


int main() {

    int arr[] = {10, 20, 30, 50, 50};

    printf("%ld\n", &arr);
    printf("%ld\n", &arr + 1);

    return 0;
}

程序运行结果:

140732795418336
140732795418356

我们可以看到,步长是 20,整个数组的大小就是 20 字节。所以,我们可以看到,对数组名取地址并不是二级指针类型,而是指向整个数组的指针类型,即:数组指针类型。数组指针定义语法如下:

#include <stdio.h>


int main() {

    int arr[] = {10, 20, 30, 50, 50};

    // 1. 方式一
    int(*p1)[5] = &arr;
    printf("%ld %ld\n", (long)p1, (long)(p1+1));

    // 2. 方式二
    typedef int(ARRAY_TYPE)[5];
    ARRAY_TYPE* p2 = &arr;
    printf("%ld %ld\n", (long)p1, (long)(p2+1));

    // 3. 方式三
    typedef int(*ARRAY_POINTER)[5];
    ARRAY_POINTER p3 = &arr;
    printf("%ld %ld\n", (long)p1, (long)(p3+1));

    
    return 0;
}

程序运行结果:

140732691384032 140732691384052
140732691384032 140732691384052
140732691384032 140732691384052

4. 数组作为函数参数

在 C 语言中,数组可以作为函数参数传递给其他函数。当我们将数组作为函数参数传递时,实际上传递的是数组的指针。通过传址的方式避免在函数内部创建数组的副本,从而减少了内存开销和时间消耗。这对于处理大型数组或需要频繁操作数组的情况非常有用。要在函数中使用数组参数,我们需要指定数组的类型和大小

示例代码:

#include <stdio.h>

// 1. 一维数组作为函数参数
void test01(int arr[5], int len)
{
    for (unsigned int i = 0; i < len; ++i)
        printf("%d ", arr[i]);
    printf("\n");
}

void test02(int arr[], int len)
{
    for (unsigned int i = 0; i < len; ++i)
        printf("%d ", arr[i]);
    printf("\n");
}

void test03(int* arr, int len)
{
    for (unsigned int i = 0; i < len; ++i)
        printf("%d ", arr[i]);
    printf("\n");
}

// 2. 二维数组作为函数参数
void test04(int arr[3][3], int len_row, int len_col)
{
    for (size_t i = 0; i < len_row; ++i)
    {
        for (size_t j = 0; j < len_col; ++j)
            printf("%d ", arr[i][j]);
    }
    printf("\n");
}

void test05(int arr[][3], int len_row, int len_col)
{
    for (size_t i = 0; i < len_row; ++i)
    {
        for (size_t j = 0; j < len_col; ++j)
            printf("%d ", arr[i][j]);
    }
    printf("\n");
}

void test06(int(*arr)[3], int len_row, int len_col)
{
    for (size_t i = 0; i < len_row; ++i)
    {
        for (size_t j = 0; j < len_col; ++j)
            printf("%d ", arr[i][j]);
    }
    printf("\n");
}


int main() {

    // 1. 一维数组作为函数参数
    int arr1[] = {10, 20, 30, 50, 50};
    test01(arr1, sizeof(arr1) / sizeof(int));
    test02(arr1, sizeof(arr1) / sizeof(int));
    test03(arr1, sizeof(arr1) / sizeof(int));

    // 2. 二维数组作为函数参数
    int arr2[][3] = {{10, 20, 30}, {40, 50, 60}, {70, 80, 90}};
    test04(arr2, sizeof(arr2)/sizeof(arr2[0]), sizeof(arr2[0])/sizeof(arr2[0][0]));
    test05(arr2, sizeof(arr2)/sizeof(arr2[0]), sizeof(arr2[0])/sizeof(arr2[0][0]));
    test06(arr2, sizeof(arr2)/sizeof(arr2[0]), sizeof(arr2[0])/sizeof(arr2[0][0]));

    return 0;
}
未经允许不得转载:一亩三分地 » C/C++ 指针和数组
评论 (0)

6 + 8 =