C语言_数组基础

数组

存储

核心结论是:无论数组的维度是多少,计算机内存的物理结构都是一维的(线性排列的)。因此,二维数组在内存中实际上是被“压扁”成一维序列来存储的

行优先存储

这是C、C++、Python等绝大多数编程语言默认采用的方式。

方式:从第0行开始,一行接一行地依次存储。

示例
假设我们有一个 3行 x 4列 的二维数组 arr[3][4]

text

|  a00  |  a01  |  a02  |  a03  |  // 第0行
|  a10  |  a11  |  a12  |  a13  |  // 第1行
|  a20  |  a21  |  a22  |  a23  |  // 第2行

在内存中的排列顺序为:

a00 -> a01 -> a02 -> a03 -> a10 -> a11 -> a12 -> a13 -> a20 -> a21 -> a22 -> a23

地址计算
对于一个 M行 x N列 的数组 arr[M][N],要访问元素 arr[i][j],其在一维内存序列中的位置(假设基地址为 base,每个元素占 L 字节)可以通过以下公式计算:

地址(arr[i][j]) = base + (i * N + j) * L
  • i * N:跳过前面 i 整行的元素。

  • + j:在第 i 行中,再跳过 j 个元素。

  • * L:乘以每个元素所占的字节数(如 int 通常是4字节)。

高效遍历

为什么这很重要?

  1. 性能优化(缓存友好性)

    • 行优先存储的语言(如C++)中,按行遍历(外层循环为行,内层循环为列)的效率远高于按列遍历。因为按行访问时,相邻的数据在内存中也是相邻的,CPU缓存可以高效地加载一整块数据(缓存行)。
    • 反之,如果按列遍历,每次访问都可能需要从内存的不同位置加载数据,导致大量的缓存未命中,性能会急剧下降。

    C++示例(行优先语言)

    cpp

    // 高效:按行遍历
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < N; j++) {
            arr[i][j] = ...; // 内存访问是连续的
        }
    }
    
    // 低效:按列遍历
    for (int j = 0; j < N; j++) {
        for (int i = 0; i < M; i++) {
            arr[i][j] = ...; // 每次访问都跳过了N个元素,不连续
        }
    }
    

初始化

一、一维数组初始化

1. 基本初始化方式

c

// 方式1:声明时直接初始化
int arr1[5] = {1, 2, 3, 4, 5};

// 方式2:不指定大小,编译器自动计算
int arr2[] = {1, 2, 3, 4, 5};  // 自动确定为5个元素

// 方式3:部分初始化,其余自动补0
int arr3[5] = {1, 2};  // 实际为:{1, 2, 0, 0, 0}

// 方式4:全部初始化为0
int arr4[5] = {0};  // {0, 0, 0, 0, 0}
2. 字符数组初始化

c

// 字符串初始化
char str1[] = "hello";  // 自动包含'\0',共6个字符
char str2[6] = "hello"; // 同上
char str3[] = {'h', 'e', 'l', 'l', 'o', '\0'}; // 等价写法

// 字符初始化
char chars[5] = {'a', 'b', 'c'}; // {'a', 'b', 'c', 0, 0}

二、多维数组初始化

1. 二维数组

c

// 完全初始化
int matrix1[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

// 部分初始化
int matrix2[2][3] = {
    {1, 2},     // {1, 2, 0}
    {4}         // {4, 0, 0}
};

// 连续初始化
int matrix3[2][3] = {1, 2, 3, 4, 5, 6}; // 按行优先存储

// 省略第一维大小
int matrix4[][3] = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};  // 自动推断为3行
2. 三维数组

c

int cube[2][2][3] = {
    {
        {1, 2, 3},
        {4, 5, 6}
    },
    {
        {7, 8, 9},
        {10, 11, 12}
    }
};

下面考虑这个赋值过程:

维度解析

cube[2][2][3] 分解:
- 第一维:2个"面"
- 第二维:每个面有2个"行"  
- 第三维:每行有3个"列"

内存布局与二维数组类似

面0:
  行0: [1, 2, 3]
  行1: [4, 5, 6]
面1:
  行0: [7, 8, 9]  
  行1: [10, 11, 12]

三、特殊初始化技巧

1. 使用memset函数

c

#include <string.h>

int arr[10];
memset(arr, 0, sizeof(arr));  // 全部初始化为0
memset(arr, -1, sizeof(arr)); // 全部初始化为-1
2. 指定初始化器(C99标准)

c

// 指定特定位置初始化
int arr[10] = {[0] = 1, [5] = 2, [9] = 3};
// 结果:{1, 0, 0, 0, 0, 2, 0, 0, 0, 3}

// 范围初始化(GCC扩展)
int arr2[10] = {[0 ... 4] = 1, [5 ... 9] = 2};
// 结果:{1, 1, 1, 1, 1, 2, 2, 2, 2, 2}

四、完整示例程序

c

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

int main() {
    // 一维数组示例
    int scores[5] = {95, 87, 92, 78, 85};
    printf("一维数组:");
    for(int i = 0; i < 5; i++) {
        printf("%d ", scores[i]);
    }
    printf("\n");
    
    // 二维数组示例
    int classroom[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };
    
    printf("二维数组:\n");
    for(int i = 0; i < 2; i++) {
        for(int j = 0; j < 3; j++) {
            printf("%d ", classroom[i][j]);
        }
        printf("\n");
    }
    
    // 字符数组示例
    char greeting[] = "Hello, World!";
    printf("字符串:%s\n", greeting);
    
    return 0;
}

五、注意事项

  1. 数组越界:初始化值不能超过数组大小
  2. 自动补零:未初始化的部分会自动设为0
  3. 字符串结束符:字符数组用字符串初始化时会自动添加’\0’
  4. const数组:如果数组内容不变,建议加const修饰符

操作

输入

1. getchar() 用法

函数原型int getchar(void)
功能:从标准输入读取一个字符
返回值:读取的字符(转为int类型),遇到错误或文件结束返回EOF

#include<stdio.h>

int main() {
    char ch;
    
    // 基本用法
    ch = getchar();
    
    // 循环读取直到换行
    while((ch = getchar()) != '\n') {
        // 处理每个字符
        printf("%c", ch);
    }
    
    return 0;
}

2. fgets() 用法

函数原型char *fgets(char *str, int n, FILE *stream)
功能:从指定流读取一行字符串
参数

  • str:存储读取字符串的缓冲区
  • n:最大读取字符数(包括结尾的’\0’)
  • stream:输入流(stdin表示标准输入)
#include<stdio.h>

int main() {
    char str[100];
    
    // 读取一行,最多99个字符
    fgets(str, 100, stdin);
    
    printf("输入的内容:%s", str);
    return 0;
}

要点

不可越界访问

边界记得检查

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值