数组和指针的关系比较密切,这里再总结一下这个知识点。由问题引入话题:
1. char a[] 和char *a等价吗?
不是。 数组定义char a[6]请求预留6个字符的位置,并用名称a表示。也即:有一个用a标识的位置,可以放入6个字符。 而指针声明char *p :请求一个位置放置一个指针,用名称p表示。这个指针可以指向任何字符或任何连续的字符,或者哪里都不指。通过图像来说明: 若有下面语句:它们在内存中的表现形式如下(当然,箭头是我们人为添加的): 注意:数组不能被赋值。但是指针可以。在使用指针过程中一定要清楚指针本身和指针指向的内容,尤其是指针运算。指针运算总是居于所指对象的大小的。 2. 如何理解“指针和数组等价”? 在C语言中知识指针算数和数组下标运算等价,指针和数组是不同的。那么,在作为函数形参的数组和指针为何可以互换呢?其实,给函数传入数组只是一种便利的做法。在这个过程中,数组会马上退化为指针,事实上,数组从来没有被传入函数。 例如:char a[] = "hello"; char *p = "world";
void fun(int a[]) {...}
在编译器里都被当做指针来处理。因为在传入数组的时候,函数接受到的正是指针。 注意:这种转换仅限于函数形参的声明,在别的地方并不适用。
3. 如何动态分配数组? (1)动态分配一维数组:用指向malloc分配的内存的指针来高效模拟数组。 int *dArray = (int *)malloc(10 * sizeof(int)); (2)动态分配二维数组: 方法一:分配一个指针数组,然后把每个指针初始化为动态分配的“行”。
方法二:让数组的内容连续。但在分配行的时候要使用一点指针运算:内存布局:int **arr1 = (int *)malloc(rows * sizeof(int *)); for (i = 0; i < rows; i++) { arr1[i] = (int *)malloc(cols * sizeof(int)); }
内存布局:int **arr2 = malloc(rows * sizeof(int *)); arr2[0] = malloc(rows * cols * sizeof(int)); for (i = 1; i<rows; i++) { arr2[i] = arr2[0] + i * cols; }
方法三:用一个动态分配的一维数组来模拟二维数组: int *arr3 = malloc(rows * cols * sizeof(int)); 但是在使用是偶必须手工计算下标,如:arr3[i * cols + j]访问第i、j个元素。 方法四:使用数组指针: int (*arr4)[COLS] = malloc(rows * sizeof(*arr4));
或,
int (*arr5)[ROWS][COLS] = malloc(sizeof(*arr5));
这种方法的语法比较可怕,并且运行时最多只能确定一维。 4. 如何释放前面动态分配的数组? 对于arr1和arr2:
for (i=0; i<rows; i++) free((void *)arr1[i]); free((void *)arr1);
注意:释放的原则:每个malloc()都必须有一个对应的free();
5. 如何给函数传递一个二维数组? 数组退化为指针的规则不能递归应用。数组的数组(二维数组)退化为数组的指针,而不是指针的指针。 若想函数传递二维数组:
int arr[ROWS][COLS]; fun(arr);那么,函数的声明也必须匹配:或者void fun(int a[][COLS]) {...}
void fun(itn (*pArr)[COLS]) /* pArr is a pointer to an array */ { ... }