17.C语言数组
一维数组
在 C 语言里,一维数组指针指的是指向一维数组的指针。它和普通指针不同,普通指针指向单个变量,而一维数组指针指向的是一整个一维数组。一维数组指针存储的是数组的起始地址,并且它的类型和所指向的数组类型相匹配。
声明与初始化
声明一维数组指针的一般格式为:
数据类型 (*指针变量名)[数组长度];
这里的括号不能省略,因为[]
的优先级比*
高,若不使用括号,声明的就会是一个指针数组。下面是具体的声明和初始化示例:
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int (*ptr)[5] = &arr; // 声明并初始化一个指向包含5个int元素的数组的指针
return 0;
}
在上述代码中,ptr
是一个指向包含 5 个 int
类型元素的数组的指针,通过 &arr
把数组 arr
的地址赋给了 ptr
。
数组本质上不是一个指针,我们可以通过sizeof进行查看
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void array_test01() {
int array[5] = {1,2,3,4,5};
//一维数组是不是指针?
printf("array siof: %d\n",sizeof(array));
}
int main() {
array_test01();
return EXIT_SUCCESS;
}
运行结果
数组赋值给指针
我们看以下案例:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void array_test01() {
int array[5] = {1,2,3,4,5};
int* p = NULL;
p = &array;
printf("array index: %d\n", *(p+0));
}
int main() {
array_test01();
return EXIT_SUCCESS;
}
运行结果
从提示来看,两者赋值是不支持的。两者的的指针其实不是一个等价指针,可以看下面警告,间接级别不同。原因是两者之间的步长并不是等价。
数组的步长
void array_test02() {
int array[5] = { 1,2,3,4,5 };
printf("array index: %d\n",&array);
printf("array index: %d\n",&array + 1);
}
int main() {
array_test02();
return EXIT_SUCCESS;
}
从&array+1后直接就跳转到了最后一位的地址。如果是数组取地址的话,取的是整个数组的指针,直接跳转,会跳转到最后
向以下案例就属于等价的。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void array_test01() {
int array[5] = {1,2,3,4,5};
int* p = array;
printf("array index: %d\n", *(p+0));
}
int main() {
array_test01();
return EXIT_SUCCESS;
}
指针常量
指针的指向是不可修改的,但是可以修改指针内部元素
指针放负数,原理
我们的数组是可以放入负数的。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void array_test01() {
int array[5] = {1,2,3,4,5};
int* p = array;
p = p + 3;
printf("array: %d\n", p[-1]);
}
int main() {
array_test01();
return EXIT_SUCCESS;
}
运行结果
逻辑
首先当前p的偏移位置在第3位,然后,p[-1] 就是第2 。 其实是等价的
定义一个数组的指针
方式1
直接通过数组赋地址给指针,对数组名称取地址,获取的是指针步长,是整个数组的长
void array_test01() {
int array[5] = {1,2,3,4,5};
int* p = &array; //这种方式并不是数组指针,对数组名称取地址,获取的是指针步长,是整个数组的长度
}
如果需要定义一个数组指针,首先定义出数组的类型,在通过类型创建数组的指针。
void array_test01() {
int array[5] = {1,2,3,4,5};
typedef int(ARRAY_TYPE)[5]; //ARRAY_TYPE是一个数据类型,代表有5个int类型的数组元素
ARRAY_TYPE *p = &array;
for (int i = 0; i < 5;i++) {
//首先是p的地址,如果不把p解引用,那会打印的是地址
printf("%d\n",(*p)[i]);
}
}
方式2
先定义数组指针的类型,在创建数组指针的变量
void array_test01() {
int array[5] = {1,2,3,4,5};
typedef int(*ARRAY_TYPE)[5]; //先定义数组指针的类型,在创建数组指针的变量
ARRAY_TYPE p = &array;
for (int i = 0; i < 5;i++) {
//首先是p的地址,如果不把p解引用,那会打印的是地址
printf("%d\n",(*p)[i]);
}
}
与上面方法有所不同,定义规则直接就变了,直接一步到位。
方式3
直接创建数组指针变量
void array_test01() {
int array[5] = {1,2,3,4,5};
int (*p)[5] = &array; // 直接创建数组指针变量
for (int i = 0; i < 5;i++) {
//首先是p的地址,如果不把p解引用,那会打印的是地址
printf("%d\n",(*p)[i]);
}
}
案例
案例1
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void printf_array(int arr[],int len) {// int arr[],等价与int * arr
for (int i = 0; i < len; i++) {
printf("%d\n",arr[i]); //给人看
//printf("%d\n",*(arr+i));//给机器看
}
}
void array_test01() {
int array[5] = {1,2,3,4,5};
int arr_len = sizeof(array) / sizeof(array[0]);
printf_array(array,arr_len);
}
int main() {
array_test01();
return EXIT_SUCCESS;
}
案例2
借助数组指针,可以通过偏移和间接引用操作符来访问数组元素。
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int (*ptr)[5] = &arr;
// 访问数组的第三个元素
printf("数组的第三个元素是: %d\n", (*ptr)[2]);
return 0;
}
在这个例子中,(*ptr)
先对指针 ptr
进行解引用,得到数组本身,然后 [2]
访问数组的第三个元素(下标从 0 开始)。
案例3
一维数组指针能够作为函数参数传递,这样可以在函数内部对数组进行操作。
#include <stdio.h>
// 函数接收一个指向包含5个int元素的数组的指针
void printArray(int (*ptr)[5]) {
for (int i = 0; i < 5; i++) {
printf("%d ", (*ptr)[i]);
}
printf("\n");
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int (*ptr)[5] = &arr;
printArray(ptr);
return 0;
}
在上述代码中,printArray
函数接收一个指向包含 5 个 int
元素的数组的指针作为参数,在函数内部通过该指针访问数组元素并打印。
案例4
在处理二维数组时,可利用一维数组指针来遍历二维数组的每一行。
#include <stdio.h>
int main() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 声明一个指向包含4个int元素的数组的指针
int (*ptr)[4];
for (ptr = arr; ptr < arr + 3; ptr++) {
for (int i = 0; i < 4; i++) {
printf("%d ", (*ptr)[i]);
}
printf("\n");
}
return 0;
}
在这个例子中,ptr
是一个指向包含 4 个 int
元素的数组的指针。外层循环让 ptr
依次指向二维数组的每一行,内层循环遍历当前行的每个元素。
二维数组
在 C 语言中,二维数组指针是指向二维数组的指针。二维数组在内存中是按行优先的顺序连续存储的,二维数组指针可以用来指向这个连续的内存块,并且可以方便地对二维数组进行操作。
从本质上来说,二维数组指针指向的是二维数组的一行,它存储的是二维数组某一行的起始地址
声明二维数组指针
声明二维数组指针的一般形式为:
数据类型 (*指针变量名)[列数];
这里的括号是必需的,因为[]
的优先级高于*
,不加括号就会声明成指针数组。例如,声明一个指向包含 3 个int
类型元素的数组的指针:
int (*ptr)[3];
定义二维数组
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void array_test01() {
//格式1
int array1[3][3] = {
{1,2,3},
{4,5,6},
{7,8,9}
};
//格式2
int array2[3][3] = {1,2,3,4,5,6,7,8,9};
//格式3
int array3[][3] = {1,2,3,4,5,6,7,8,9};
}
int main() {
array_test01();
return EXIT_SUCCESS;
}
二维数组指针
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void array_test01() {
int array[3][3] = {
{1,2,3},
{4,5,6},
{7,8,9}
};
int (*p)[3] = array; //二维数组名称 指向 第一个一维数组的指针
//特殊情况
printf("sizeof: %d",sizeof(array)); //9个 int类型,输出36
//创建二维数组指针
int (*p2)[3][3] = &array;
}
int main() {
array_test01();
return EXIT_SUCCESS;
}
指向二维数组并访问元素
#include <stdio.h>
int main() {
int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// 声明并初始化二维数组指针
int (*ptr)[3] = arr;
// 访问第一行第二列的元素
printf("第一行第二列的元素: %d\n", ptr[0][1]);
// 另一种访问方式
printf("第一行第二列的元素: %d\n", *(*(ptr + 0) + 1));
return 0;
}
解释:
int (*ptr)[3] = arr;
:这里arr
代表二维数组首行的地址,将其赋值给ptr
,ptr
就指向了二维数组的第一行。ptr[0][1]
:ptr[0]
表示第一行的首地址,再加上偏移量1
就访问到第一行第二列的元素。*(*(ptr + 0) + 1)
:ptr + 0
指向第一行,*(ptr + 0)
得到第一行首地址,*(ptr + 0) + 1
得到第一行第二列元素的地址,最后再解引用就得到该元素的值。
二维数组输出
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
//以下三种方式都是等价的
//void arrayPrintf(int arr[3][3],int row,int col) {
//void arrayPrintf(int arr[][3],int row,int col) {
void arrayPrintf(int (*arr)[3], int row, int col) {
for (size_t i = 0; i < row; i++)
{
for (size_t j = 0; j < col; j++)
{
//printf("%d ",arr[i][j]); //给人看
printf("%d ",*(*(arr + i)+j)); //机器看
}
printf("\n");
}
}
void array_test01() {
int array[3][3] = {
{1,2,3},
{4,5,6},
{7,8,9}
};
arrayPrintf(array, 3, 3);
}
int main() {
array_test01();
return EXIT_SUCCESS;
}
输出结果
动态分配二维数组并使用指针操作
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 2;
int cols = 3;
// 动态分配二维数组
int (*arr)[3] = (int (*)[3])malloc(rows * sizeof(*arr));
if (arr == NULL) {
printf("内存分配失败\n");
return 1;
}
// 初始化二维数组
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
arr[i][j] = i * cols + j;
}
}
// 打印二维数组
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
// 释放内存
free(arr);
return 0;
}
int (*arr)[3] = (int (*)[3])malloc(rows * sizeof(*arr));
:使用malloc
函数动态分配了一个包含rows
行、cols
列的二维数组。sizeof(*arr)
表示一行的大小。- 之后通过双重循环对二维数组进行初始化和打印操作。
- 最后使用
free(arr)
释放动态分配的内存,避免内存泄漏。