一维/二维数组操作实验

      我们在使用数组编程时, 往往会对数组的访问范围格外留心.  一般地, 我们会认为只要数组越界访问, 程序就会报错——其实不然, 有时越界访问也不会造成程序的崩溃. 值得强调的是, 我并不是在鼓励朋友们越界访问数组, 而是想让大家正视这一问题, 并在模拟越界访问现场时理解数组访问操作的实质. 事实上, 越界访问数组绝不是一种好的编程习惯, 甚至可以说"糟糕", 因为合法范围外的存储单元中的数据是未知的, 这些存储单元的作用也是未知的, 如果我们随意修改这些数据, 其造成的后果是不可估量的. 

      下面这个程序给出了越界访问操作的实例: 从程序的运行结果来看, 编译器为变量i、a、c和数组b分配了连续的存储空间, 根据程序运行结果中的内存地址可画出下面的内存地址对应图. 程序在运行期间, 从0060FEE0存储单元开始, 顺序修改各内存单元存储的数值(int型变量占4个字节, 而内存按字节编址, 所以每个int型变量占4个存储单元). 在程序结束前, 从0060FEE0到0060FF00的9个存储块中的值都被修改过. 碰巧的是, 程序运行时对变量i的修改未影响循环的正确结束. 这里多想一步, 这次只是碰巧, 在一般情况下, 这种性质的操作极有可能给程序的运行带来负面的影响.

#include<stdio.h>
/* 此程序无任何实际意义 */
int main()
{
    int i, a = 1, c = 2, b[5] = {0};
    printf("%p  %p  %p  %p\n", b, &c, &a, &i);
    i = 0;
    while(i <= 8)
    {
        b[i] = i;
        printf("%d  ", b[i]);
        i ++;
    }
    printf("\n");
    printf("c = %d, a = %d, i = %d\n", c, a, i);
    /* */
    return 0;
}


      由程序的运行结果, 我们可以画出变量在内存中的分布情况.

内存地址程序开始时程序运行结束前
0060FEE0  (b[0])00
 
 
 
0060FEE4 (b[1])01
 
 
 
0060FEE8 (b[2])02
 
 
 
0060FEEC (b[3])03
 
 
 
0060FEF0 (b[4])04
 
 
 
0060FEF4 (c)25
 
 
 
0060FEF8 (a)16
 
 
 
0060FEFC (i)07(最后变为9)
 
 
 
0060FF00 (b[8])未知8
 
 
 

      我想大多数朋友对二维数组的理解仅限于将其想象为一个存储矩阵, 其实这只是二维数组的逻辑存储结构, 其在内存中的物理存储结构并不真的是一个矩阵, 而是和一维数组一样的一维连续存储空间. C语言编译器将二维数组在内存中按行存储, 所以实际上二维数组和一维数组一样, 都是连续的存储在内存中. 

      我们一起来设计一个程序, 如果一个小组有4名同学, 每名同学都参加了期末考试, 而期末考试共考察了5门课的学习情况(微积分、C语言、电路分析基础、电子技术、信号与系统), 使用二维数组编程并求这4名同学总成绩的平均值和电子技术课程的平均分.

#include<stdio.h>
#define N 4
float Average1(unsigned short s[][5]);
float Average2(unsigned short s[][5]);
int main()
{
    /* 共有N名同学, 5门课程: 微积分、C语言、电路分析基础、电子技术、信号与系统 */
    unsigned short students[N][5] = {0};
    short temp;
    unsigned short i;
    unsigned short j;
    i = 0;
    while(i < N)
    {
        j = 0;
        printf("第%hu位同学的成绩 ", i + 1);
        while(j < 5)
        {
            switch(j)
            {
                case 0 : printf("微积分: "); break;
                case 1 : printf("C语言: "); break;
                case 2 : printf("电路分析基础: "); break;
                case 3 : printf("电子技术: "); break;
                case 4 : printf("信号与系统: "); break;
            }
            /* 增加输入容错机制 */
            while(scanf("%hd", &temp) != 1 || temp < 0 || temp > 100)
            {
                while(getchar() != '\n') ;
                printf("请输入合法数据.\n");
            }
            students[i][j] = temp;
            j ++;
        }
        i ++;
    }
    /* */
    printf("%d名学生的总成绩平均值为%.4f.\n", N, Average1(students));
    /* */
    printf("%d名学生电子技术课程的平均分为%.4f.\n", N, Average2(students));
    /* */
    return 0;
}
/* 求N名同学的总成绩平均值 */
float Average1(unsigned short s[][5])
{
    unsigned short i = 0;
    unsigned short j;
    unsigned long Sum = 0;
    while(i < N)
    {
        j = 0;
        while(j < 5)
        {
            Sum += s[i][j];
            j ++;
        }
        i ++;
    }
    return (float)Sum / N;
}
/* 求N名学生电子技术课程的平均成绩 */
float Average2(unsigned short s[][5])
{
    unsigned short i = 0;
    unsigned int Sum = 0;
    while(i < N)
    {
        Sum += s[i][3];
        i ++;
    }
    return (float)Sum / N;
}


      上面的程序在求解电子技术课程的平均值时, Average2函数的参数为一二维数组: 我们大胆地猜想一下, 可以用一个一维数组解决这个问题吗? 可以这样考虑, 将二维数组看作一个一维数组, 访问第一名同学的电子技术成绩存储单元后, 往后数5个存储单元就是第二名同学的电子技术成绩存储单元, 以此类推可访问所有同学的电子技术成绩存储单元.

/* 求N名学生电子技术课程的平均成绩 */
float Average3(unsigned short s[])
{
    unsigned short i = 0;
    unsigned int Sum = 0;
    while(i < 5 * N)
    {
        Sum += s[i];
        i += 5;
    }
    return (float)Sum / N;
}

       下面给出完整的程序代码.

#include<stdio.h>
#define N 4
float Average1(unsigned short s[][5]);
float Average2(unsigned short s[][5]);
float Average3(unsigned short s[]);
int main()
{
    /* 共有N名同学, 5门课程: 微积分、C语言、电路分析基础、电子技术、信号与系统 */
    unsigned short students[N][5] = {0};
    short temp;
    unsigned short i;
    unsigned short j;
    i = 0;
    while(i < N)
    {
        j = 0;
        printf("第%hu位同学的成绩 ", i + 1);
        while(j < 5)
        {
            switch(j)
            {
                case 0 : printf("微积分: "); break;
                case 1 : printf("C语言: "); break;
                case 2 : printf("电路分析基础: "); break;
                case 3 : printf("电子技术: "); break;
                case 4 : printf("信号与系统: "); break;
            }
            /* 增加输入容错机制 */
            while(scanf("%hd", &temp) != 1 || temp < 0 || temp > 100)
            {
                while(getchar() != '\n') ;
                printf("请输入合法数据.\n");
            }
            students[i][j] = temp;
            j ++;
        }
        i ++;
    }
    /* */
    printf("%d名学生的总成绩平均值为%.4f.\n", N, Average1(students));
    /* */
    printf("%d名学生电子技术课程的平均分为%.4f.\n", N, Average2(students));
    /* */
    printf("%d名学生电子技术课程的平均分为%.4f.\n", N, Average3(students[0] + 3));
    /* */
    return 0;
}
/* 求N名同学的总成绩平均值 */
float Average1(unsigned short s[][5])
{
    unsigned short i = 0;
    unsigned short j;
    unsigned long Sum = 0;
    while(i < N)
    {
        j = 0;
        while(j < 5)
        {
            Sum += s[i][j];
            j ++;
        }
        i ++;
    }
    return (float)Sum / N;
}
/* 求N名学生电子技术课程的平均成绩 */
float Average2(unsigned short s[][5])
{
    unsigned short i = 0;
    unsigned int Sum = 0;
    while(i < N)
    {
        Sum += s[i][3];
        i ++;
    }
    return (float)Sum / N;
}
/* 求N名学生电子技术课程的平均成绩 */
float Average3(unsigned short s[])
{
    unsigned short i = 0;
    unsigned int Sum = 0;
    while(i < 5 * N)
    {
        Sum += s[i];
        i += 5;
    }
    return (float)Sum / N;
}

      虽然有时的确可以用一维数组代替二维数组解决问题, 但从编程的便利性来看, 使用二维数组的操作思想比使用一维数组的操作思想更易编写程序.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

好梦成真Kevin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值