【C语言】数组

目录

1.数组的概念

2.一维数组

2.1.一维数组的创建和初始化

2.1.1.数组声明

2.1.2.初始化方式

2.1.2.1.完全初始化

2.1.2.2.部分初始化

 2.1.2.3.特殊初始化技巧

2.2.一维数组的使用

2.2.1. 数组下标

2.2.2.数组元素打印

2.2.3.数组输入 

2.3.一维数组在内存中的存储

3.sizeof计算数组元素个数

4.二维数组

4.1.二维数组的创建

4.2.二维数组的初始化

4.2.1.不完全初始化

4.2.2.完全初始化

4.2.3. 按照行初始化

4.2.4.初始化时省略行,但是不能省略列 

4.3.二维数组的使用

4.3.1.二维数组的下标

4.3.2.二维数组的输入和输出

4.4.二维数组在内存中的存储

5.C99变长数组(VLA)详解

5.1. 基本定义

5.2. 核心特性

5.3. 关键限制

5.4. 多维数组应用

5.5. 特殊运算

5.6. 工程实践建议

5.7. 标准演进

6.数组练习

练习1:多个字符从两端移动,向中间汇聚

练习2:⼆分查找


1.数组的概念

数组是一组相同类型元素的集合

  • 数组中存放的是1个或者多个数据,但是数组元素个数不能为0。
  • 数组中存放的多个数据,类型是相同的。
数组分为⼀维数组和多维数组,多维数组⼀般⽐较多⻅的是⼆维数组。

2.一维数组

2.1.一维数组的创建和初始化

2.1.1.数组声明

格式:数据类型 数组名[元素个数]

int scores[5];       // 声明包含5个整数的数组
float temperatures[7]; // 声明包含7个浮点数的数组
 

此时数组元素值不确定(包含内存残留数据)

2.1.2.初始化方式

2.1.2.1.完全初始化
int primes[5] = {2, 3, 5, 7, 11};
char vowels[] = {'a', 'e', 'i', 'o', 'u'}; // 自动推导长度为5
 
2.1.2.2.部分初始化
int arr[5] = {10,20}; // 剩余元素自动补0
// 结果为:[10,20,0,0,0]
 
 2.1.2.3.特殊初始化技巧
  • 字符串数组
char greeting[] = "Hello"; // 等价于 {'H','e','l','l','o','\0'}
 

  • 指定位置初始化(C99+)
int arr[6] = {[3] = 9, [5] = 20}; 
// 结果为:[0,0,0,9,0,20]
 

2.2.一维数组的使用

2.2.1. 数组下标

数组元素通过下标访问,下标从0开始计算:

int scores[5] = {85, 90, 78, 92, 88};
// 索引:  0    1    2    3    4
printf("%d", scores[2]);  // 输出第三个元素78
 

 注意:数组越界访问(如scores[5])会导致未定义行为

2.2.2.数组元素打印

使用循环结构遍历数组:

#include <stdio.h>

int main(){
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    for(i = 0; i < 10; i++){
        printf("%d ", arr[i]);
    }
    return 0;
}

输出:

 

2.2.3.数组输入 

#include <stdio.h>

int main(){
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    for(i = 0; i < 10; i++){
        scanf("%d", &arr[i]);
    }
    for(i = 0; i < 10; i++){
        printf("%d ", arr[i]);
    }
    return 0;
}

 

2.3.一维数组在内存中的存储

依次打印数组元素的地址:

#include <stdio.h>

int main(){
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    for(i = 0; i < 10; i++){
        printf("&arr[%d] = %p\n ", i, &arr[i]);
    }
    return 0;
}

 我们看看输出结果:

 

从输出的结果我们分析,数组随着下标的增⻓,地址是由⼩到⼤变化的,并且我们发现每两个相邻的元素之间相差4(因为⼀个整型是4个字节)。所以我们得出结论:数组在内存中是连续存放的

 

3.sizeof计算数组元素个数

sizeof 中C语⾔是⼀个关键字,是可以计算类型或者变量⼤⼩的,其实 sizeof 也可以计算数组的
⼤⼩。
⽐如:
#include <stido.h>

int main(){
    int arr[10] = {0};
    printf("%d\n", sizeof(arr));
    return 0;
}
输出的结果是40,计算的是数组所占内存空间的总⼤⼩,单位是字节
⼜知道数组中所有元素的类型都是相同的,那只要计算出⼀个元素所占字节的个数,数组的元素
个数就能算出来。
#include <stido.h>

int main(){
    int arr[10] = {0};
    printf("%d\n", sizeof(arr[0]));//计算⼀个元素的⼤⼩,单位是字节
    return 0;
}
接下来就能计算出数组的元素个数:
#include <stido.h>

int main(){
    int arr[10] = {0};
    int sz = sizeof(arr)/sizeof(arr[0]);
    printf("%d\n", sz);
    return 0;
}
输出结果是:10,表⽰数组有10个元素。

4.二维数组

4.1.二维数组的创建

前⾯学习的数组被称为⼀维数组,数组的元素都是内置类型的,如果我们把⼀维数组作为数组的元
,这时候就是⼆维数组⼆维数组作为数组元素的数组被称为三维数组,⼆维数组以上的数组统称为多维数组

4.2.二维数组的初始化

4.2.1.不完全初始化

int arr1[3][5] = {1, 2};
int arr2[3][5] = {0};

 

4.2.2.完全初始化

int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};

 

4.2.3. 按照行初始化

intarr4[3][5]={{1,2},{3,4},{5,6}};

4.2.4.初始化时省略行,但是不能省略列 

intarr5[][5]={1,2,3};
intarr6[][5]={1,2,3,4,5,6,7};
intarr7[][5]={{1,2},{3,4},{5,6}};

 

4.3.二维数组的使用

4.3.1.二维数组的下标

访问格式:数组名[行下标][列下标]

如对于:

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

 

第2⾏,第4列,就能快速定位出7。
#include <stdio.h>

int main(){
    int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
    printf("%d\n", arr[2][4]);
    return 0;
}

输出:

4.3.2.二维数组的输入和输出

  • 方法1:逐元素输入
for(int i=0; i<3; i++){
    for(int j=0; j<4; j++){
        scanf("%d", &matrix[i][j]);
    }
}
 

  • ​​​​​​方法2:按行输入
for(int i=0; i<3; i++){
    scanf("%d %d %d %d", &matrix[i][0], &matrix[i][1], 
                         &matrix[i][2], &matrix[i][3]);
}
 
  • 输出方法
for(int i=0; i<3; i++){
    for(int j=0; j<4; j++){
        printf("%4d", matrix[i][j]); // 每个元素占4字符宽度
    }
    printf("\n"); // 换行输出下一行
}
 

4.4.二维数组在内存中的存储

打印出数组所有元素的地址:

#include <stdio.h>

int main(){
    int arr[3][5] = { 0 };
    int i = 0;
    int j = 0;
    
    for (i = 0; i < 3; i++){
        for (j = 0; j < 5; j++){
            printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
        }
    }
    return 0;
}

输出结果:

 

 

从输出的结果来看,每⼀⾏内部的每个元素都是相邻的,地址之间相差4个字节,跨⾏位置处的两个元素(如:arr[0][4]和arr[1][0])之间也是差4个字节,所以⼆维数组中的每个元素都是连续存放的。
如下图所⽰:

 了解清楚⼆维数组在内存中的布局,有利于我们后期使⽤指针来访问数组的学习。

5.C99变长数组(VLA)详解

5.1. 基本定义

变长数组(Variable-Length Array)允许在程序运行时确定数组长度,其声明语法为:

int n = 10;
int arr[n];  // 合法VLA声明

5.2. 核心特性

  • 动态尺寸:数组维度可以是任意整型表达式
    void func(int size) {
        double buffer[size*2];  // 使用参数计算数组大小
    }
    

  • 存储分配:分配在栈内存空间
  • 生存周期:与自动变量相同,离开作用域自动释放

5.3. 关键限制

  • 禁止静态存储:
    static int vla[10];  // 错误:静态存储期变量不能是VLA
    

  • 禁止初始化列表:
    int n = 5;
    int arr[n] = {0};  // 错误:VLA不能初始化
    

  • 函数参数限制:
    void foo(int arr[][n]);  // 错误:n必须先于数组声明
    

5.4. 多维数组应用

支持多维动态数组:

int rows = 3, cols = 4;
int matrix[rows][cols];  // 二维VLA

// 动态初始化示例
for(int i=0; i<rows; i++){
    for(int j=0; j<cols; j++){
        matrix[i][j] = i*j;
    }
}

5.5. 特殊运算

  • sizeof运算符:运行时计算实际大小
    int n = 5;
    printf("%zu", sizeof(int[n]));  // 输出20(假设int为4字节)
    

5.6. 工程实践建议

  • 适用场景:临时缓冲区、算法中的中间存储
  • 风险控制:避免超大尺寸导致栈溢出
  • 替代方案:超过1KB建议使用动态内存分配
    int* safe_array = malloc(n * sizeof(int));
    

5.7. 标准演进

  • C99:强制支持
  • C11:改为可选特性(__STDC_NO_VLA__宏标识)
  • C17:维持可选特性

典型应用示例:矩阵运算

void matrix_mult(int m, int n, int p, 
                int A[m][n], int B[n][p], int C[m][p]) {
    for(int i=0; i<m; i++){
        for(int j=0; j<p; j++){
            C[i][j] = 0;
            for(int k=0; k<n; k++){
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
}

6.数组练习

  • 练习1:多个字符从两端移动,向中间汇聚

#include <stdio.h>
int main(){
    char arr1[] = "welcome to bit...";
    char arr2[] = "#################";
    int left = 0;
    int right = strlen(arr1)-1;
    printf("%s\n", arr2);
    
    while(left<=right){
    Sleep(1000);
    arr2[left] = arr1[left];
    arr2[right] = arr1[right];
    left++;
    right--;
    printf("%s\n", arr2);
    }
    return 0;
}

  • 练习2:⼆分查找

给定⼀个升序的整型数组,在这个数组中查找到指定的值n,找到了就打印n的下标,找不到就
打印:"找不到"。
#include <stdio.h>
int main(){
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    int left = 0;
    int right = sizeof(arr)/sizeof(arr[0])-1;
    int key = 7;//要找的数字
    int mid = 0;//记录中间元素的下标
    int find = 0;

    while(left <= right){
    mid = (left+right)/2;

        if(arr[mid] > key){
            right = mid-1;
        }
        else if(arr[mid] < key){
            left = mid+1;
        }
        else{
            find = 1;
            break;
        }
    }

    if(1 == find)
    printf("找到了,下标是%d\n", mid);
    else
    printf("找不到\n");
    
    return 0;
}
求中间元素的下标,使⽤ mid = (left + right) / 2 ,如果left和right⽐较⼤的时候可能存在
问题,可以使⽤下⾯的⽅式:
mid = left + (right - left) / 2;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值