目录
1.数组名的理解
数组名的本质
在C语言中,数组名本质上是一个指向数组第一个元素的指针。数组在内存中是连续存储的,而数组名就是数组首元素的地址。例如,假设我们有一个数组 int myArray[5];
,则 myArray
是指向 myArray[0]
的指针。
下面通过代码进行验证:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("arr = %p\n", arr);
return 0;
}
输出结果:
&arr[0] = 000000CC0177FB08
arr = 000000CC0177FB08
很明显他们确实是相同的.
数组名作为指针
数组名可以看作是一个常量指针,它指向数组的第一个元素。这意味着我们可以使用指针算术来访问数组中的元素。例如:
int *ptr = myArray; // ptr指向数组的第一个元素
printf("%d\n", *ptr); // 输出第一个元素的值,结果是10
printf("%d\n", *(ptr+1)); // 输出第二个元素的值,结果是20
传递数组给函数
当数组作为参数传递给函数时,实际上传递的是数组的指针。例如:
void Modify(int* arr, int length)
{
int i = 0;
for (i = 0; i < length; i++)
{
arr[i] = 0;
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
Modify(arr, sizeof(arr) / sizeof(arr[0]));
int i = 0;
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
return 0;
}
输出结果
0 0 0 0 0 0 0 0 0 0
代码分析:
我们通过传址方式将数组传入修改函数中,可以在函数内直接修改数组arr中的内容
特列:
sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,
单位是字节
代码示例:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sizeof(arr));
return 0;
}
输出:
40
&数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素
的地址是有区别的)
代码示例:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}
代码结果:
&arr[0] = 000000084B4FF858
arr = 000000084B4FF858
&arr = 000000084B4FF858
这时候,你发现他们三个都是一样的值,因为在这里看不出来,其实&arr是一个数组指针,我们通过+1的方式,可以观察到他们的差别.
代码示例:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0]+1);
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);
return 0;
}
输出结果:
&arr[0] = 000000A9F1EFFAD8
&arr[0]+1 = 000000A9F1EFFADC
arr = 000000A9F1EFFAD8
arr+1 = 000000A9F1EFFADC
&arr = 000000A9F1EFFAD8
&arr+1 = 000000A9F1EFFB00
这里我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是
首元素的地址,+1就是跳过一个元素。
但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。
2.使用指针访问数组
数组名和指针的关系
在C语言中,数组名本质上是一个指向数组第一个元素的指针。例如,假设有一个数组 int myArray[5];
,则 myArray
是指向 myArray[0]
的指针。
使用指针访问数组元素
我们可以通过指针算术来访问数组中的元素。指针算术是指在指针的基础上进行加法或减法操作,从而访问数组中的不同元素。
示例代码:
以下是一个完整的示例,展示了如何使用指针访问数组元素:
#include <stdio.h>
int main() {
// 声明并初始化一个数组
int myArray[5] = {1, 2, 3, 4, 5};
// 声明一个指向数组第一个元素的指针
int *ptr = myArray;
// 使用指针访问和修改数组元素
printf("Using pointer to access array elements:\n");
for (int i = 0; i < 5; i++) {
printf("myArray[%d] = %d\n", i, *(ptr + i)); // 使用指针访问数组元素
}
// 修改数组元素的值
*(ptr + 0) = 10;
*(ptr + 1) = 20;
printf("\nAfter modifying array elements using pointer:\n");
for (int i = 0; i < 5; i++) {
printf("myArray[%d] = %d\n", i, myArray[i]); // 直接访问数组元素
}
return 0;
}
代码输出:
Using pointer to access array elements:
myArray[0] = 1
myArray[1] = 2
myArray[2] = 3
myArray[3] = 4
myArray[4] = 5After modifying array elements using pointer:
myArray[0] = 10
myArray[1] = 20
myArray[2] = 3
myArray[3] = 4
myArray[4] = 5
代码解释
- 声明和初始化数组:我们声明并初始化一个包含5个整数的数组
myArray
。 - 声明指针:我们声明一个指针
ptr
并使其指向数组myArray
的第一个元素。 - 使用指针访问数组元素:通过指针算术(如
*(ptr + i)
)访问数组元素。这里的*(ptr + i)
等价于myArray[i]
。 - 修改数组元素的值:通过指针修改数组元素的值。例如,
*(ptr + 0) = 10
将第一个元素的值修改为10。 - 打印修改后的数组元素:直接使用数组名访问并打印修改后的数组元素。
更加灵活的操作
使用指针访问数组元素提供了更大的灵活性和更高的效率,特别是在处理动态数组和复杂数据结构时。了解并掌握指针和数组的关系对于深入理解C语言编程非常重要。
3.一维数组传参的本质
下面我将通过一个代码示例,让你对数字传参有一个更加透彻的理解
代码示例:
void print_length(int arr[])
{
printf("%d", sizeof(arr) / sizeof(arr[0]));
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int length = sizeof(arr) / sizeof(arr[0]);
printf("%d ", length);
print_length(arr);
return 0;
}
代码输出:
10 2
通过输出,我们发现在函数中使用sizeof(arr) / sizeof(arr[0])计算得到的结果是2
这是因为,arr此时是一个地址,地址在下×86环境下字节大小为8,而arr[0]是整型,大小为4,所以计算得出为2,我们在传参的过程为了方便,可以将参数写成int arr[]但实际是指针。
4.二级指针
引言:
整型变量的地址我们用int*来接收,那么整型指针变量的地址我们用什么来接收呢❓
这时我们就要引入二级指针int** ,以此类推,自然还有三级指针,四级指针等等。
二级指针(double pointer)是指向另一个指针的指针。通过二级指针,我们可以间接地引用和操作其他指针,从而访问内存中的数据。在C语言中,二级指针的声明、使用和操作都是很常见且重要的。
二级指针的声明
二级指针的声明如下:
数据类型 **指针名;
例如,声明一个指向 int
类型指针的二级指针:
int **p;
二级指针的使用
指向指针
一个二级指针指向一个一级指针。例如:
int a = 10;
int *ptr = &a; // ptr 是一个指针,指向变量 a
int **pptr = &ptr; // pptr 是一个二级指针,指向指针 ptr
在这个例子中:
a
是一个整数变量。ptr
是一个指向a
的指针。pptr
是一个指向ptr
的二级指针。
通过图示,将可以更加清晰的理解二级指针
访问和修改数据
通过二级指针,我们可以间接地访问和修改数据。例如:
#include <stdio.h>
int main() {
int a = 10;
int *ptr = &a; // ptr 是一个指针,指向变量 a
int **pptr = &ptr; // pptr 是一个二级指针,指向指针 ptr
// 打印变量 a 的值
printf("Value of a: %d\n", a);
printf("Value of a using ptr: %d\n", *ptr);
printf("Value of a using pptr: %d\n", **pptr);
// 修改变量 a 的值
**pptr = 20;
// 打印修改后的值
printf("Value of a after modification: %d\n", a);
return 0;
}
代码输出:
Value of a: 10
Value of a using ptr: 10
Value of a using pptr: 10
Value of a after modification: 20
代码解释
- 声明和初始化变量:声明并初始化一个整数变量
a
,一个指向a
的指针ptr
,以及一个指向ptr
的二级指针pptr
。 - 打印变量的值:通过
a
、ptr
和pptr
打印a
的值。 - 修改变量的值:通过二级指针
pptr
修改a
的值。 - 打印修改后的值:打印修改后的
a
的值,以验证修改是否成功。
5.指针数组
引言:
整型数组是存放整型的数组,字符数组是存放字符的数组
那么指针数组顾名思义便是存放指针的数组
指针数组的声明
指针数组的声明语法如下:
数据类型 *数组名[数组大小];
例如,声明一个包含5个整型指针的数组:
int *ptrArray[5];
指针数组的初始化
指针数组可以在声明时初始化,例如:
#include <stdio.h>
int main() {
// 声明并初始化一个指针数组,每个元素指向一个整数变量
int a = 10, b = 20, c = 30;
int *ptrArray[3] = {&a, &b, &c};
// 打印指针数组中每个指针指向的值
for (int i = 0; i < 3; i++) {
printf("Value of ptrArray[%d]: %d\n", i, *ptrArray[i]);
}
return 0;
}
示例:使用指针数组存储字符串
指针数组常用于存储字符串数组。每个元素都是一个指向字符的指针(即字符串)。
#include <stdio.h>
int main() {
// 声明一个指针数组并初始化为字符串数组
char *strArray[] = {"Apple", "Banana", "Cherry", "Date"};
// 计算数组的大小
int arraySize = sizeof(strArray) / sizeof(strArray[0]);
// 打印字符串数组的每个元素
for (int i = 0; i < arraySize; i++) {
printf("strArray[%d]: %s\n", i, strArray[i]);
}
return 0;
}
代码输出:
strArray[0]: Apple
strArray[1]: Banana
strArray[2]: Cherry
strArray[3]: Date
代码解释
- 声明和初始化字符串数组:声明一个字符指针数组
strArray
并初始化为一组字符串。 - 计算数组大小:使用
sizeof
计算数组的大小。 - 打印数组元素:使用循环打印每个字符串。
图示指针数组:
6.指针数组模拟二维数组
代码示例:
#include <stdio.h>
int main()
{
// 声明并初始化三个一维数组
int arr1[] = {1, 2, 3, 4, 5};
int arr2[] = {2, 3, 4, 5, 6};
int arr3[] = {3, 4, 5, 6, 7};
// 声明一个指针数组,并将这三个数组的首地址存储在指针数组中
int* parr[3] = {arr1, arr2, arr3};
// 声明两个整型变量i和j用于循环控制
int i = 0;
int j = 0;
// 使用嵌套循环遍历指针数组并打印每个元素的值
for(i = 0; i < 3; i++) // 外层循环遍历指针数组中的每个指针
{
for(j = 0; j < 5; j++) // 内层循环遍历每个数组中的元素
{
// 使用指针数组访问并打印二维数组的元素
printf("%d ", parr[i][j]);
}
printf("\n"); // 每打印完一行元素后换行
}
return 0; // 程序执行完毕,返回0
}
其中有段代码单独拿出来讲解一番,对你通过指针来操作数组,会使你对指针操控数组有更深的理解。
for(i = 0; i < 3; i++) // 外层循环遍历指针数组中的每个指针
{
for(j = 0; j < 5; j++) // 内层循环遍历每个数组中的元素
{
printf("%d ", parr[i][j]); // 使用指针数组访问并打印二维数组的元素
}
printf("\n"); // 每打印完一行元素后换行
}
外层循环控制遍历指针数组 parr
,每次迭代 i
从0增加到2,对应 parr
的每个元素(即每个一维数组的首地址)。 内层循环控制遍历 parr[i]
指向的数组中的每个元素,j
从0增加到4,对应每个一维数组的元素。
parr[i][j]
等价于 (*(parr + i))[j]
,即首先通过 parr
数组找到第 i
个指针,然后访问该指针指向的数组的第 j
个元素。
通过这个示例,展示了如何使用指针数组来模拟二维数组,并通过嵌套循环访问和打印数组元素。这样的方法特别适用于需要固定大小的数组,而不需要动态分配内存的情况。
以上便是此次所有的内容,对你有帮助的话,请留下你的点赞吧!!!