关于C语言的数组赋值和数组下标越界问题

考研(C语言常见问题) 专栏收录该内容
1 篇文章 0 订阅

一、数组赋值

数组名就代表着该数组的首地址,后面的所有元素都可以根据数组名加上偏移量取到。

1. 一维数组


第一个小例子:编程实现显示用户输入的月份(不考虑闰年)拥有的天数。**

#include<stdio.h>
#define MONTHS 12
int main(){
    int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
    int month;  // 1-12
    do{
        printf("Input a month:");
        scanf("%d", &month);
    }while(month<1 || month>12);  // 处理不合法数据
    // days数组是从0-11
    printf("The number of days is %d\n", days[month-1]);  
    return 0;
}

        上面这个例子我们简单的用一维数组存储了一年的年份。这个代码的功能想必题目说得很清楚了,我就不再重复赘述。中间有一段while循环的代码,那个是为了处理不合法数据的,因为当我们输入的值在1-12之间的时候,这个循环我们是出不去的,会让我们一直输入月份。
        在后面输出对应月份的天数的时候,我们用的下标是month-1,因为数组的下标是从0-11,我们输入的是1-12,所以-1后才是我们对应需要的月份。


2. 二维数组

一维数组比较简单,我们来看二维数组。下面是一个二维数组的小例子。

int main(){
	// 没有指定的位置,自动赋值为0
    int a[][3] = {{1,2,3}, {4,5}, {6}, {0}};  
    printf("%d, %d, %d\n", a[1][1], a[0][2], a[2][1]);
    return 0;
}
// 数组构造:
// 1   2   3
// 4   5   0
// 6   0   0
// 0   0   0

Input:
5, 3, 0

        这个例子我们可以看出来,这个二维数组,我们最初只是指定了列数,没有指定行数,也就是说二维数组可以只指定列数,行数可以在我们进行赋值的时候,自动确定。比如我这个例子,二维数组给定了三列,里面写了4个小的一维数组,所以最终这个二维数组的维度是4行3列


我们再来看下面这个更有趣的例子。

int main(){
	// 没有指定的位置,自动赋值为0
    int a[][3] = {1, 2, 3, 4, 5, 6, 7};  
    return 0;
}

        这个例子中没有一个小括号,虽然前面的定义是一个二维数组,但是里面的赋值就像一个一维数组一样,有点儿奇怪?其实他的内部是这样的。

1   2   3
4   5   6
7   0   0

        为什么会这样?因为他指定了三列,这是毋容置疑的,但是没有指定行数,所以里面的元素会按照三列的规则一行一行的排列下去,如果排到某一行,还有空的位置没有元素,就会自动被赋值为0,这个二维数组的维度就是3行3列

数组的奇特赋值方式我想大家已经理解了,那么我们来看 数组越界 的问题。


二、数组越界

1. 一维数组越界

        一维数组的越界,编译器(我使用的VC6.0)不会在你越界的提示你,而是会根据这个地址继续对该位置进行相关操作(比如赋值或者取值)。我们来看下面这个例子:

#include<stdio.h>
int main(){
	int a = 1, c = 2, b[5], i;
	printf("\na = %d, c = %d\n", a, c);
	
	// 这里明显对于b数组的赋值,已经越界
	for (i=0; i<=8; i++){
		b[i] = i;
	}
	
	printf("\n a = %d, c = %d\n", a, c);
	return 0;
}

运行结果如下:
在这里插入图片描述
这个 ac 的值明显被改变了,我们下面来看一下数组越界后,发生了什么:
在这里插入图片描述
        其实编译器之所以在编译的时候没有报错,是因为他的取值赋值是根据地址来做的。由于数组名就是该数组的首地址,也就是第一个元素的地址,所以当你的索引变化的时候,虽然数组已经没有对应元素给他使用了,但是他依然会根据偏移量往后继续找,这也就把后面跟着的 c 和 a 的值给修改了。(我的编译器虽然在运行的时候没有报错,但是运行结束后还是出现了异常,提示关闭程序)

关于偏移量,我举个简单的例子:
比如,在C语言中 char 字符类型是占一个字节的。
我们定义一个一维数组  char a[5];
假设首地址是 40,那我们去 a[5] ,这个元素的地址就是 40+5*1 
因为字符类型一个位置占一个字节。
那个5就是偏移量,也就是说如果你定义的 int a[5],那么后面就是 40+5*4 了。
因为在大多数编译器中 int 占4个字节,当然也有占2个字节的。

        上面是一维数组的越界情况。


2. 二维数组越界

        其实这个的越界和一维数组越界原理是一样的,都是根据偏移量来取值,不同的是二维数组有多行,也就是你在这一行越界后,他会跑到下一行,我们来看下面这个例子:

#include<stdio.h>
int main(){
    int i, j;
    char a[6][4] = {(char)0x00};
    printf("%p\n", a);  // 输出首地址
    a[0][0] = 0x01;  // 16进制
    a[1][0] = 0x10;
    a[0][4] = 0x04; // a[1][0]
    a[1][4] = 0x05; // a[2][0]
    a[5][4] = 0x20; // 完美刚好越界
    a[5][5] = 0x30; // 越界
    for (i=0; i<6; i++){
        for (j=0; j<4; j++){
            printf("%d\t", a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

Input:
1    0    0    0
4    0    0    0
5    0    0    0
0    0    0    0
0    0    0    0
0    0    0    0

        代码中的注释我们可以看到,在一个二维数组 arr[6][4]中,a[0][4] 和 a[1][0] 是同一个元素,因为 a[0][4] 在第一排越界后,根据偏移量的计算,他跑到了第二列的第一个。

a[6][4] 是一个6行4列的二维数组,一行4个
a[0][4] = 0*4 + 4 = 4
a[1][0] = 1*4 + 0 = 4

a[i][j] = i*4 + j
表示前面已经走过了 4*i 个地址,然后在 i+1 行走了 j 个位置。
计算出来就是最终的地址。

对了,0x表示的十六进制的数组,比如 0x10 换算成十进制就是 16。
因为就像16进一一样,在第二位有一个1,说明就已经有个16了。

        以上就是二维数组越界的情况。


3. 下面我们看一下简单的二维数组小例子

#include<stdio.h>
#define MONTHS 12
int main(){
    int days[MONTHS] = {{31,28,31,30,31,30,31,31,30,31,30,31},  // 平年
                       {31,29,31,30,31,30,31,31,30,31,30,31}};  // 闰年
    int year, month;  // 1-12
    
    do{
        printf("Input a month:");
        scanf("%d, %d", &year, &month);
    }while(month<1 || month>12);  // 处理不合法数据
    
    if (((year%4==0) && (year%100!=0)) || (year%400==0)){  /* 闰年 */
    	printf("The number of days is %d\n", days[1][month-1]);  // days数组是从0-11
    }else{  /* 平年 */
        printf("The number of days is %d\n", days[0][month-1]);
    }
    
    return 0;
}
  • 1
    点赞
  • 0
    评论
  • 3
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页

打赏作者

为什么我不是源代码

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值