关于C语言的数组赋值和数组下标越界问题
一、数组赋值
数组名就代表着该数组的首地址,后面的所有元素都可以根据数组名加上偏移量取到。
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;
}
运行结果如下:
这个 a
、c
的值明显被改变了,我们下面来看一下数组越界后,发生了什么:
其实编译器之所以在编译的时候没有报错,是因为他的取值赋值是根据地址来做的。由于数组名就是该数组的首地址,也就是第一个元素的地址,所以当你的索引变化的时候,虽然数组已经没有对应元素给他使用了,但是他依然会根据偏移量往后继续找,这也就把后面跟着的 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;
}
上一篇: php与mysql三日通-第二天