本文是读完《c和指针》后记录的一些关于数组和指针中值得注意的细节,如有错误,还望指正;另外要说明的是,c中数组和指针有一定的关联,故讲到数组时,难免会与指针牵扯上关系,所以这篇文章也包含部分指针的注意事项,你还可以阅读这篇文章了解更多指针内容:c语言细节 - 指针
char a[5] = "abcd";
char b[5] = "efgh";
b = a;
struct
{
char a[5];
char b[5];
char c[5];
}arrays = {"abcd","efgh","igkl"};
int main(void)
{
printf("%d\n",sizeof(arrays.a));
printf("%p,%p\n", arrays.a , &arrays.a );
printf("%p,%p\n", arrays.a+1 , &arrays.a+1 );
printf("%c,%c\n", *(arrays.a+1), *(char*)(&arrays.a+1));
return 0;
}
以上说明:数组名array和数组名取地址&array,地址值相同,但指向类型不同,实际上&array的类型为数组指针,指向整个数组,而非数组的第一个元素,若定义p为char (*p)[5]
,则p与array属于同一类型
以char array[3][4];
为例:
int main(void)
{
char array[3][4] = {"abc","def","ghi"};
char (*p)[4] = array;
printf("%s\n",*p);
printf("%s\n",*(p+1));
return 0;
}
结果为:
这里可以看出用数组名array进行指针运算,其步长为4个字节,相当于第二层数组的大小
注:你不应该像这样赋值:char (*p)[] = array;
,否则在进行指针运算时,将根据空数组的长度进行调整,即步长为0
char array[3][4];
为例:像这样是错误的func( char **p )
,因为数组名是数组指针,而非指针的指针,应该为func( char (*p)[4] );
,或者func( char p[][4] )
,这里的关键在于除了第一维,你需要告知编译器后面各维的长度除了优先级之外,下标引用与间接访问完全相同
下标引用可以用于任何指针,不仅仅只是数组
假设p指向char型数组array第一个元素:
多维数组中下标是从左到右计算的,数组名是指向第一维第一个元素的指针,类型为数组指针
以char array[3][4];
为例:
int main(void)
{
char array[3][4] = {"abc","def","ghi"};
printf("%s,%s\n", *(array+1) ,array[1] );
printf("%c,%c\n", *(*(array+1)+1),array[1][1] );
return 0;
}
int main(void)
{
char array[2][3][3] =
{
{"12","34","56"},
{"78","90","ab"}
};
char (*p)[3][3] = array;
printf("%s,%s\n", *((*(p + 1)) + 1), p[1][1]);
return 0;
}
数组的初始化会执行多条隐式赋值语句,那么问题在于,在函数体内,当数组元素很多时,这样的初始化会消耗很多时间,你需要考虑的是能否将数组声明为 static 静态变量,来避免执行流每次进入该函数体内时对数组进行初始化
数组的存储顺序总是根据最右边的下标率先变化:
如int array[2][3] = {0,1,2,3,4,5}
,最左边为array[0][0],往后依次为array[0][1],array[0][2],array[1][0]…
部分初始化,其他默认初始化为0;以下两两等价:
//-----------1-------------
int array[4] = {1};
int array[4] = {1,0,0,0};
//-----------2-------------
int array[2][2][3] = {1,2};
int array[2][2][3] = {1,2,0, 0,0,0, 0,0,0, 0,0,0};
//-----------3-------------
int array[2][2][3] = {
{
{
{1}
}
},
{
{
{2}
}
}
};
int array[2][2][3] = {1,0,0, 0,0,0, 2,0,0, 0,0,0};
一维数组,若声明中未给出数组长度,则编译器将长度设置为刚好能够容纳所有初始值的长度,而不是弹性可变的
int main(void)
{
char str[] = {'a','b','c'};
printf("%d\n",sizeof(str));
str[3] = 'd';
printf("%d\n",sizeof(str));
return 0;
}
结果为:
这里初始化后数组长度就固定为3了,str[3] = 'd';
这条语句实际上是非法的,即使编译器未报错,它已经访问了数组外的地址,并且改变了该地址的值,可能会出现问题
多维数组,只有第一维可以不给定长度,剩余几维均要提供长度
void func(char *array);
void func(char array[]);
不用指出数组长度,因为传入的是地址值二维为例:
void func(char (*array)[10]);
void func(char array[][10]);
第一维不用给出长度,但后面的维度都需要给出个人认为你只需要记住几个操作符的优先级便能很好地区分:从上到下优先级降低
1 ( ) 聚组
2 [ ] 下标引用
3 * 间接访问
例如:
int main(void)
{
char const *array[] = {
"ab",
"abc",
"12",
"123",
"ab12",
NULL
};
printf("%d,%d",sizeof(array),sizeof(array[0]));
return 0;
}
结果为:
经验:在初始化char型指针数组时,将最后一个元素定为NULL有时候是很方便的,例如你需要遍历这个数组,可以将NULL作为结束标志,而无需知道数组长度