C语言程序设计-Chap.5


数组

1. 数组概述

数组概念

数组 是一种能够包含多个 同类型数据 的复合结构。

可以理解为一个连续的存储空间,被切分为了多个存储单元

数组定义

数组的定义需要三个要素:

  1. 数组元素类型
  2. 数组名称
  3. 数组的大小
int a[10];
double m[20];

注:

  1. 数组方括号内的只能是 字面量枚举常量,不能是变量(已赋值的也不行) ;
    如:
    int a[10];
    
    #define N 10
    int m[N*5];
    
    enum{X=20};
    int n[X];
    
    //以上是合法的数组定义
    
    //以下是不合法的数组定义
    
    int i=10;
    double x[i];
    
    //注:该种定义方法在C99标准中被允许
  2. 在常规编程中,数组的大小不宜太大(博主最高使用过长度为10000的数组),过大的数组可能会引起报错。
    如真的需要使用一个很大的存储空间,请参考后一篇文章: 指针

数组使用

基本操作:元素访问

int a[10];
a[0]=9;
......

值得一提的是,变量不能出现在数组定义的方括号中,但可以出现在使用数组时的方括号中。如:

常用操作:批量赋值

int a[10], i;
int a[0]=0;
for(i=1;i<9;i++){
    a[i]=a[0];
}

这种用法是可行的。

注:
利用 for 循环对数组内的元素进行统一赋值是十分常见的用法,但这一过程中常常会出现 越界访问 问题。

数组初始化

三种方法:

  1. 直接初始化
    int a[3]={1, 2, 3};
    注:直接初始化不能使用除了常量之外的任何表达式。
  2. 部分初始化
    int a[3]={1};
    这种初始化方法会将未提到的元素自动赋值0。
  3. 直接写数组
    int a[]={1, 2, 3, 12, 23, 32};
    这种初始化方法会在编译过程中自动给出数组的大小。

数组作为函数参数

问题在于,C语言的参数传递是值传递,因此无法简单的在函数中使用数组。

解决方案:将数组在内存中存储的地址传递给函数。

int function(int array[]){
    ......
}

int main(){
    int a[3]={1, 2, 3};
    function(a);
    ......
    return 0;
}

数组的基本使用至此以叙述完毕,接下来会阐述一些常用的数组及其用法。

2. 一维数值型数组的应用

排序问题

排序 是十分常见的可以用数组解决的问题之一,在本篇博文中,仅仅展示出一个基本的方法—— 冒泡排序

冒泡排序 的基本思路是运用嵌套for循环对数组进行操作,每一轮循环都将最大的数挪到数组后方相应位置。

这里给出参考代码:

void bubblesort(int array[], int n){
   //array[]是需要排序的数组,n是数组的大小
   int i1, i2;
   for(i1=0;i1<n-1;i1++){
      for(i2=0;i2<n-i1-1;i2++){
         if(array[i2]>=array[i2+1]){
            int m;  //m是个临时变量
            m=array[i2];
            array[i2]=array[i2+1];
            array[i2+1]=m;
         }
         //如果前一个元素大于(等于)后一个元素,则将两个元素交换
      }
   }
   //排序完成,如需输出:
   for(i1=0;i1<n;i1++){
      printf("%d ", array[i1]);
   }
}

需要标注的是,关于排序还有不少改进方法,如 选择排序插入排序 等,在后面的进阶训练篇章中会有提及。

查找问题

查找 即在数组中寻找某个元素

线性查找

线性查找的思路是最简单的,即从头到尾全过一遍,找到相应元素便返回。

int search(int array[], int n, int goal){
   //array[]为要进行查找的数组,n为数组大小,goal为要查找的元素
   int i, count=0;
   for(i=0;i<n;i++){
      if(array[i]==goal){
         printf("%d ", i);
         count++;
      }
   }
   return count;
   //数组中每个等于goal的元素下标均输出,并返回出现次数(count)
}

很明显,这种查找方法在数组已经排好序的情况下是效率较低的。这样就衍生出了另一种查找方法—— 折半查找

同样的,在后续进阶文章中会提及。

插入,删除问题

插入,删除的操作方法相比于查找就多了一步,因此此处不再详细阐述,给出思路。

插入:
找到插入位置->将后面的元素全部往后挪一位->将要插入的元素放进去

删除:
找到要删除的元素->将后面的元素往前挪一位

3. 字符数组 / 字符串

字符串常量

顾名思义, 字符串常量 即由一连串字符组成的常量。
在C语言中规定: \0 是字符串结尾的标志。
即:如果定义了一个内容为”C programming”的字符串,那它在存储空间内的存储方式为:”C programming\0”

字符数组

字符数组 可以用来存储字符串。

定义

与其他数组定义方式相同:

char array[20];

注:定义字符数组时需要考虑”\0”占据的一个大小。

初始化

初始化字符数组有多种方法。

  1. 逐个字符赋值:

    char task[20]={'C', 'p', 'r', 'o', 'g', 'r', 'a', 'm'};
    //其中未声明初始值的元素会自动赋值为 '\0'
  2. 直接定义字符串常量:

    char str1[20]="C programming";
    char str2[]="That's a string";
    //未定义初始大小的字符串常量会自动设定其大小为链长+1(用于存储 \0)

    需要注意的是,这种方法只能用于初始化,不能用于赋值。

输入/输出

C语言中的scanf(visual studio中为scanf_s)以及printf函数为字符串设定了单独的输入输出格式:

char str1[20];
scanf_s("%s", str1);
printf("%s", str1);

但很遗憾,由于scanf的限制,这样的输入不适用于列中带有空格的字符串。因为scanf遇到空格会中止。

因此,以下部分引入新的标准库<string.h>

<string.h>概述

在C语言中,涉及到字符串,这个标准库几乎是逃不过去的,因为其中涉及到了许多很好用的操作字符串的函数。

字符串输入函数 gets

char str[20];
gets(str);

该函数遇到空格不会终止,当遇到回车时会中止输入,并自动在最后多赋值一个 \0

字符串输出函数 puts

char str[]="C programming";
puts(str);

该函数会输出括号中的字符串,并在最后自动加上回车。

字符串长度函数 strlen

char str[]="I'm editing a blog";
printf("%d", strlen(str));

该函数会自动计算括号内字符串的实际长度。(不含 \0)

字符串复制函数 strcpy

char str1[20], str2[20];
strcpy(str1, "blog");
strcpy(str2, str1);

该函数可以将后面字符串的内容复制到前面的字符串中。
需要注意的是前面的字符串需要够大来容纳后面的字符串。

字符串比较函数 strcmp

char str1[]="Beijing";
char str2[]="Beijing";
char str3[]="Shanghai";

printf("%d", strcmp(str1, str2));  //输出0
printf("%d", strcmp(str1, str3));  //输出一个非0的值

该函数会比较前后两字符串的值,相同则输出0,不相同则输出非0;

字符串连接函数 strcat

char str[30]="Programming", str2[10]="c language";
strcat(str, str2);  //将str2的内容接到str后面

该函数可以将后面字符串的内容接到前面的字符串后面。

一点补充

字符串的操作方法十分繁杂,包括最简单的gets,puts函数的各种变体也有很多,在这里不过多赘述,在后面的文章中还会相应提及。

4. 二维数组

定义

int a[5][5];

上述方式表示定义一个5*5的二维数组。

赋值/使用

二维数组的赋值常用两个for循环嵌套进行

int a[5][5], i1, i2;
for(i1=0;i1<5;i1++){
   for(i2=0;i2<5;i2++){
      scanf_s("%d", &a[i1][i2]);
   }
}

若二维数组作为函数参数使用,则需要使用如下格式:

int function(int a[][5]){
   ......
}
//二维数组前面的一维长度不需要给出,但后面的二维长度必须给出

int main(){
   int a[5][5];
}

综上,关于数组的一些基本概念已经梳理完毕了。

下一章的指针,相对而言更加复杂,理解难度也更高,但归根结底与数组的原理极其相似,熟练应用后指针反而更加灵活。 (虽然我现在还不能熟练应用)

这篇博文就到这里~~


文章作者: MUG-chen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MUG-chen !
  目录
加载中...