1、数组及数组的访问
1.1 数组的存储方式
在内存中,数组是一块连续的区域。数组的存储结构有如下特点:
(1)数组中的元素是同质的数据;
(2)索引从0开始;
(3)数组在内存中的存储单元是连续的;
(4)数据元素是按顺序连续存放的;
比如数组:
int a[6] = {1, 2, 3, 3, 5, 6};
它在内存中的存储结构是这样的:
1.2 数组指针
访问数组可以用下标法访问,比如a[0]访问数组的第一个元素;也可以通过指针访问。在C语言中,数组名代表数组首个元素的地址。比如a与&a[0]是等价的。所以,我们可以通过*a访问数组的第1个元素,*(a+1)访问数组的第2个元素,等。
在用数组名时,有几个地方不能把数组名当首地址看:
(1)sizeof(a)求数组空间长度时。这里的a代表整个数组。注意这个sizeof()不是函数,是关键字,用来计算数据在内存中使用的字节数,是在编译时确定的。比如,sizeof(a) = 6*4 = 24个字节。
(2)&a表示取数组a的地址,跟&a[0]是不同的。
a只是代表数组首元素的地址,是指向数组首元素的指针;和指向这个数组的指针不是一码事。在指针变量的定义时我们知道,指针变量包含数据类型和指针名字,所以指向数组的指针的数据类型是数组,其指针值+1就代表地址往下移动 “每个数组元素字节*数组长度” 的字节数。而a+1指针只是往后移动1个数组元素字节。
int a[6] = {1, 2, 3, 3, 5, 6};
int *pi = &a[0];
pi = &a[1]; //可以的
pi = &a; //会报错,因为pi是个int指针,可以指向数组里的元素,但不能指向整个数组
int (*pi2)[6]; //指向包含6个int元素的数组的指针
pi2 = &a; //这么写就可以了
(3)数组赋值的错误方式:
char str[10];
str = "abcd"; //报错
int arr_i[10];
arr_i = { 1, 2, 3 }; //报错
正确的赋值方式:
char str[10];
strcpy(str, "abc");
int arr_i[10];
for (int i = 0; i < 10; i++) {
arr_i[i] = i;
}
或者在定义的时候初始化:
char str[10] = "abcd";
int arr_i[10] = { 1, 2, 3 };
2、数组指针相关运算
2.1 基础运算规则
假如定义了下面的数组和数组指针:
int a[6] = {1, 2, 3, 3, 5, 6};
int *p1 = &a[0];
int *p2 = &a[2];
(1)p1表示数组的首地址,则p1+i表示数组元素a[i]的地址。
(2)*p1表示数组元素a[0]的值,*(p1+i)表示数组元素a[i]的值。
(3)p2 - 1表示上一个数组元素,即a[1];p2 + 1表示下一个数组元素,即a[3]。
(4)p2 - p1是2个地址之差。
int a[6] = { 1, 2, 3, 3, 5, 6 };
int* p1 = &a[0];
int* p2 = &a[2];
printf("%p\n", p1);
printf("%p\n", p2);
int size1 = (char)p2 - (char)p1; //转成char后的差才是字节数
int size2 = p2 - p1;
printf("size1 = %d, size2 = %d\n", size1, size2);
输出结果如下:
这里需要注意,因为p2、p1是地址,所以减之前要转成char,这样减的差值才是地址字节数。
2.2 数组指针运算应注意的几个问题
int a[6] = {1, 2, 3, 3, 5, 6};
int *p = a;
(1)*p++
从右往左读。因此它等价于*(p++)。++在变量后表示先返回变量的值,然后再+1。比如i++表示先返回i的值,然后i+1。所以*(p++)先返回*p的值,然后指针再加1,指向p+1,即a[2]的位置。
(2)*(p++)和*(++p)
我们知道i++表示先返回i的值,然后i+1;++i表示先i+1,然后返回i的值。所以*(++p)先把指针往下移动1个数组元素,然后再返回这个元素的值,即返回a[2]的值。
(3)++(*p)
从右往左读。先得到*p的值,即a[1]的值。然后把a[1]的值加1。
3、用数组名作函数参数
数组名作函数参数可以写成下面的形式:
func(int arr[], int len)
//或
func(int* parr, int len)
上面2种方式是等价的,另外要记住数组作形状参时,必须同时传入数组的长度,否则可能会造成读取数组越界。这里有人可能会有疑问,不是可以在函数里用sizeof(arr)去获取数组的长度吗?不行的,因为形参int arr[]其实传过来的是实参数组的首地址。所以函数里的sizeof(arr)返回的是指针的长度,在32位系统中是4个字节,64位系统中是8个字节。
3.1
(1)数组表示法
void print(int arr[], int len) {
for (int i = 0; i < len; i++) {
//printf("arr[%d] = %d\n", i, *(arr + i)); //这2种打印方法都可以
printf("arr[%d] = %d\n", i, arr[i]);
}
}
int main()
{
int a[6] = { 1, 2, 3, 3, 5, 6 };
print(a, 6);
return 0;
}
(2)指针表示法
void print(int *arr, int len) {
for (int i = 0; i < len; i++) {
printf("arr[%d] = %d\n", i, *(arr + i));
//printf("arr[%d] = %d\n", i, arr[i]); //这2种打印方法都可以
}
}