探索指针(2)-C语言

目录

1.数组名的理解

2.使用指针访问数组

3.一维数组传参的本质

4.二级指针

5.指针数组

6.指针数组模拟二维数组


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] = 5

After modifying array elements using pointer:
myArray[0] = 10
myArray[1] = 20
myArray[2] = 3
myArray[3] = 4
myArray[4] = 5

代码解释

  1. 声明和初始化数组:我们声明并初始化一个包含5个整数的数组 myArray
  2. 声明指针:我们声明一个指针 ptr 并使其指向数组 myArray 的第一个元素。
  3. 使用指针访问数组元素:通过指针算术(如 *(ptr + i))访问数组元素。这里的 *(ptr + i) 等价于 myArray[i]
  4. 修改数组元素的值:通过指针修改数组元素的值。例如,*(ptr + 0) = 10 将第一个元素的值修改为10。
  5. 打印修改后的数组元素:直接使用数组名访问并打印修改后的数组元素。

更加灵活的操作

使用指针访问数组元素提供了更大的灵活性和更高的效率,特别是在处理动态数组和复杂数据结构时。了解并掌握指针和数组的关系对于深入理解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

代码解释

  1. 声明和初始化变量:声明并初始化一个整数变量 a,一个指向 a 的指针 ptr,以及一个指向 ptr 的二级指针 pptr
  2. 打印变量的值:通过 aptrpptr 打印 a 的值。
  3. 修改变量的值:通过二级指针 pptr 修改 a 的值。
  4. 打印修改后的值:打印修改后的 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

代码解释

  1. 声明和初始化字符串数组:声明一个字符指针数组 strArray 并初始化为一组字符串。
  2. 计算数组大小:使用 sizeof 计算数组的大小。
  3. 打印数组元素:使用循环打印每个字符串。

图示指针数组:

 

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 个元素。

 通过这个示例,展示了如何使用指针数组来模拟二维数组,并通过嵌套循环访问和打印数组元素。这样的方法特别适用于需要固定大小的数组,而不需要动态分配内存的情况。

 

以上便是此次所有的内容,对你有帮助的话,请留下你的点赞吧!!!

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值