深度剖析C语言指针和数组的关系(新手必看)
学习C语言数组时,我们学习了使用下标来访问数组元素。看一个使用下标访问数组元素的示例。
我们可以使用取地址运算符
在 printf() 函数中,通过对指针 p 进行加法操作,指针会根据数组元素的步长进行移动。例如,*(p + 1) 将访问数组中的第二个元素,*(p + 2) 将访问第三个元素,以此类推。最后,该程序输出数组中的所有元素。运行结果为:
不使用括号,
C语言有一种更加简便的方法来获取数组的首地址,那就是直接使用数组的名字表示数组的首地址。所以当你使用数组名时,实际上就是获取了数组的首地址。
下面是一个使用数组名获取数组首地址的示例。
由于值是相同的,因此我们可以尝试将数组名赋值给一个指针类型,具体代码如下:
那么,有的同学可能会认为数组名的类型就是一个指向元素的指针。为了验证这个猜想,我们使用 sizeof 测量数组名的大小。如果数组名是一个指针,那么它的大小在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
程序用 64 位系统进行编译。运行结果为:
这段程序演示了 C语言中指针和数组的一些概念,接下来我们对其进行详细分析:
首先,p 是一个指针,通过前面的学习我们知道了 64 位系统的指针大小是 8 字节,这个结果是毫无疑问的;
其次,arr 的大小为 20,因为它是一个包含 5 个整数的数组。整数(int 类型)占用 4 字节。因此,arr 的大小是 5 个整数,每个整数占用 4 字节,总共是 5 × 4 = 20 字节。
最后,arr + 1 的大小是 8,因为这里涉及一个指针运算。arr 是一个数组,在表达式中被解释为指向数组第一个元素的指针。当你对数组名执行加法操作时,结果是一个指针。在 64 位系统上,指针占用 8 字节。因此,在这种情况下,sizeof(arr + 1) 计算的是一个指针的大小,而不是数组本身的大小。这就是为什么 sizeof(arr + 1) 的值为 8。
这里有一个重要的知识点要强调一下,当数组名 arr 出现在一个表达式当中,数组名 arr 将会被转换为指向数组第一个元素的指针。但是,这个规则有两个例外:
也就是说,数组名 arr 的类型其实是
假设我们有如下整数数组:
1) 使用数组名和下标访问元素。
2) 使用指针和偏移访问元素。
3) 使用数组名和偏移访问元素。
4) 使用数组表示法访问指针所指向的连续内存空间。
在上面的例子中,我们使用了数组名 arr 和指针 p 以不同的方式访问数组元素。需要注意的是,数组名和指针虽然在很多情况下可以互换使用,但在本质上是不同的。
现在我们学会了访问数组元素的两种办法:
例如,要访问第 2 个元素,可以使用
通过这些例子,我们可以看到指针和数组在很多情况下可以相互替换。但是,我们应该明确它们之间的区别,以避免在编程时产生错误。总结如下:
理解这些概念后,你将能够灵活地运用指针和数组来编写高效且易于理解的代码。在实际编程中,合理地运用指针和数组有助于简化代码结构,提高程序的执行效率。
#include <stdio.h> int main() { int arr[5] = {111, 222, 333, 444, 555}; // 定义一个包含5个整数的数组 printf("%d\n", arr[0]); // 第1个元素 printf("%d\n", arr[1]); // 第2个元素 printf("%d\n", arr[2]); // 第3个元素 printf("%d\n", arr[3]); // 第4个元素 printf("%d\n", arr[4]); // 第5个元素 return 0; }由于数组元素在内存中的存储是连续的,因此第一个元素的首地址就是整个数组的首地址。
我们可以使用取地址运算符
&
来获取第一个元素的首地址以及空间大小,从而获得一个 int* 类型的指针。例如:
int *p = &arr[0]; // 从第 1 个元素获取数组首地址 p; // 指向第 1 个元素 p + 1; // 指向第 2 个元素 p + 2; // 指向第 3 个元素 p + 3; // 指向第 4 个元素 p + 4; // 指向第 5 个元素通过取值运算符
*
,我们可以使用指针中的首地址和空间大小来访问或修改数组元素。具体代码如下:#include <stdio.h> int main() { int arr[5] = {111, 222, 333, 444, 555}; int *p = &arr[0]; printf("%d\n", *p); // 第1个元素 printf("%d\n", *(p + 1)); // 第2个元素 printf("%d\n", *(p + 2)); // 第3个元素 printf("%d\n", *(p + 3)); // 第4个元素 printf("%d\n", *(p + 4)); // 第5个元素 return 0; }这段代码的主要目的是通过指针操作来访问和输出数组中的元素。它首先定义了一个整数数组 arr 并初始化了 5 个整数值,然后创建了一个整型指针 p 并将其指向数组的首地址(即第一个元素的地址),最后使用了指针运算访问并输出了数组中的每个元素。
在 printf() 函数中,通过对指针 p 进行加法操作,指针会根据数组元素的步长进行移动。例如,*(p + 1) 将访问数组中的第二个元素,*(p + 2) 将访问第三个元素,以此类推。最后,该程序输出数组中的所有元素。运行结果为:
111
222
333
444
555
不使用括号,
*p
的值为 111,*p + 1
的结果为 112。使用括号,(p + 1)
使得首地址移动到第二个元素,*(p + 1)
得到结果为 222。C语言有一种更加简便的方法来获取数组的首地址,那就是直接使用数组的名字表示数组的首地址。所以当你使用数组名时,实际上就是获取了数组的首地址。
下面是一个使用数组名获取数组首地址的示例。
#include <stdio.h> int main() { int arr[5] = {111, 222, 333, 444, 555}; printf("arr = %llu\n", arr); printf("&arr[0] = %llu", &arr[0]); }运行结果为:
arr = 6487552
&arr[0] = 6487552
由于值是相同的,因此我们可以尝试将数组名赋值给一个指针类型,具体代码如下:
#include <stdio.h> int main() { int arr[5] = {111, 222, 333, 444, 555}; // 使用数组名初始化 int*指针 int *p = arr; printf("%d\n", *p); printf("%d\n", *(p + 1)); printf("%d\n", *(p + 2)); printf("%d\n", *(p + 3)); printf("%d\n", *(p + 4)); return 0; }程序在 Visual Studio 中可以成功编译,并且能够访问数组中各个元素的值。
那么,有的同学可能会认为数组名的类型就是一个指向元素的指针。为了验证这个猜想,我们使用 sizeof 测量数组名的大小。如果数组名是一个指针,那么它的大小在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
程序用 64 位系统进行编译。运行结果为:
#include <stdio.h> int main() { int arr[5] = {111, 222, 333, 444, 555}; int *p = arr; printf("sizeof arr = %d\n", sizeof(arr)); printf("sizeof p = %d\n", sizeof(p)); printf("sizeof arr + 1 = %d\n", sizeof(arr + 1)); return 0; }运行结果为:
sizeof arr = 20
sizeof p = 8
sizeof arr + 1 = 8
这段程序演示了 C语言中指针和数组的一些概念,接下来我们对其进行详细分析:
首先,p 是一个指针,通过前面的学习我们知道了 64 位系统的指针大小是 8 字节,这个结果是毫无疑问的;
其次,arr 的大小为 20,因为它是一个包含 5 个整数的数组。整数(int 类型)占用 4 字节。因此,arr 的大小是 5 个整数,每个整数占用 4 字节,总共是 5 × 4 = 20 字节。
最后,arr + 1 的大小是 8,因为这里涉及一个指针运算。arr 是一个数组,在表达式中被解释为指向数组第一个元素的指针。当你对数组名执行加法操作时,结果是一个指针。在 64 位系统上,指针占用 8 字节。因此,在这种情况下,sizeof(arr + 1) 计算的是一个指针的大小,而不是数组本身的大小。这就是为什么 sizeof(arr + 1) 的值为 8。
这里有一个重要的知识点要强调一下,当数组名 arr 出现在一个表达式当中,数组名 arr 将会被转换为指向数组第一个元素的指针。但是,这个规则有两个例外:
- 对数组名 arr 使用 sizeof 时;
- 对数组名 arr 使用 & 时。
也就是说,数组名 arr 的类型其实是
int [5]
,因此 sizeof(arr) 的结果是 20。数组名 arr 出现在表达式 int *p = arr 中,会被转换为指向数组第一个元素的指针,即 int [5]
被转为 int *
类型,之后将转化后的指针赋值给指针变量 p。arr + 1 也是一个表达式,数组名 arr 被转换为 int * 类型,并进行加法运算后,仍然为 int * 类型。C语言指针和数组的关系
通过前面的学习,我们知道了指针和数组之间存在密切的关系,接下来我们对其进行总结。1、数组名会被转换为指针
当你声明数组时,数组名在表达式中会被转换为一个指向数组第一个元素的指针,这意味着数组名的值是数组第一个元素的地址。2、指针运算
我们可以对指针进行算术运算,如加法和减法。这可以用来遍历数组。例如,pArr + 1
将指向数组中的下一个元素,而 pArr - 1
将指向上一个元素。指针运算会考虑所指向的数据类型的大小,以确保正确地进行遍历。3、数组和指针的互换
在 C 语言中,数组和指针可以在很多情况下互换使用。下面用实际的例子来说明它们之间的关系和相互调用的方式。假设我们有如下整数数组:
int arr[] = {1, 2, 3, 4, 5};数组名 arr 在表达式中可以被转换为一个指向数组第一个元素的指针。因此,我们可以声明一个指针 p,让它指向数组的第一个元素。
int *p = arr;现在,我们可以使用指针 p 来访问和操作数组元素。以下是一些使用指针和数组名访问数组元素的例子。
1) 使用数组名和下标访问元素。
printf("arr[2] = %d\n", arr[2]); // 输出:arr[2] = 3
2) 使用指针和偏移访问元素。
printf("*(p + 2) = %d\n", *(p + 2)); // 输出:*(p + 2) = 3
3) 使用数组名和偏移访问元素。
printf("*(arr + 2) = %d\n", *(arr + 2)); // 输出:*(arr + 2) = 3
4) 使用数组表示法访问指针所指向的连续内存空间。
printf("p[2] = %d\n", p[2]); // 输出:p[2] = 3
在上面的例子中,我们使用了数组名 arr 和指针 p 以不同的方式访问数组元素。需要注意的是,数组名和指针虽然在很多情况下可以互换使用,但在本质上是不同的。
现在我们学会了访问数组元素的两种办法:
数组名[下标]
和*(数组名 + 偏移量)
。其中,偏移量就是指针指向的地址与数组首地址之间相差几个元素。例如,要访问第 2 个元素,可以使用
数组名[1]
或 *(数组名+ 1)
;要访问指针指向的数据,可以使用 *(指针名 + 偏移量)
或 指针名[偏移量]
;要访问指针移动后 2 字节的数据,可以使用*(指针名 + 2)
或指针名[2]
。通过这些例子,我们可以看到指针和数组在很多情况下可以相互替换。但是,我们应该明确它们之间的区别,以避免在编程时产生错误。总结如下:
- 数组名在表达式中会被转换为一个指针常量,但它的值不能改变;指针是一个变量,它的值可以改变。
- 数组在内存中是一段连续的内存空间,用于存储多个相同类型的元素;指针是一个变量,它存储一个地址,通常用于指向数组或其他变量。
- 使用数组名和指针访问数组元素时,要注意数组下标和偏移量的使用。
理解这些概念后,你将能够灵活地运用指针和数组来编写高效且易于理解的代码。在实际编程中,合理地运用指针和数组有助于简化代码结构,提高程序的执行效率。