【C语言基础入门】5.C语言中数组与字符串

文章目录

一、初探程序中的数组

二、数组特性深入剖析

三、多维数组的概念与示例 

四、字符数组与字符串(上) 

五、字符数组与字符串(下) 

六、数组专题练习


一、初探程序中的数组

  • 数组的概念 

    • 数组是相同数据类型变量有序集合

      • 数组作为整体需要一个合法的命名(数组名)

      • 数组中的变量没有独立命名,只有在数组中的编号

      • 数组中的变量数量是固定不变的(数组大小固定)

  • 数组的定义

    • 语法:type Name [ size ];

    • 示例:

      int arrl[10];   //定义名为 arrl 的数组
                      //数组中一共有 10 个元素(变量)
                      //每个元素的类型为 int
      
      double farr[5]; //定义名为 farr 的数组
                      //数组中一共有 5 个元素(变量)
                      //每个元素的类型为 double

  • 数组元素的访问 
    • 通过数组名 [下标]  的方式访问数组元素
    • 数组是一个有序集合,每个元素有固定下标
    • 数组元素下标固定从0开始(可以使用变量作为下标)
    • 示例:
      int arrl[3];
      
      arrl[-1] = 0;   //ERROR
      
      arrl[0] = 1;
      arrl[1] = 2;
      arrl[2] = 3;
      
      arrl[3] = 4;    //ERROR

        

  •  注意事项
    • 只有整型数才能作为下标访问数组元素
    • 下标越界是一种非常严重的编程错误(工业级BUG来源)
    • 下标越界带来的严重后果可能不会立即表现

        下面来编写程序求出0到100之间任意一个数的累加和,并对程序进行改进。首先看最常规的:

#include <stdio.h>

int main()
{
    int n = 0;
    int i = 0;
    int flag = 0;
    int sum = 0;

    do
    {
        printf("Input: ");

        scanf("%d", &n);

        flag = ((0 <= n) && (n <= 100));

        if( flag )
        {
            sum = 0;

            for(i=0; i<=n; i++)
            {
                sum += i;
            }

            printf("Sum = %d\n", sum);
        }
    } while( flag );

    return 0;
}

        下面为输出结果:

         上面的程序每求一次和,就得重新通过 for 循环求一次,造成资源的浪费,有没有更好的方法呢?下面看第二种方法:

#include <stdio.h>

int main()
{
    int n = 0;
    int i = 0;
    int flag = 0;

    int sum[101];

    for(n=0; n<=100; n++)
    {
        sum[n] = 0;

        for(i=0; i<=n; i++)
        {
            sum[n] += i;
        }
    }

    do
    {
        printf("Input: ");

        scanf("%d", &n);

        flag = ((0 <= n) && (n <= 100));

        if( flag )
        {
            printf("Sum = %d\n", sum[n]);
        }
    } while( flag );

    return 0;
}

        下面输出结果:

         这个程序相对于第一个程序改进的地方就是把所求的和提前算好,存在数组中,所以输入数后,只用查询就行。但是这种方法在提前算好的时候也存在重复计算,所以也不是最好的方法。下面看第3种方法:

#include <stdio.h>

int main()
{
    int n = 0;
    int flag = 0;

    int sum[101];

    sum[0] = 0;

    for(n=1; n<=100; n++)
    {
        sum[n] = sum[n-1] + n;    // sum[1] = sum[0] + 1;
                                  // sum[2] = sum[1] + 2;
                                  // ......
    }

    do
    {
        printf("Input: ");

        scanf("%d", &n);

        flag = ((0 <= n) && (n <= 100));

        if( flag )
        {
            printf("Sum = %d\n", sum[n]);
        }
    } while( flag );

    return 0;
}

        下面为输出结果:

         第三种方法采用递推的方法求和,充分利用资源,是最佳的方法。

  • 小结
    • 数组是相同数据类型变量有序集合
    • 数组元素通过数组名 [下标] 的方式访问
    • 只有整型数才能作为下标访问数组元素
    • 下标越界是一种非常严重的编程错误

二、数组特性深入剖析

  • 数组的初始化
    • 语法:type Name [N]  = { v0, v1, ... , vN-1}
    • 意义:将数组中的元素分别初始化为  v0, v1, ... , vN-1
    • 示例:
      int main()
      {
          int arr[5] = {2, 4, 6, 8, 10};
          int i = 0;
      
          for(i = 0; i < 5; i++)
          {
              printf("arr[%d] = %d\n", i, arr[i]);
          }
      
          return 0;
      }
  •  数组初始化技巧
    • 自动确定数组大小
      • type Name [ ] = {v0, v1, ... , vn-1}
    • 将部分数组元素初始化为0
      • type Name [ N ] = {v0, v1, ... , vs}  注:S <  N,未指定初始值的元素默认为0
    • 将所有数组元素初始化为0
      • type Name [N] = {0};
  • 数组的内存分布
    • 数组在计算机底层是一片连续的内存,用于存储数组元素
    • 数组的大小字节数可以用 sizeof 获取(单位:字节)
  • 计算数组的大小
    • type Name [ ] =  {v0, v1, ..., vn-1};        //共 n 个元素
    • sizeof (Name) / sizeof ( Name[0] );      //计算结果为n
  • 注意事项
    • 数组名只能当作左值使用(可看作常量)
    • 只能使用整型常量对数组大小进行定义
    • 只能使用整型值对作为下标访问数组元素
    • 示例
      int size;
      int arr[size];    //错误,只能使用整型常量定义数组
      int a[5] = {0};    
      
      arr[1.5] = 1;    //错误:小数不能用来定义数组下标
      
      arr = a;    //错误 :常量不能做左值使用

        下面进行一段代码:从键盘输入10个整数,完成之后输入目标整数 x  进行查找

#include <stdio.h>

int main()
{
    int arr[10] = { 0 };
    int i = 0;
    int len = sizeof(arr)/sizeof(arr[0]);
    int x = 0;

    for(i=0; i<len; i++)
    {
        printf("Input NO.%d: ", i + 1);
        scanf("%d", &arr[i]);
    }

    while( 1 )
    {
        printf("Target Number: ");
        scanf("%d", &x);

        for(i=0; i<len; i++)
        {
            if( x == arr[i] )
            {
                break;
            }
        }

        if( i < len )
        {
            printf("Index: %d\n", i + 1);
        }
        else
        {
            printf("No Target Number!\n");
        }
    }

    return 0;
}

        下面为输出结果:

                 接下来再看一段代码:已知整型数组中的元素都是 0 - 9 之间的数,编写程序统计各个数字的个数

#include <stdio.h>

int main()
{
    int arr[] = { 9, 1, 2, 1, 1, 4, 5, 5, 5, 9, 9, 9 };
    int i = 0;
    int j = 0;
    int len = sizeof(arr)/sizeof(arr[0]);
    int n = 0;

    for(i=0; i<10; i++)
    {
        n = 0;

        for(j=0; j<len; j++)
        {
            if( i == arr[j] )
            {
                n++;
            }
        }

        printf("%d: %d\n", i, n);
    }

    return 0;
}

        下面为输出结果:

         上面的程序采用了两段 for 循环,造成资源的浪费,下面看一种好的方法:

#include <stdio.h>

int main()
{
    int arr[] = { 9, 1, 2, 1, 1, 4, 5, 5, 5, 9, 9, 9 };
    int i = 0;
    int j = 0;
    int len = sizeof(arr)/sizeof(arr[0]);
    int cnt[10] = {0};
    int num = 0;

    for(j=0; j<len; j++)
    {
        num = arr[j];
        cnt[num] += 1;
    }

    for(i=0; i<10; i++)
    {
        printf("%d. %d\n", i, cnt[i]);
    }

    return 0;
}

        下面为输出结果:

  • 小结
    • 数组在计算机底层是一片连续的内存
    • 数组名只能当作左值使用(可看作常量)
    • 只能使用整型常量对数组大小进行定义
    • 只能使用整型值对作为下标访问数组元素

三、多维数组的概念与示例 

  • 数组类型 
    • 数组类型由 元素类型数组大小 共同决定
    • 数组类型的具体表现形式为:type [ N ]
    • 示例:
      int a[10] = {0};    //类型:int[10]
      float b[5] = {0};    //类型:float[5]
      
      int aa[5] = 0;    //类型:int[5]
      float bb[8] = {0};    //类型:float[8]
      
      int c[10];    //类型:int[10]
      
      int i;
      
      for(i = 0; i < 10; i++)
      {
          c[i] = a[i];    //相同类型数组的“赋值操作”
      }
  • 小知识

    • 数组中的元素可以是变量,也可以是其它程序元素
    • 当数组中的元素是另一个数组时,就构成了多维数组
    • C语言中的二维数组定义: type Name[ N1 ][ N2 ];
    • 二维数组可看作数学中的矩阵

  •  注意
    • 二维数组的本质还是就是数组,即:数组中的每一个元素是数组!!
    • 示例:
      int a[3][4];    //定义一个数组,数组中有 3 个元素,每个元素的类型为 int[4]
      
      (a[1])[2] = 2;    //对第 1 个数组中的第 2 个变量赋值
      
      a[2][3] = 2;    对第 2 个数组中的第 3 个变量赋值
      
      a[3][0] = 0;    //越界
      a[0][4] = 0;    //越界
  • 二维数组的初始化
    • 方式一:int a[2][3]= { {1,2],{4,5] };
    • 方式二:int a[2][3] = { 1,2,3,4 };
    • 方式三:int a[2][3] = { 0 };
    • 方式四:int a[ ][3] = { {1,2,3},{4,5,6} };
    • 方式五: int a[ ][3] = { 1,2,3,4 };

        方式一示例:

        方式二示例:

  • 注意事项 
    • 二维数组能且仅能让编译器自动确定第一维的大小
    • 第二维大小必须显示给定,即:数组元素的类型必须正确合法
    • 第一维大小自动确定的方法:(初始值个数 除以 第二维大小) 向上取整
    • 示例:
      int a[][3] = {1, 2, 3, 4};
      int s1 = sizeof(a) / sizeof(a[0]);
      int i = 0;
      int j = 0;
      
      for(i = 0; i < s1 ; i++)
      {
          for(j = 0; j < 3; j++)
          {
              printf("a[%d][%d] = %d\n", i, j, a[i][j]);
          }
      }
      

        下面先看一段代码, 弄清数组占用内存和赋值问题:

#include <stdio.h>

int main()
{
    int a[10] = {0};            // int[10]
    int b[5] = {1, 2, 3, 4, 5}; // int[5]
    int i = 0;

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

    for(i=0; i<5; i++)
    {
        a[i] = b[i];
    }

    for(i=0; i<10; i++)
    {
        printf("a[%d] = %d\n", i, a[i]);
    }


    return 0;
}

        下面为输出结果:

         注意这里赋值只能将数组 b 中的元素赋值给 数组 a,如果反过来 数组b 会发生越界,后面5个值会变成垃圾值。

        再来看一个二维数组的内存占用空间的代码: 

#include <stdio.h>

int main()
{
    int a[][3] = { {1, 2, 3}, {4, 5} };
    int s1 = sizeof(a) / sizeof(a[0]);  // 2
    int i = 0;
    int j = 0;

    printf("s1 = %d\n", s1);
    printf("sizeof(a) = %d\n", sizeof(a));
    printf("sizeof(a[0]) = %d\n", sizeof(a[0])); // int[3]

    for(i=0; i<s1; i++)
    {
        for(j=0; j<3; j++)
        {
            printf("a[%d][%d] = %d\n", i, j, a[i][j]);  // 数组的数组可以看作矩阵
                                                        // 因此,可以使用 2 个下标访问矩阵中的值
        }
    }

    return 0;
}

        下面为输出结果:

        这里注意两个问题:

        第一个问题就是第一维数组大小的计算,有对应的公式

        第二个问题就是对二维数组的理解,数组中的每一个元素是数组,所以 a 数组有 2 个数组,每个数组的类型为 int[3] 。所以对 a[0] 取字节后看到占用 12 个字节,即3个元素,每个元素为int 类型,占用 4 个字节,总的就为 12字节

        下面看一下对数组转置的代码 :

#include <stdio.h>

int main()
{
    int a[3][3];
    int i = 0;
    int j = 0;

    for(i=0; i<3; i++)
    {
        for(j=0; j<3; j++)
        {
            printf("Input a[%d][%d]: ", i, j);
            scanf("%d", &a[i][j]);
        }
    }

    printf("Matrix:\n");

    for(i=0; i<3; i++)
    {
        for(j=0; j<3; j++)
        {
            printf("%d    ", a[i][j]);
        }

        printf("\n");
    }

    for(i=0; i<3; i++)
    {
        for(j=0; j<3; j++)
        {
            if( i < j )
            {
                int t = a[i][j];
                a[i][j] = a[j][i];
                a[j][i] = t;
            }
        }
    }

    printf("Matrix-T:\n");

    for(i=0; i<3; i++)
    {
        for(j=0; j<3; j++)
        {
            printf("%d    ", a[i][j]);
        }

        printf("\n");
    }

    return 0;
}

        下面为输出结果:

  •   多维数组
    • 三维数组定义:一个数组,每个元素为一个二维数组
      • 语法:type Name[ N1 ][ N2 ][ N3 ];
    • 四维数组定义:一个数组,每个元素为一个三维数组
      • 语法:type Name[ N1 ][ N2 ][ N3 ][ N4 ];
  • 小结
    • 数组类型由元素类型数组大小共同决定(type [ N ])
    • 数组中的元素可以是变量,也可以是其它程序元素
    • 当数组中的元素是另一个数组时,就构成了多维数组
    • 二维数组能且仅能让编译器自动确定第一维的大小

四、字符数组与字符串(上) 

  • 字符数组是特殊的整数有序集合 
    • 每个整数占用一个字节(-128 --127)
    • 可以用字符字面量对数组元素进行初始化或者赋值
    • 常用来存储可阅读的文本信息

        下面看一段代码:

#include <stdio.h>

int main()
{
    char a[] = { 97, 98, 99 };
    char b[] = { 'D', '.', 'T', '.' };
    int i = 0;

    for(i=0; i<sizeof(a); i++)
        printf("%c", a[i]);

    printf("\n");

    for(i=0; i<sizeof(b); i++)
        printf("%c", b[i]);

    printf("\n");

    return 0;
}

        下面为输出结果:

  • 注意事项
    • 没有专用的字符串类型(字符串:双引号括起来的有序字符集 [这里可以看成字符串常量])
    • 只能通过字符数组“模拟”字符串变量
    • 存在字符串字面量,但仅能作为常量使用
    • 示例:
      #define DT "D.T."
      printf("%s\n", DT);
      printf("%s\n", "Software");

  • 字符串中的 0 元素
    •  整数 0 即字符串中的 0 元素(char 是最小的整型)
    • 0 元素对应的字符为 '\0’ (转义字符)
    • '0' 与 '\0' 不同,表示一个非0值,对应的整数为48

        下面编写程序看一下吧:

#include<stdio.h>

int main()
{
    printf("%d\n",'\0');
    printf("%d\n", '0');

    return 0;
}

         下面为输出结果:

  •  “字符串变量” - 字符数组
    • C语言中通过字符数组定义字符串
    • 当字符数组中存在 0 元素时,可当作字符串使用
    • 字符数组中的 0 元素表示了一个字符串的结束
    • 字符数组中的元素,不一定是字符串中的元素
    • 可以使用字符串常量进行初始化
    • 字符串长度 小于 字符数组大小
    • 一个字符数组不一定是一个字符串
    • 而一个字符串一定是一个字符数组

        字符数组中的元素,不一定是字符串中的元素,这句话该怎么理解呢?下面看一段代码:

#include<stdio.h>

int main()
{
    char s[] = {'D','.','T','.','\0'};
    char ss[] = {'D','.','\0','T','.'};

    printf("%s\n",s);
    printf("%s\n",ss);

    return 0;
}

        下面为输出结果:

         可以看到,'\0'出现的位置不同,字符串打印出来的结果也会不一样,字符数组中的元素不一定全部打印出来。

        下面看一段代码,加深对字符数组的理解:

#include <stdio.h>

int main()
{
    char dt[] = "abcd";
    char name[] = { "efg" };
    int ds = sizeof(dt);
    int ns = sizeof(name);

    int i = 0;

    printf("dt = %s\n", dt);
    printf("name = %s\n", name);
    printf("dt size = %d\n", ds);
    printf("name size = %d\n", ns);

    printf("dt array value:\n");

    for(i=0; i<ds; i++)
        printf("%d ", dt[i]);

    printf("\n");

    i = 0;

    while( dt[i] != 0 )
        i++;

    printf("dt length = %d\n", i);

    name[0] = 'x';
    name[1] = 'y';
    name[2] = 'z';

    printf("name = %s\n", name);

    return 0;
}

        下面为输出结果:

         由上面可以看出,字符串长度 小于 字符数组大小,而且 name 的内容可以改变,这就对应了“字符串变量”。

  • 小结 
    • C语言中的字符数组可以存储文本信息
    • C语言中没有专用字符串类型
    • C语言中的 “字符串变量” 只能使用字符数组模拟
    • 当字符数组中存在0元素时,即可当做字符串使用

五、字符数组与字符串(下) 

  •  注意事项
    • C语言中没有专用的字符串类型,也没有针对字符串的专用运算操作!
  • 字符串工具包
    • C语言中提供了字符串相关的工具包,即: string.h
    • string.h中定义了字符串相关的实用工具,如:字符串连接
    • 字符串工具的本质就是与字符串相关的运算操作
    • strlen(s)→获取字符串的长度
    • strcpy(s1, s2)→将s2中的字符复制到s1,   s1 <-- s2
    • strcat(s1,s2)→将s2追加到s1后面, s1 <-- s1 + s2
    • strcmp(s1, s2)→比较s1和s2是否相等,相等时为0

        下面看一段代码:

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

int main()
{
    char s[10] = "abcd";
    int size = sizeof(s);

    int len = strlen(s);

    printf("size = %d\n", size);
    printf("len = %d\n", len);

    return 0;
}

         下面为输出结果:

         再来看一组关于字符串工具使用的代码:

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

int main()
{
    char s[10] = "abcd";
    char d[] = "efg";
    int len = strlen(s);
    char in[16] = {0};

    printf("s = %s\n", s);
    printf("len = %d\n", len);

    strcpy(s, d);
    strcat(s, d);

    len = strlen(s);

    printf("s = %s\n", s);
    printf("len = %d\n", len);

    printf("Input: ");
    scanf("%s", in);

    if( strcmp(s, in) == 0 )
    {
        printf("equal\n");
    }
    else
    {
        printf("non-equal\n");
    }

    return 0;
}

        下面为输出结果:

         这里注意一点就是字符串的输入,不需要使用 &

  • 字符串小知识
    • 字符串常量的本质是字符数组,大小为字符串长度+1
    • 使用字符串工具进行字符串赋值时:
      • 必须保证存储赋值结果的字符数组足够大(防止越界)
      • 必须保证参与赋值的字符串必须合法(字符数组存在 0 元素)
  • 小结 
    • C语言中的没有直接的字符串运算操作
    • string.h工具包间接提供了对字符串的操作
    • 通过 #include <string.h> 声明使用字符串工具包
    • 借助工具包可进行字符串赋值,连接,比较等操作

六、数组专题练习

        1.对一个数组进行倒序输出:

#include<stdio.h>

int main()
{
    int a[] = {2, 3, 4, 5, 6, 7, 8, 9};
    int len = sizeof(a) / (sizeof(*a));
    int i = 0;

    for(i = 0; i < len/2; i++)
    {
        int t = a[i];
        a[i] = a[len - i -1];
        a[len - i -1] = t;
    }

    for(i = 0; i < len; i++)
    {
        printf("%d ",a[i]);
    }

    return 0;
}

        下面为输出结果:

        2.房间里有50盏灯(编号为1-50),初始状态为打开。现在有10个学生(编号为2,4,6,...),每个学生进入房间后拨动灯的开关,规则为:当灯编号能够整除自己编号时才能拨动,完成后退出房间。问:当最后一个学生退出房间时,哪些灯依旧是打开状态?

#include <stdio.h>

#define LN  50
#define SN  10

int main()
{
    int light[LN] = {0};
    int student[SN] = {0};
    int i = 0;
    int j = 0;

    for(i=0; i<LN; i++)  light[i] = 1;  // 1 表示灯的打开状态, 0 表示灯的关闭状态

    for(i=0; i<SN; i++)  student[i] = (i + 1) * 2;  // 2, 4, 6, 8, ...

    for(i=0; i<SN; i++)
        for(j=0; j<LN; j++)
            if( (j + 1) % student[i] == 0 )
            {
                light[j] = !light[j];
            }

    for(i=0; i<LN; i++)
        if( light[i] )
            printf("%d ", i + 1);

    return 0;
}

         下面为输出结果:

         这里一个小技巧就是把灯开的状态设置为1,灯灭的状态设置成0,并存到数组中

        3.字符数组边界问题,主要就是注意赋值后不能越界

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

int main()
{
    int i = 0;
    int e = 0;
    char r[10] = ""; // char r[10] = {0};

    for(i=0; i<strlen("abcd")+1; i++)
    {
        e = "abcd"[i];

        printf("%d ", e);
    }

    printf("\n");

    strcat(r, "abcd");
    strcat(r, "efghi");

    printf("r = %s\n", r);


    return 0;
}

        下面为输出结果:

         

        4.字符串长度问题:设 char s[10] = "\n\\\r";则:s的长度为: 

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

int main()
{
    char s[10] = "\n\\\r";
    printf("the length is %d\n",strlen(s));

    return 0;
}

        下面为输出结果:

         \表示转义,\n,\\,\r均为转义字符

        

         5.字符串倒序输出问题:

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

int main()
{
    char s[] = "abcdefg";
    int len = strlen(s);
    int i = 0;
    int j = 0;

    for(i = 0, j = len-1; i < j; i++, j--)
    {
        char t = s[i];
        s[i] = s[j];
        s[j] = t;
    }

    printf("s = %s\n",s);
    return 0;
}

        下面为输出结果:

        6.加深对字节大小,字符串长度的理解

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

int main()
{
    char s[] = "abc\0de\0fg";

    printf("%d %d\n", sizeof(s), strlen(s));
    printf("%s\n",s);

    return 0;
}

        下面为输出结果:

         字符数组的大小为10,而字符串的大小为3,因为字符串结束的字符就是第一次出现 0 元素。

7.将字符数组中的中间多余0元素删除,合并所有其它字符,进而构成一个字符串。

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

int main()
{
    char s[] = "abc\0de\0fg";
    int size = sizeof(s);
    int i = 0;
    int j = 0;

    while( i < size )
    {
        if( s[i] == 0 )
        {
            for(j=i+1; j<size; j++)
            {
                s[j-1] = s[j];
            }

            size--;
        }
        else
        {
            i++;
        }
    }

    printf("s = %s\n", s);

    return 0;
}

         下面为输出结果:

         主要思想:当字符数组里面出现 0 元素,就把后面的元素向前移动一位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清风自在 流水潺潺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值