为何二维数组的行指针地址与该行首个元素地址相同?
动态二维数组与静态二维数组的内存结构差异解析
你的代码运行后会呈现这样的输出特征:
- 动态分配部分:前两行打印的
s+i是一组连续的地址(指针数组的元素地址),后续打印的*(s+i)+j是另一组和前者无关联的地址(每个子数组的元素地址); - 静态数组部分:打印的
s2+i和对应行的*(s2+i)+0地址完全相同,s2+i之间的地址间隔为12字节(假设int占4字节,对应3个int的长度),*(s2+i)+j之间的地址间隔为4字节。
先看修正后的完整代码(补充了必要的头文件和内存释放):
#include <stdio.h> #include <stdlib.h> int main() { int **s = (int**)malloc(3 * sizeof(int*)); for (int i = 0; i < 3; i++) { printf("%d\n", s+i); } for (int i = 0; i < 3; i++) { s[i] = (int*)malloc(3 * sizeof(int)); } for (int i = 0; i < 3; i++) { printf("%d\n", s+i); } printf("\n"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { printf("%d\n", *(s+i)+j); } } printf("\n"); int s2[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; for (int i = 0; i < 3; i++) { printf("%d\n", s2+i); } printf("\n"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { printf("%d\n", *(s2+i)+j); } } // 释放动态分配的内存 for (int i = 0; i < 3; i++) { free(s[i]); } free(s); return 0; }
核心差异:两种结构的本质区别
1. 动态分配的int **s:指针数组
s是一个指向指针的指针,它指向的是一块malloc分配的内存,这块内存里存储了3个int*类型的指针(即s[0]、s[1]、s[2])。s+i表示的是指针数组中第i个指针变量本身的地址(比如s+0就是s[0]这个指针所在的内存地址)。*(s+i)(等价于s[i])是该指针变量的值,也就是后续单独malloc出来的int数组的首地址。- 这两个地址必然不同:指针数组的元素(指针)存放在一块内存区域,每个
int子数组是独立分配的另一块区域,两者物理位置不重叠。
2. 静态定义的int s2[3][3]:二维数组
s2是一个二维数组,本质是「包含3个元素的数组,每个元素是长度为3的int数组」,在内存中是连续存放的9个int,没有额外的指针存储空间。- 当你写
s2+i时,数组名s2会自动退化为指向第一个元素(即第一个int[3]子数组)的指针,类型为int (*)[3],s2+i的数值就是第i个int[3]子数组的起始地址。 - 当你写
*(s2+i)时,这里的*(s2+i)是第i个int[3]子数组本身,它作为数组名会再次退化为指向该子数组第一个元素的指针,类型为int*,其数值就是这个子数组的起始地址——和s2+i的数值完全相同。
关键理解:并非同一内存存储两个内容
地址相同只是因为同一块内存的起始位置被不同类型的指针指向:
s2+i是int (*)[3]类型,指向的是一个长度为3的int子数组;*(s2+i)退化为int*类型,指向的是这个子数组的第一个int元素。
它们指向的物理内存位置完全一致,但类型不同,所以指针偏移的步长不同:比如s2+1会跳过3个int(整个子数组的长度),而*(s2+1)+1只会跳过1个int。
内容的提问来源于stack exchange,提问作者M. Saamin Rahman
相关产品推荐
相关产品推荐

