C Programming: A Modern Approach

第8章 数组

2020-02-27  本文已影响0人  橡树人

英文原版:P161

截止目前,我们接触到的变量都是标量:可存储单个数据项。

C语言支持聚合变量:可存储多个数据项。C语言中有两类聚合变量:数组和结构体。

本章会集中讨论一维数组,因为在C语言中一维数组比多维数组更重要。第12章会提供更多有关数组的信息。第16章会介绍结构体。

本章的主要内容:

8.1 一维数组

什么是数组?

最简单的数组就是一维数组。
从概念上讲,一维数组的元素是一个接一个地排成一行(或者一列)。

数组声明

#define N 10

int a[N];

数组下标

例1 清空数组a

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

例2 把数据读进数组a

for (i = 0; i< N; i++) {
 scanf("%d", &a[i]);
}

例3 对数组a求和

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

程序示例:逆序输出一组数

源文件reverse.c

/** Reverse a series of numbers */

#include <stdio.h>

#define N 10

int main(void) {
    int a[N], i;

    printf("Enter %d numbers: ", N);
    for (i =0; i<N; i++) {
        scanf("%d", &a[i]);
    }

    printf("In reverse order:\n");
    for (i = N-1; i >= 0; i--)
    {
        printf(" %d\n", a[i]);
    }
    printf("\n");

    return 0;
}

数组初始化

规则:

例1 用大括号括起来的常量表达式类给数组初始化

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

例2 如果初始化式比数组长度短,则将数组剩余元素设置为0,比如

int a[10] = {1,2,3,4,5,6};//等价于数组a的初始化式为{1,2,3,4,5,6,0,0,0,0}

例3 将数组全部初始化为0,可以这么做:

int a[10] = {0};

例4 如果有初始化式,则数组的长度可以省略不写,由编译器根据初始化式的长度来确定数组的长度:

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

指定初始化式

例1 假设想让数组的2号元素为29,9号元素为7,14号元素48,其余值为0,则该如何给该数组初始化?
方法一:

int a[15] = {0,0,29,0,0,0,0,0,0,7,0,0,0,0,48};

方法二:指定初始化式

int a[15] = {[2]=49, [9] = 7, [14] = 48};

中括号里的数字为指示符,比如2914

指示符有哪些性质?

例2 确定数组的长度

int b[] = {[5] = 10, [23] = 13, [11] = 36, [15] = 29};

则该数组的长度为24。
例3 混合使用逐个元素和指定初始化式来初始化数组

int c[10] = {5,1,9, [4] = 3, 7, 2, [8] = 6};//{5, 1, 9, 0, 3, 7, 2, 0, 6, 0}

检查重复数字

源文件repdigit.c

#include <stdio.h>
#include <stdbool.h>

int main(void) {
    //使用初始化式来初始化数组,且初始化式的长度小于数组的长度
    bool digit_seen[10] = {false};
    int digit;
    long n;

    printf("Enter a number:");
    scanf("%ld", &n);

    while(n > 0){
        digit = n%10;
        if (digit_seen[digit]) {
            break;
        }
        digit_seen[digit] = true;
        n /= 10;
    }

    if (n > 0) {
        printf("Repeated digit\n");
    }else {
        printf("No repeated digit\n");
    }
}

对数组使用sizeof运算符

sizeof运算符可判断一个数组的大小。
例1 有一个10个整数的数组a,则sizeof(a)等于40。

可使用sizeof运算符来判断一个数组元素的大小。

例2 有一个10个整数的数组a,则数组a的长度等于sizeof(a)/sizeof(a[0])

程序示例:计算收益

源文件interest.c

#include <stdio.h>

#define NUM_RATES ((int)(sizeof(value)/sizeof(value[0])))
#define INITIAL_BALANCE 100.00

int main(void) {
    int i, low_rate, num_years, year;
    double value[5];

    printf("Enter interest rate: ");
    scanf("%d", &low_rate);
    printf("Enter number of years:");
    scanf("%d", &num_years);

    printf("\nYears");
    for (int i = 0; i < NUM_RATES; ++i)
    {
        printf("%6d%%", low_rate+i);
        value[i] = INITIAL_BALANCE;
    }
    printf("\n");

    for(year = 1; year <= num_years; year++){
        printf("%3d   ", year);
        for (i = 0; i < NUM_RATES; i++) {
            value[i] += (low_rate + i)/100.0 *value[i];
            printf("%7.2f", value[i]);
        }
        printf("\n");
    }


    return 0; 
}

8.2节 多维数组

跟其他语言相比,多维数组在C语言中的角色相对较弱,因为C语言使用指针数组来存储多维数组,使用指针数组来存储多维数组比其他语言更为灵活。

C语言是以行主序的方式来在内存中存储二维数组的,先第0行,再第1行,依次类推。

例1 二维数组m[5][9]

/**
* 两维数组的声明
* 两维数组元素的访问
*/
int m[5][9];//声明了一个叫m的两维数组
//使用m[i][j]来访问在i行j列的元素

m数组在内存中的行主序存储示意图:


二维数组m的行主序示意图.png

例2 初始化一个数组,来用作单位矩阵。
方法一:使用嵌套循环

#define N 10

double ident[N][N];
int row, col;

for (row = 0; row < N; row++){
  for (col = 0; col < N; col++) {
    if (row == col) {
      ident[row][col] = 1.0;
    }else {
      ident[row][col] = 0.0;
    }
  }
}

方法二:使用指定初始化式

#define N 10

double ident[N][N]={[0][0]=1.0, 
                    [1][1]=1.0,
                    [2][2]=1.0, 
                    [3][3]=1.0,
                    [4][4]=1.0, 
                    [5][5]=1.0,
                    [6][6]=1.0,
                    [7][7]=1.0,
                    [8][8]=1.0, 
                    [9][9]=1.0};

例3 初始化二维数组m[5][9]

// 内部初始化式满5个
int m[5][9] = {{1,1,1,1,1,0,1,1,1},
               {0,1,0,1,0,1,0,1,0},
               {0,1,0,1,1,0,0,1,0},
               {1,1,0,1,0,0,0,1,0},
               {1,1,0,1,0,0,1,1,1}};
// 内部初始化式不满5个,则将剩余元素全部设置为0
m[5][9] = {{1,1,1,1,1,0,1,1,1},
           {0,1,0,1,0,1,0,1,0},
           {0,1,0,1,1,0,0,1,0}};
// 内部初始化式不足一行,则将该行剩余元素置为0
m[5][9] = {{1,1,1,1,1,0,1,1,1},
           {0,1,0,1,0,1,0,1},
           {0,1,0,1,1,0,0,1},
           {1,1,0,1,0,0,0,1},
           {1,1,0,1,0,0,1,1,1}};

常量数组

保留字:const
效果:不应该对声明为const的数组进行修改,编译器能检测到修改某个数组元素的尝试
优点:方便代码阅读,帮助编译器捕获错误
使用范围:数组、变量

例1 常量数组

const char hex_chars[] = 
  {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
   'A', 'B', 'C', 'D', 'E', 'F'};

程序示例:发牌

源文件:deal.c

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define NUM_SUITS 4
#define NUM_RANK 13

int main(void)
{
    bool in_hand[NUM_SUITS][NUM_RANK] = {false};
    int num_cards, rank, suit;
    const char rank_code[] = {'2', '3', '4', '5', '6', '7', '8',
                              '9', 't', 'j', 'q', 'k', 'a'};

    const char suit_code[] = {'c', 'd', 'h', 's'};

    srand((unsigned) time(NULL));

    printf("Enter number of cards in hand: ");
    scanf("%d", &num_cards);

    printf("Your hand:");
    while(num_cards > 0) {
        suit = rand() % NUM_SUITS;
        rank = rand() % NUM_RANK;
        if (!in_hand[suit][rank]) {
            in_hand[suit][rank] = true;
            num_cards--;
            printf(" %c%c", rank_code[rank], suit_code[suit]);
        }

    }
    printf("\n");


    return 0;
}

8.3节 可变长度数组VLA

如何指定可变长度数组的长度?

可变长度数组有哪些优势?

可变长度数组有哪些缺陷?

例1 使用变量来指定数组长度
reverse2.c

/** Reverse a series of numbers using a variable-length array */

#include <stdio.h>

int main(void) {
    int i, n;

    printf("How many numbers do you want to reverse? ");
    scanf("%d", &n);

    int a[n];

    printf("Enter %d numbers: ", n);
    for (i =0; i<n; i++) {
        scanf("%d", &a[i]);
    }

    printf("In reverse order:\n");
    for (i = n-1; i >= 0; i--)
    {
        printf(" %d\n", a[i]);
    }
    printf("\n");

    return 0;
}

例2 使用任意表达式来指定数组长度

int a[3*i + 5];
int b[j+k];
int c[m][n];

提问和解答

问题1 数组的下标为什么是从0开始,而不是从1开始?
答:让数组的下标从0开始,有两个好处:1,使编译器能简化点;2,使数组下标运算能更快点;

问题2 如果想让数组下标从1开始到10而不是到9,该怎么做?
答:一个常用的技巧是声明数组为11个元素,而不是10个。此时,虽然数组的下标是从0到10,但可以忽略0号元素。

上一篇 下一篇

猜你喜欢

热点阅读