C语言中的数组


C语言中的数组

1 语法定义和基本概念

1.1 基本概念

在C语言中,数组是最基本的 构造类型 ,前面的 整型、浮点型、字符型 都是基本数据类型。
数组中的元素在内存中连续存放,所以用数组名和下标可以唯一的确定一个数组元素。
基于以上的观点,我们可以这样去定义数组:具有相同基本数据类型的一组元素,在内存中连续存放而组成的一种数据结构

1.2 一维数组的定义和引用

int arr1[10]; // 定义一个 int 型的数组,大小为10
char arr2[10]; // 定义一个 char 型的数组,大小为10
float arr3[10]; // 定义一个 float 型的数组,大小为10
实际上数组名是一个常量地址,用来存放内存空间的首地址

1.3 数组默认值问题

运行下面的一段代码,发现如果数组中没有指定默认值,数是随机的。所以书上例题清0是有必要的

  1. #include <stdio.h>
  2. int main(void) {
  3. int a = 1, arr1[10];
  4. static int arr2[10];
  5. for (int i = 0; i < 10; i++){
  6. printf("%d \n",arr1[i]);
  7. }
  8. printf("------------------\n");
  9. for (int i = 0; i < 10; i++){
  10. printf("%d \n",arr2[i]);
  11. }
  12. return 0;
  13. }
1.4 一维数组的初始化

建议不省略,严格写法 如 : int arr1[10] = {1,2,3,4,5,6,7,8,9,10};

int arr1[10] = {1,2,3,4,5,6,7,8,9,10};
static int arr2[2]; // 静态数组不赋值,默认是0
static int arr3[5] = {1,2,3,4,5}; // 也是可以的
static int arr4[10] = {1,2} //这样的话 前两位元素的值为 1和2,后面8个元素的值都是 0
int arr4[10] = {1,2} //这样的话 前两位元素的值为 1和2,后面8个元素的值不确定

2.简单例题练习

2.1 电视节目投票
  1. /* 投票情况统计 */
  2. #include <stdio.h>
  3. int main(void)
  4. {
  5. int count[8]; // 电视节目-对应的类目数组
  6. int i, response;
  7. // 数组个元素存放 int 0
  8. for (i = 0; i < 8; i++){
  9. count[i] = 0;
  10. }
  11. for (i = 1; i <= 10; i++){
  12. printf("input your response (1~8): \n");
  13. scanf("%d", &response);
  14. if (response < 1 || response > 8){ // 防止数组下标越界
  15. printf("this response is bad \n");
  16. }else{
  17. count[response]++;
  18. }
  19. }
  20. printf("category----num \n");
  21. for (i = 0; i <= 8; i++){
  22. printf("%4d%4d\n", i, count[i]);
  23. }
  24. return 0;
  25. }
2.1 利用数组求斐波那契数列的前10个数,每5个数为一行输出。
#include <stdio.h>

int main(void)
{
    // [1]定义一个数组存放斐波那契数列的前10个数
    int arr1[10] = {1, 1};

    // [2]for 循环计算出 后面的8个元素的值
    for (int i = 2; i < 10; i++){
        arr1[i] = arr1[i - 1] + arr1[i - 2];
    }

    // [3]遍历数组,满5个一换行输出
    for (int i = 0; i < 10; i++){
        printf("%4d", arr1[i]);
        if ((i + 1) % 5 == 0) {
            printf("\n");
        }
    }

    return 0;
}

偶然发现 将printf(" 1 "); 中的双引号就会报错,猜想 C中单引号只是为了表示字符型 项 ‘A’ 这样的

2.3 顺序查找法,输入5个整数,将他们存入数组a中,在输入一个数x,找数字中查找是否存在这个元素,如果找到,返回这个数组的index,没找到打印 Not found
#include <stdio.h>
int main(void) {

    int i, number, x, a[5];
    int flag = 0;
    printf("请输入5个整数: \n");
    for ( i = 0; i < 5; i++){
        scanf("%d", &number);
        a[i] = number;
    }

    printf("请输入要查找的数x:");
    scanf("%d", &x);

    for ( i = 0; i < 5; i++){
        if(x == a[i]){
            flag = 1;
            printf("找到了这个数,在数组中的下标是 %d \n",i);
            break;
        }
    }
    if (flag == 0) {
        printf("没找到啊~");
    }
    return 0;
}
2.4 冒泡排序

原理:对一组数进行排序,两个相邻位置比较,大的数移动到右边,小的数移动到左边…
看例子理解 : [5,4,3,2,1] 进行冒泡排序的过程
+++++++++++++++
1️⃣ 5,4 => 4, 5, 3, 2, 1
2️⃣ 5 , 3 => 4, 3, 5, 2, 1
3️⃣ 5 , 2 => 4, 3, 2, 5, 1
4️⃣ 5,1 => 4, 3, 2, 1, 5
+++++++++++++++
1️⃣ 4 , 3 => 3, 4, 2, 1, 5
2️⃣ 4 , 2 => 3, 2, 4, 1, 5
3️⃣ 4 , 1 => 3, 2, 1, 4, 5
+++++++++++++++
1️⃣ 3 , 2 => 2, 3, 1, 4, 5
2️⃣ 3 , 1 => 2, 1, 3, 4, 5
+++++++++++++++
1️⃣ 2 , 1 => 1, 2, 3, 4, 5
+++++++++++++++
我们发现按照这种算法,每一轮比较都会为下一次比较较少一次比较。
第一轮 比较 n-1次, 第二轮比较 n-2 次 …. …. 最后第 n-1 轮比较 1次。然后我们将其转换为数学问题 n个数一共要比较 1 + 2 + 3 + ... + (n -1) = n * (n - 1) / 2

#include <stdio.h>

int main(void) {
    int i, j, arr[5] = {5, 4, 3, 2, 1};
    // 外层循环控制只是第几轮比较
    for (i = 0; i < 5 - 1; i++){ // 5 -1 实际上就是 数组长度-1
        for (j = 0; j < 5 - (i + 1); j++){ // 每次确定了一个 ,而i的初始值是 0 ... 所以你懂的。
            if (arr[j] > arr[j+1]) { // 满足条件要去实现值的互换 a= a+b; b= a- b; a= a -b;
                arr[j] = arr[j] + arr[j+1];
                arr[j+1] = arr[j] - arr[j+1];
                arr[j] = arr[j] - arr[j+1];
            }
        }
    }

    for (i = 0; i < 5; i++){
        printf("%4d", arr[i]);
    }
    return 0;
}
2.5 选择排序

原理:将一组数从头扫到末尾,找到最小的一个数和第一个数做交换。直到从头到尾扫描一遍。
看例子理解:[5, 4, 3, 2, 1] 进行选择排序
1️⃣ {1} , {4, 3, 2, 5} — 有序队列+1,无序队列 -1
2️⃣ {1,2 }, { }
… …
懒得写了,其实冒泡排序和选择排序的规则是,选择排序确定一个位置,然后和不同位置的元素相互比较,而冒泡排序每轮则是和相邻位置的元素相比较,位置数不断变化的。


#include <stdio.h>
int main(void)
{
    int i, j, t, arr[5] = {5, 4, 3, 2, 1};
    for (i = 0; i < 4; i++){
        for (j = i+1; j < 5 ; j++){
            if(arr[i] > arr[j]){
                arr[i] = arr[i] + arr[j];
                arr[j] = arr[i] - arr[j];
                arr[i] = arr[i] - arr[j];
            }
        }
    }
    for (i = 0; i < 5; i++){
        printf("%4d", arr[i]);
    }
    return 0;
}
2.6 二分查找

针对于有序数组的一中查找方式。假设有一个size 为10的 升序数组
int arr1[10] = {1, 2 ,3, 4, 5, 6, 7, 8, 9, 10};
int x; // x 是我们要找的数
int low = 0, height = 9 ;
int mid = (low + height) / 2;
判断 array[mid] 和 x 的大小关系 ... ... 就可以进一步缩小查找的范围。直到找到这个数
当这个范围一直缩小,缩小到一个数而这个数还不是要查找的数是程序终止

#include <stdio.h>

int main(void) {

    // [1]定义变量
    int x, low, height, mid, arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    low = 0;
    height = 9;
    printf("Enter x: \n");
    scanf("%d", &x);

    // [2]开始查找
    while(low <= height){
        mid = (low + height) / 2;
        if(arr[mid] == x){
            break;
        }else if (arr[mid] > x){
            height = mid - 1;
        }else if (arr[mid] < x){
            low = mid + 1;
        }
    }

    // [3]输出结果
    if(low <= height){
        printf("index = %d \n", mid);
    }else{
        printf("Not Found \n");
    }

    return 0;
}

3.思考

3.1 C语言中如何求数组的大小?

其他语言带来的参考

不像 java array.length ,
js 数组 也有 length 属性 (实际上 js中的数组用 .length 计算得到的并不一定对,这里就不写说明了 )。
php 用 count() ?

很遗憾 C语言中的数组既没有 length 属性 也没有 count() 方法

但是 C 中有这样一个方法 sizeof() , 他可以有这个变量的字节数
于是 我们是否可以求出 数组的大小?
由于 C 中的数组数据类型一致,所以嘿嘿这样 sizeof(arr)/sizeof(arr[0]) 就可以解决问题了,也就是 总占用字节/每个单元的字节 = 单元个数
但是我们要理解,如果是下面这样
int a[10] = {1, 2, 3, 4, 5}; 我们 sizeof(arr)/sizeof(arr[0]) 求出的是10,后面的元素没有被初始化,也就是将我们无法确定数组中元素的实际数量… …

4.二维数组

应用场景

和java 等其它编程语言一样,C语言也支多维数组。常用二维数组表示二维表个矩阵。

4.1 定义

类型名 数组名[行][列]
int arr[2][3]; / 这个二维数组2行3列,一共存放了6个 int型的 元素 /

4.2 引用

注意先行后列,其它略过… ….

二维数组的初始化

分行赋值
int a[3][3] ={{1, 2 ,3}, {4, 5, 6}, {7, 8, 9}};

顺序赋值
int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

此时这个二维数组用矩阵表示就是下面这个样子

1       2       3
4       5       6
7       8       9
4.3 二维数组在内存中的存放形式**

参考下图C语言中的数组 - 图1

4.4 例题-练习题
例7-7 将一个3 x 2 的矩阵中的值存入到一个3 x 2 的二维数组中,现将矩阵输出出来,再找出其中最大值的下标将其输出。
#include <stdio.h>

int main(void) {
    // 定义数组、行列变量
    int arr[3][2];
    int i, j, row, col;
    // 将数据存到数组中
    printf("请输入6个数:\n"); // 提示用户输入6个数
    for (i = 0; i < 3; i++){ // 控制行 
        for (j = 0; j < 2; j++){ // 控制列 
            scanf("%d", &arr[i][j]);
        }
    }

    // 按照矩阵的格式输出二维数组
    for (i = 0; i < 3; i++){ 
        for (j = 0; j < 2; j++){ 
            printf("%4d", arr[i][j]);
        }
        printf("\n");
    }

    // 遍历整个二维数组找出最大值
    row = col = 0;
    for (i = 0; i < 3; i++){ 
        for (j = 0; j < 2; j++){ 
            if(arr[i][j] > arr[row][col]){
                row = i;
                col = j;
            }
        }
    }

    // 输出矩阵中最大数的 index
    printf("max = arr[%d][%d] = %d \n", row, col, arr[row][col]);

    return 0;
}
例7-8 定义一个3 x 2 的二维数组a, 二维数组的值由下式给出,按照矩阵的形式输出a,a[i][j] = i + j
#include <stdio.h>

int main(void)
{
    int i, j;
    int a[3][2];

    // 给数组赋值
    for (i = 0; i < 3; i++ ){
        for (j = 0; j < 2; j++){
            a[i][j] = i + j;
        }
    }

    // 按照矩阵的形式输出二维数组 a
    for (i = 0; i < 3; i++ ){
        for (j = 0; j < 2; j++){
            printf("%4d", a[i][j]);
        }
        printf("\n");
    }  

    return 0;
}
例7-9 输入一个正整数n(1< n <= 6),根据a[i][j] = i * n + j + 1;. 生成一个 n x n 的矩阵,然后将这个矩阵行列互换后输出。
/*
例如 n = 3 时,如下
1   2   3           1    4   7
4   5   6           2    5   8
7   8   9           3    6   9 
转化前             转换后
*/

#include<stdio.h>

int main(void) {
    int i, j, n;
    int a[6][6];
    printf("Enter n: \n");
    scanf("%d", &n);

    // 给二维数组赋值
    for (i = 0; i < n; i++){
        for (j = 0; j < n; j++){
            a[i][j] = i * n + j + 1;
        }   
    }

    // 输出原矩阵
    printf("原矩阵如下: \n");
    for (i = 0; i < n; i++){
        for (j = 0; j < n; j++){
            printf("%4d", a[i][j]);
        }
        printf("\n");
    }

    // 行列互换
    int temp;
    for (i = 0; i < n; i++){
        for (j = 0; j < n; j++){
            if (i <= j ){
                // a[i][j] = a[i][j] + a[j][i];
                // a[j][i] = a[i][j] - a[j][i];
                // a[i][j] = a[i][j] - a[j][i];
                temp = a[i][j];
                a[i][j] = a[j][i];
                a[j][i] = temp;
            }
        }   
    }

    // 输出转换后的矩阵
    printf("新矩阵如下: \n");
    for (i = 0; i < n; i++){
        for (j = 0; j < n; j++){
            printf("%4d", a[i][j]);
        }
        printf("\n");
    }
    return 0;
}
例7-11 判断输入的字符串是否是回文数 回车结束,回文数正反都一样
#include <stdio.h>

int main(void) {
    int i, left, right;
    static char s[10];

    // 存入数组
    printf("Enter a String (length <= 10) : ");
    i = 0;
    while( (s[i] = getchar()) != '\n' ){
        i++;
    }
    // s[i] = '\0';

    for(left = 0, right = i-1; left < right; left++, right--){
        if(s[left] != s[right]){
            break;
        }
    }

    if(left >= right ){ // 表示for循环正常结束
        printf("true");
    }else {
        printf("false");
    }
    return 0;
}

5. 从字符型数组引到字符串

5.1 基本概念。

C语言不像是Java等其他的编程语言,并没有为字符串单独设置一个基本数据类型,而是用数组存放连续的字符char表示了字符串

定义一维字符数组

  • char str[80];
  • char str[5] = { ‘H’, ‘a’, ‘p’,’ p’, ‘y’};
  • static str[6] = { ‘H’, ‘a’, ‘p’,’ p’, ‘y’};
  • static str[6] = { ‘H’, ‘a’, ‘p’,’ p’, ‘y’, ‘\0’};
  • static str[] = { ‘H’, ‘a’, ‘p’,’ p’, ‘y’, ‘\0’};

定义一个字符串

  • static char str[6] = {'H', 'a', 'p',' p', 'y', '\0'};// 第一种方式
  • static char str[6] = {"Happy}; // 第二种方式
  • static char str[6] = "Happy"; // 第三种方式

这里的 长度设置比size 多1,应为我们需要设置 ‘\0’ 作为字符串的结束符。
static char s; s[0] = 'a'; s[1] = '\0';
等价于 => static char s[80] = "a";
区分 “a” 和 ‘a’,前者是字符串常量,后者这是一个字符而已,我们遍历字符串是用的是 '\0'.

5.2 练习
例题 7-13 输入一个以回车符号未结束的字符串(少于80个字符)),统计其中数字字符的个数
#include <stdio.h>

int main() {

    // [1] 数组存取字符
    int i, count;
    printf("请输入字符串,字符个数 < 80 : ");
    char str[80];
    while ( (str[i] = getchar()) != '\n'){
        i++;
    }
    str[i] = '\0';

    // [2]遍历统计其中数字的个数
    count = 0;
    for (i = 0; str[i] != '\0'; i++){
        if( str[i] > '0' && str[i] < '9'){
            count++;
        }
    }
    printf("count = %d \n", count);

    return 0;
}