【C】数组

数组

数组的定义

  • 语法:类型 数组名[元素个数] (方括号内只能是常量或者常量表达式)

    int a[6];

    char b[24];

    double c[3];

  • 上面几个类型分别占用内存的字节数为:

    int a[6] ==> 4 * 6 = 24

    char b[24] ==> 1 * 24 = 24

    double c[3] ==> 8 * 3 = 24

数组不能动态定义

C语言不允许在程序运行过程中修改数组空间大小

访问数组中的元素

  • 语法: 数组名[下标]

    a[0]; //访问a数组中的第个元素

    b[1]; //访问b数组中的第个元素

    c[5]l //访问c数组中的第个元素

  • 注意

    • int a[5]; //创建一个具有五个元素的数组
    • a[0]; //访问第一个元素,第一个元素下标为0,不是1
    • a[5]; //报错, 第五个元素下标为a[4]

循环跟数组的关系

  • 实现一个执行10次的循环,我们通常是这么写的:

    for (i = 0; i < 10; i++)
    {
        ...
    }
    
  • 而不是这么写:

    for (i = 1; i <= 10; i++)
    {
        ...
    } 
    
  • 这是因为我们常常需要使用循环来访问数组:

    int a[10];
    for (i = 0; i < 10; i++)
    {
        a[i] = i;
    }
    
  • 举个例子,我们尝试用数组存放班里5位同学的数学成绩,并计算出平均数。

    #include<stdio.h>
    #define NUM 5  //常量宏定义,可以在此直接修改存放的学生个数
    int main()
    {
        int s[NUM]; //学生数组
        int i, sum = 0;
        for (i = 0; i < NUM; i++) //循环次数为学生个数
        {
            printf("请输入第%i位同学的成绩:",i+1);  
            scanf("%d",&s[i]); //将输入的成绩存放到对应的数组下标
            sum += s[i];
        }
        printf("成绩录入完毕,该次考试的平均分是:%.2f\n", (double)sum / NUM ); //计算结果并转换为浮点型
        return 0;
    }
    

数组的初始化

在定义数组的同时给它赋值,称为数组的初始化

方式为:

  • 将数组中所有元素初始化为0,可以这么写:

    • int a[10] = {0}; //事实上这里只是将第一个元素赋值为0
  • 如果是赋予不同的值,那么用逗号隔开即可:

    • int a[10] = {1,2,3,4,5,6,7,8,9,0};
  • 也可以只给一部分元素赋值,未被赋值的元素自动初始化为0:

    • int a[10] = {1,2,3,4,5,6};

      // 表示为前边6个元素赋值,后边4个元素系统自动初始化为0

  • 有时还可以偷懒,可以只给出各个元素的值,而不指定数组的长度(因为编译器会根据值的个数自动判断数组的长度):

    • int a[] = {1,2,3,4,5,6,7,8,9,0};
  • C99增加了一种新特性:指定初始化的元素。这样就可以只对数组中的某些指定元素进行初始化,而未被赋值的元素自动初始化为0:

    • int a[10] = {[3]=3,[5]=5,[8]=8};
  • 使用sizeof可以计算数组占用内存的大小

    int a[10]={0};
    printf("%d\n",sizeof(a));
    

    结果

    40  // int有4个字节,4*10=40
    

字符数组

  • 字符串常量: "Hello","ABC"

  • 字符数组:

    char str[10];
    str[0] = 'H';
    str[1] = 'e';
    str[2] = 'l';
    str[3] = 'l';
    str[4] = 'o';
    str[5] = '\0';
    ...
    

字符串处理函数

请参考:C语言标准函数库分类

获取字符串长度

  • strlen函数,用于获取字符串的长度: (注意:获取的是长度,不是尺寸)
#include<stdio.h>
#include<string.h>  //头文件
int main()
{
    char str[] = "Hello!";
    printf("sizeof str = %d\n",sizeof(str));
    printf("strlen str = %u\n",strlen(str));
    return 0;
}

运行结果

sizeof str = 7  \\包含'\0'
strlen str = 6 \\字符的个数,不包含'\0'

拷贝字符串

strcpy和strncpy函数

  1. strcpy函数用于拷贝字符串,包含最后的结束符 '\0'
  • 语法:strcpy(目标字符串,源字符串)

    注意

    • 为了避免溢出,必须确保用于存放的数组长度足以容纳待拷贝的字符串(注意:长度需要包含结束符 '\0')。
    • 源字符串和目标数组的位置不应该重叠。
#include<stdio.h>
#include<string.h>  //头文件
int main()
{
    char str1[] = "Original String"; //长度15
    char str2[] = "New String";   //长度10
    char str3[100];
    
    strcpy(str1, str2); 
    //将str2中的字符(包括'\0')拷贝到str1
    strcpy(str3, "Copy Successful");
    printf("str1: %s\n",str1);
    printf("str2: %s\n",str2);
    printf("str3: %s\n",str3);
    return 0;
}

运行结果

str1: New String //拷贝完后,str1的长度改变了,为10.但sizeof没有变化,仍为16
str2: New String
str3: Copy Successful    

若源字符串长度大于目标字符串:

char str1[] = "Original String"; //长度15
char str2[] = "New String";  //长度10
strcpy(str2, str1); 
printf("str1: %s\n",str1);
printf("str2: %s\n",str2);

运行结果

str1: ring
str2: Original String

拷贝时,源字符串过长,目标字符串溢出,导致拷贝后源字符串发生变化。

因此在程序实现拷贝时,应该限制源字符串的长度,确保目标字符串在执行完拷贝后不会发生溢出

  1. strncpy函数

    • strcpy函数一样,strncpy(dest, src, n) 函数将拷贝源字符串的 n 个字符到目标数组中。如果源字符串的长度小于 n,那么就用 '\0' 填充额外的空间。如果源字符串的长度大于或等于 n,那么只有 n 个字符被拷贝到目标数组中(注意:这样的话将不会以结束符 '\0' 结尾)。
    • 提示:为了使该函数更“安全”,建议使用dest[sizeof(dest) - 1] = '\0'; 语句确保目标字符串是以 '\0' 结尾
    • 源字符串和目标数组的位置不应该重叠。
    #include<stdio.h>
    #include<string.h>  //头文件
    int main()
    {
        char str1[] = "To be or not to be"; 
        char str2[40]; 
        strncpy(str2, str1,5); //拷贝str1中5个字符到str2中
        str2[5] = '\0'; //追加结束字符
        printf("str2: %s\n",str2);
        return 0;
    }
    

    运行结果

    str2: To be
    

    注意:如果源字符串的长度大于或等于 n,在拷贝完后需要对目标字符串追加结束符,否则结果会乱码

连接字符串

  • 连接字符串: strcat和strncat

    连接字符串用于将一个字符串连接到另一个字符串的后边,也叫字符串的拼接。(注意:目标数组里必须包含一个字符,可以是空字符)

  1. strcat -- 连接字符串

    • strcat函数用于连接两个字符串。

    • 将源字符串拷贝并连接到目标数组存放的字符串后边,此过程将覆盖第一个参数的结束符 '\0'。

    • 两个参数的位置不应该重叠

    #include<stdio.h>
    #include<string.h>  //头文件
    int main()
    {
        char str1[] = "I love"; 
        char str2[] = "this world!"; 
        strcat(str1," "); //在str1末尾连接一个空格
        strcat(str1, str2); //将str2连接到str1末尾
        printf("str1: %s\n",str1);
        return 0;
    }
    

    运行结果

    str1: I love this world!
    
  2. strncat -- 连接字符串(受限)

    • strncat函数用于拷贝源字符串中的 n 个字符到目标数组的字符串后边,并在末尾添加结束符 '\0'。
    • 如果源字符串的长度小于 n,那么不会像strncpy函数那样使用 '\0' 进行填充(但结束符 '\0' 还是有的)。
    • 另外,目标数组中的原有的字符串并不算在 n 中

比较字符串

  • 比较字符串: strcmpstrncmp

    比较字符串用于比较两个字符串是否完全一致。如果一致,则返回0;如果不一致,那么根据情况,返回大于0或小于0的值。(从第一个字符开始,依次对比两个字符串每一个字符的ASCII码。如果第一个字符串ASCII码小于第二个字符串对于的字符,则返回一个小于0的值,反之则相反。)

  1. strcmp -- 比较字符串

    strcmp函数用于比较两个字符串。

    该函数从第一个字符开始,依次比较每个字符的 ASCII 码大小,直到发现两个字符不相等或抵达结束符('\0')为止

    #include<stdio.h>
    #include<string.h>  //头文件
    int main()
    {
        char str1[] = "Hello World!"; 
        char str2[] = "Hello World"; 
        if (!strcmp(str1,str2)) //比较两个字符串返回值是否等于0
        {
            printf("两个字符串完全一致!\n");
        }
        else
        {
            printf("两个字符串存在差异!\n");
        }
        return 0;
    }
    

    运行结果

    两个字符串完全一致!
    
  2. strncmp -- 比较字符串(受限)

    strncmp函数用于比较两个字符串的前 n 个字符。

    该函数从第一个字符开始,依次比较每个字符的 ASCII 码大小,发现两个字符不相等或抵达结束符('\0')为止,或者前 n 个字符完全一样,也会停止比较。

二维数组

如果说一维数组是一条线,那么二维数组就可以说是一个面。二维数组也称为矩阵

科目\学号 1 2 3 4 5 ...
语文 80 92 85 86 99
数学 78 65 89 70 99
英语 67 78 76 89 99
综合 88 68 98 90 99

上表是一个记录了各个同学各个科目的成绩。对于上表,如果使用一维数组,则需分别定义4个数组

int chinese[50];
int math[50];
int english[50];
int comprehensive[50];

但如果使用二维数组,我们只需要定义一个数组就可以解决了,如:

int score[4][50];
score[0][0] = 80;
score[1][0] = 78;
score[2][0] = 67;
score[3][0] = 88;
...

二维数组的定义

  • 类型 数组名[常量表达式][常量表达式]

    int a[6][6]; // 6*6,6行6列

    char b[4][5]; // 4*5, 4行5列

    double c[6][3]; // 6*3,6行3列

  • 在C语言中,内存的存放是线性的。所以不管是一维数组还是二维、三维数组,它们的存放都是线性的。

    以二维数组int b[4][5]为例

    首先它在内存中定义一个一维数组出来,进而再在一维数组中每一个元素中再定义一个一维数组出来。也可以说是一维数组的嵌套。

二维数组的访问

与一维数组相似,二维数组同样是以下标来访问

  • **数组名[下标][下标] **

    a[0][0]; // 访问a数组中第1行第1列的元素

    b[1][3]; // 访问b数组中第2行第4列的元素

    c[3][3]; // 访问c数组中第4行第4列的元素

  • 同样需要注意下标的取值范围,以防止数组的越界访问

    • 比如int a[3][4],其“行下标”的取值范围是0 ~ 2,“列下标”的取值范围是0 ~ 3,超出任何一个下标的访问都是越界访问。

二维数组的初始化

  • 由于二维数组在内存中是线性存放的,因此可以将所有的数据写在一个花括号内:

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

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

    运行结果

    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}};

    或者

    int a[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    
  • 二维数组也可以仅对部分元素赋初值:

    int a[3][4] = {{1}, {5}, {9}};

    这样会把前三行中每行第一个元素进行赋值,剩余未被赋值元素会用0代替

    int main()
    {
        int a[3][4] = {{1}, {5}, {9}};
        int i, j;
        for (i=0;i<3;i++)
        {
            for (j=0;j<4;j++)
            {
                printf("%d  ", a[i][j]);
            }
            printf("\n");
        }
        return 0;
    }
    

    运行结果

    1   0   0   0   
    5   0   0   0
    9   0   0   0
    
  • 如果希望整个二维数组初始化为0,那么直接在大括号里写一个0即可:

    int a[3][4] = {0};

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

    运行结果:

    0  0  0  0  
    0  0  0  0
    0  0  0  0
    
  • C99同样增加了一种新特性:指定初始化的元素。这样就可以只对数组中某些指定元素进行初始化赋值,而未被赋值的元素自动初始化为0:

    int a[3][4] = {[0][0] = 1, [1][1] = 2, [2][2] = 3};

    int main()
    {
        int a[3][4] = {[0][0] = 1, [1][1] = 2, [2][2] = 3};
        int i, j;
        for (i=0;i<3;i++)
        {
            for (j=0;j<4;j++)
            {
                printf("%d  ", a[i][j]);
            }
            printf("\n");
        }
        return 0;
    }
    

    运行结果:

    1  0  0  0  
    0  2  0  0
    0  0  3  0
    
  • 二维数组的初始化也能偷懒,让编译器根据元素的数量计算数组的长度。但只有第1维的元素个数可以不写,其他维度必须写上:

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

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

    运行结果

    1  2  3  4  
    5  6  7  8
    9  10  11  12
    
posted @   芝麻凛  阅读(0)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示