数组(Array)

数组是一种数据结构,可以存储相同类型元素的固定大小的顺序集合。

特点

  • 数组所有元素的类型相同
  • 数组的大小是固定的
  • 数组的内存地址是连续的
  • 一个单独的数组名代表第一个元素的地址
  • 数组越界将返回默认值

声明数组

Type Array_Name[Array_Size]; // 一维数组
  • Array_Size 必须是大于 0 的整数常量
int a[10];

初始化数组

// 一个一个地初始化
a[0] = 1;
a[1] = 2;
...
a[9] = 10;
// 初始化声明({} 之间元素的数量不能大于 [] 中的值)
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 同上
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

所有数组都将 0 作为其第一个元素的索引。数组的最后一个索引将是数组的总大小减 1 。

+---+     +---+     +---+     +---+     +---+     +---+     +---+     +---+     +---+     +----+
| 1 |     | 2 |     | 3 |     | 4 |     | 5 |     | 6 |     | 7 |     | 8 |     | 9 |     | 10 |
+---+     +---+     +---+     +---+     +---+     +---+     +---+     +---+     +---+     +----+
  0         1         2         3         4         5         6         7         8         9

数组大小和长度

  • 数组大小
// 数组大小、容量
size_t size = sizeof(a); // 单位:字节

// 数组长度(元素个数)
size_t length = sizeof(a) / sizeof(a[0]);

使用宏定义:

#define LENGTHOF(arr) (sizeof(arr) / sizeof(arr[0])) // 返回值类型为 size_t

访问数组元素

数组元素可以通过索引(indexing)数组名来访问,例如 a[0]。如果索引值大于或等于数组大小,将返回数组类型所对应的默认值,而不是数组越界异常。

int b = a[9];

int c = a[10]; // 返回 0,而不是数组越界异常
#include <stdio.h>
#define LEN(arr) (sizeof(arr) / sizeof(arr[0])) // 返回值类型为 size_t

void printarray(int a[])
{
    size_t i;
    for (i = 0; i < LEN(a); i++)
        printf("a[%zu] = %d\n", i, a[i]);
}

int main()
{
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    printarray(a);

    return 0;
}

数组作为函数参数

传递一维数组的数组名作为函数参数(本质上传递的是数组第一个元素的地址)。由于数组中的内存是连续的,您仍然可以使用指针算法(如(b + 1))指向第二个元素或等效 b[1]。可以用以下三种方式声明形式参数:

void func(int* arr);  // 指向数组第一个元素的指针作为形参
void func(int arr[10]); // 有大小的数组作为形参(实参数组的长度可以大于形参数组的长度)
void func(int arr[]); // 无大小的数组作为新参
#include <stdio.h>

/**
 * 打印一维数组
 * 必须传递数组长度,不能通过 sizeof(a)/sizeof(a[0]) 来获取,
 * 因为形参 arr 获取到的是数组第一个元素的地址
 */
void print_array(int *arr, size_t size)
{
    size_t i;
    for (i = 0; i < size; i++)
        printf("%zu\n", arr[i]);
}

从函数返回数组

C 语言不允许将整个数组作为参数返回给函数,但可以通过指定不带索引的数组名称来返回指向数组的指针。

C 语言不主张将局部变量的地址返回到函数外部,因此必须将局部变量定义为静态(static)变量。

int *func() {}
```

## 指向数组的指针

```c
double *p;
double balance[10];

p = balance;

数组越界

C 语言并没有规定数组访问越界时编译器该如何处理,导致程序可以自由访问所有内存空间,并且每次越界访问得到的值可能还不一样。

int main(int argc, char* argv[]) {
    int arr[10] = {3, 4, 5};
    printf("%d\n", arr[100]);
    return 0;
}

内存地址

一维数组元素 arr[i] 的内存地址:

// base_address 即 arr、arr[0] 的地址
arr[i]_address = base_address + i * type_size

二维数组元素 arr[i][j](0 <= i < m,0 <= j < n) 的内存地址:

// base_address 即 arr、arr[0][0] 的地址
a[i][j]_address = base_address + (i * n + j) * type_size
/*
 * 从左到右、从上到下连续
 *
 * 0x7ffc1eb977e0   0x7ffc1eb977e4  0x7ffc1eb977e8
 * 0x7ffc1eb977ec   0x7ffc1eb977f0  0x7ffc1eb977f4
 */
int main() {
    int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%p\t", &arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

动态数组

在内存的 “堆” 中开辟地址空间,并且是在运行时开辟。

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

// 10 11 12 13 14 15 16 17 18 19
int main() {
    size_t n = 10;
    int* arr = (int *) malloc(sizeof(int) * n);
    for (int i = 0; i < n; i++) { // 数组越界无异常
        arr[i] = i + 10;
        printf("%d ", arr[i]);
    }
    return 0;
}

malloc(size_in_bytes) 的返回值为 void *,表示类型未知的指针,因此需要强制类型转换以确定将要存储的地址的类型。

二维数组(2D Arrays)

二维数组实质上是以一维数组为元素而组成的新数组。同理,三维数组是以二维数组为元素而组成的数组,以此类推。

  • 声明
type name[x][y];
  • 数据结构

二维数组 a[x][y] 可以看作是具有 x 行 y 列的表。数组 a[3][4] 的数据结构如下:

       Column0   Column1   Column2   Column3
     +---------+---------+---------+---------+
Row0 | a[0][0] | a[0][1] | a[0][2] | a[0][3] |
Row1 | a[1][0] | a[1][1] | a[1][2] | a[1][3] |
Row2 | a[2][0] | a[2][1] | a[2][2] | a[2][3] |
     +---------+---------+---------+---------+
  • 初始化
// 每一行使用嵌套的大括号 {}
// 若缺省元素会自动在相应行补默认值
int a[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12},
};

// 省略行的嵌套大括号 {}
// 若缺省元素会自动在末尾补默认值
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
  • 访问数组元素
#include <stdio.h>
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))

int main()
{
    // Row2 缺省两个元素
    int a[3][4] = {
        {1, 2, 3, 4},
        {5, 6},
        {9, 10, 11, 12},
    };

    size_t i, j;
    for (i = 0; i < LEN(a); i++)
        for (j = 0; j < LEN(a[i]); j++)
            printf("a[%zu][%zu] = %d\n", i, j, a[i][j]);

    return 0;
}

参考

results matching ""

    No results matching ""