数组的正确使用
1.数组初始化
在 C99 以后的标准中,允许使用单个指示符为数组的两段“分配”空间,如下面的代码所示:
#define Array_Length 20
int Data[Array_Length] ={0,1,2,3,4,[Array_Long-5]=15,16,17,18,19};
在 Data[Array_Length ]数组中,如果 MAX 大于 10时都没有问题,只不过第5到15的元素将用 0 值进行填充;如果 Array_Length 小于 10,前 5 个元素中将有几个被后 5 个元素(6,7,8,9,10)所覆盖,例如Array_Length=8,初始化结束后:
Data[Array_Length] ={0,1,2,15,16,17,18,19}
是不是和预期不一样了?
2.防止数组越界访问
在 C 语言中,数组必须是静态的。数组的大小在程序运行前就确定了。所谓的数组越界访问,就是指数组下标变量的取值超过了初始定义时的大小,导致对数组元素的访问出现在数组的范围之外。由于 C 语言不会像 Java 等语言对程序中数组下标取值范围进行严格检查,一旦发现数组上溢或下溢,都会因抛出异常而终止程序。而C 语言并不检验数组边界,而检验数组的边界是程序员要做的事情,数组的两端都有可能越界,从而使其他变量的数据甚至程序代码被破坏。
还依上面声明的数组为例:
- //其他代码
- for(i=Array_Length;i>0;i–)
- {
-
a=Data[i] ;
-
//其他代码
- }
看出上面程序的问题了吗?
这里有两个隐含的bug,我们声明了Array_Length长度个元素的数组,但是别忘了,c语言的数组下标是从0,开始的,所以,数组元素Data[Array_Length]是根本不存在的,但C语言编译器却不会告诉你,也就是说,这种情况是允许的,但实际上,Data[Array_Length]的位置上,可能是其他变量(假设为t)的位置,这就会造成a在第一次循环时使用的并不是数组最后一个元素,而是t变量的值,此处为读操作,可能只是影响了for循环的结果,如果是写操作的哈,会直接修改t变量的值,会有什么结果就只有天知道了。
3.数组作为函数的参数时
假如函数func()的形参是上面声明的Data[i] 数组,通常是这样使用的:
extern int Data[];
函数代码如下所示:
void func(int Data[])
{
unsignedint i,j,len=sizeof(Data);//获取数组的长度;
//其他代码
for(i=0;i<len;i++)
{
Data[i]=j;
//其他代码
}
}
在 C 语言中,sizeof 是一个单目操作符,不是函数。其作用就是返回其操作数所占的内存字节数。其中,操作数可以是一个表达式或括在括号内的类型名,操作数的存储大小由操作数的类型来决定。例如,对于数组int a[5],可以使用sizeof(a)来获取数组的长度,这种想法对数组作为函数形参时却是行不通的,
实际上,编译器是将数组名Data隐含的转化为指向数组第一个元素的指针,函数体是使用指针的形式来访问数组的
len=sizeof(Data)
实际上获得的Data指针的长度,它显然和数组长度是两个概念,切记,获取数组的长度时不要对指针应用 sizeof 操作符,既然不能使用sizeof获取数组长度,那如何正确使用数组而不越界昵,可以在传递数组参数的时候,带上传入数组的长度,例如:
void func(int Data[],int len)
{
unsignedint i,j;
//其他代码
for(i=0;i<len;i++)
{
Data[i]=j;
//其他代码
}
}
4多维数组
C 语言中的多维数组(multidimensional array)其实就是元素为数组的数组。n 维数组的元素是 n-1 维数组。例如,二维数组的每个元素都是一维数组,特殊情况一维数组的元素计算单个元素了。
多维数组声明时,每个维度用一对方括号来表示,例如二维数据
Data[row][col]
我们需要行row和列col两个维度才能确定唯一的一个内部元素。定义一个3x4的二维数组,可以这样声明:
int array[3][4] =
{
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 }
};
该数据具有3行,每一行都具有4个元素,所以这个二维数组一共具有3x4=12个元素。
通过一个双循环嵌套来对这个二维数组进行初始化和显示:
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
array[i][j] = i+j;
printf(“%d\n”, array[i][j]);
}
}
存储多维数组时,维度靠后的的数据连续存储,如下图:
5指针数组
所谓的指针数组,就是定义一个数组,数组里面的每一个元素都是一个地址。
例如:int a=1, b=2, c=3;
int * Add [5] = {&a,&b,&c};
其中则 Add [1] 就是&b, 所以 * Add [1] 与 b 是一样的。
指针数组常常作为二维数组的一种便捷替代方式,继承了指针的优点和灵活性,达到使用灵活,节省空间和提高效率的作用,例如,如果需要处理一批长短不一字符串,我们可以将它们存储在一个二维数组中,但是,该数组行空间大小必须足以存储下可能出现的最长字符串:
#define ARRAY_Num 500
#define STRLEN_MAX 1024
char myStrings[ARRAY_Num ][STRLEN_MAX ] =
{ // 字符举例:
“123”
“1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111”
“22222”
};
然而,这个方式造成大量的内存浪费,短字符串会让大部分的数据空间是空的;另一方面,有些行根本没有用到,但却得为它预留内存。解决这个问题的一个简单的解决方案就是使用指针数组,让指针指向对象(在此处的对象就是字符串),然后只给实际存在的对象分配内存(未用到的数组元素则是空指针)。
#define ARRAY_Num 500
char *myStrPtr[ARRAY_Num ] = // char指针的数组
{ // // 字符举例:
“123”
“1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111”
“22222”
};
这样一来,myStrPtr数组指针的每个元素空间都得到充分利用率。