【问题标题】:Retrieving 2D array from pointer in C从C中的指针检索二维数组
【发布时间】:2017-10-11 10:33:00
【问题描述】:

我尝试了以下代码

    char arr[5] = {'A', 'E', 'I', 'O', 'U'};
    char (*p_arr)[1] = &arr;
    printf("%c\n", p_arr[0][4]); //returns 'U'

thisstackoverflow 线程上,

    char (*p_arr2D)[m] = &arr2D //or arr2D

也是n*m 二维数组arr2D 衰减的语法,返回指向其第一个元素的指针,即m 元素的数组。

p_arr[0] 似乎是“反向”数组衰减,从指向数组的指针中检索二维数组。怎么会这样?在上面的代码 sn-p 中,p_arr 是否被解释为 1*5 数组(来自具有 5 个元素的原始一维数组)?

【问题讨论】:

  • 我的错,我把我原来的问题分成两半,忘了在这部分把char (*p_arr)[1] = &arr;改成char (*p_arr)[5] = &arr;。但是,char (*p_arr)[1] = &arr; 不会产生任何编译错误(请参阅我的 other question),我不明白为什么。
  • (C 编译器不会阻止你想做什么。)用-Wall 选项编译它。
  • 我回滚了代码更改。一旦发布了答案,请不要修复您的代码。通过修复它,您使大多数答案变得无关紧要。相反,如果您有后续问题,请提出一个新问题(就像您所做的那样)。

标签: c pointers multidimensional-array


【解决方案1】:

二维数组像一维数组一样存储在内存中How are multi-dimensional arrays formatted in memory?

char (*p_arr)[1] = &arr; 创建一个指针,指向一个包含一个元素 ((*p_arr)[1]) 的 char 指针数组,并通过赋值 &arr 这个指针获取一个已经存在的包含 5 个元素的数组的地址

所以 printf("%c\n", p_arr[0][4]); 打印第 5 个元素 ( U ) 因为 p_arr[0][4] 处理第 1 行中的第 5 个元素

printf("%c\n", p_arr[1][4]); 会报错

所以这段代码有效的原因是二维数组像一维数组一样存储在内存中

【讨论】:

  • "并且通过赋值 &arr 这个指针得到一个已经存在的数组的地址" 不,因为那部分是无效的 C。它不是一个有效的赋值。
【解决方案2】:

此声明

char (*p_arr)[1] = &arr;

不正确,因为声明的指针和初始化程序的类型不兼容。没有从类型char ( * )[5](初始化程序的类型)到类型char ( * )[1](声明指针的ty[e)的隐式转换。因此,您需要显式转换以将一种类型的指针重新解释为另一种类型的指针,例如

char (*p_arr)[1] = ( char ( * )[1] )&arr;

至于你的问题,如果你有一个T 类型的对象,其中T 是某种类型

T obj;

还有一个指向对象的指针

T *ptr = &obj;

然后表达式*ptrptr[0] 产生T 类型的引用对象。

所以让我们假设你有以下声明

char arr[5] = {'A', 'E', 'I', 'O', 'U'};
char (*p_arr)[5] = &arr;

对于类型char[5],即变量arr 的类型,您可以引入typedef 名称。

typedef char T[5];

然后声明看起来像

T arr = {'A', 'E', 'I', 'O', 'U'};
T *p_arr = &arr;

如上所述,表达式*p_arrp_arr[0] 产生T 类型的引用对象,即char[5] 类型。

来自 C 标准(6.5.3.2 地址和间接运算符)

4 一元 * 运算符表示间接。如果操作数指向一个 函数,结果是一个函数指示符;如果它指向一个 对象,结果是一个指定对象的左值。如果操作数 类型为“类型指针”,结果类型为“类型”。如果 无效的值已分配给指针,的行为 一元 * 运算符未定义。

和(6.5.2.1 数组下标)

2 后缀表达式后跟方括号 [] 中的表达式是数组对象元素的下标名称。 下标运算符[]的定义是E1[E2]等同于(*((E1)+(E2)))。由于适用于二元+运算符的转换规则,如果 E1 是一个数组对象(相当于一个指向数组对象初始元素的指针)并且 E2 是一个整数,则 E1[E2] 指定 E1 的第 E2 个元素(从零开始计数)。

最后(6.5.6 加法运算符)

7 对于这些运算符,指向一个对象的指针 不是数组元素的行为与指向 长度为 1 的数组的第一个元素,对象的类型为 它的元素类型

所以在这个声明中

char arr[5] = {'A', 'E', 'I', 'O', 'U'};
char (*p_arr)[5] = &arr;

指针p_arr 不指向数组元素。但是,它可以表现为指向长度为 1 的数组的第一个元素。您可以按以下方式想象它

char arr[1][5] = { {'A', 'E', 'I', 'O', 'U'} };
char (*p_arr)[5] = arr;

所以表达式p_arr[0] 给出了想象的二维数组的第一个元素,它是char[5] 类型的元素。

【讨论】:

  • 我的错,我把我原来的问题分成两半,忘记改变 char (*p_arr)[1] = &arr;到 char (*p_arr)[5] = &arr;在这部分(现在完成)。但是, char (*p_arr)[1] = &arr;不会产生任何编译错误(请参阅我的另一个问题),我不明白为什么。我已经把这个评论放在我的问题下,告诉我在这里复制它是好是坏。如果需要,我会删除它
【解决方案3】:

首先,char (*p_arr)[1] = &arr; 行是无效的 C。您的编译器必须给出诊断消息。 p_arr 是一个指向 1 个字符数组的数组指针,但您将其分配为指向 5 个字符数组。

至于为什么看起来你有一个二维数组——你没有有一个——是因为p_arr[0] 像任何其他指针算法一样工作,给你指向的项目“offset + 0”。哪个是数组。然后您访问数组中的第 4 项。

指针算术原理的一个简单示例如下:

int a;
int* ptr = &a;
ptr[0] = whatever; // perfectly valid C

ptr[0] 保证等同于*(ptr+0)

正确使用数组指针应该是printf("%c\n", (*p_arr)[4])。在这种情况下它恰好可以工作,但在其他情况下,您可能会调用未定义的行为,因为不允许 p_arr 指向超出分配的内存。

【讨论】:

  • 我的错,我把原来的问题分成两半,忘记改变 char (*p_arr)[1] = &arr;到 char (*p_arr)[5] = &arr;在这部分(现在完成)。但是, char (*p_arr)[1] = &arr;不会产生任何编译错误(请参阅我的另一个问题),我不明白为什么。我已经将此评论放在我的问题下,告诉我在这里复制它是好还是坏。如果需要,我会删除它
【解决方案4】:

您将 p_arr 声明为指向字符数组的指针(并且该数组的长度为 1,这肯定是问题的迹象)。

您可能想要更简单的语法:

char *p_arr = arr;

或者也许:

#include <stdio.h>

int main() {
    char arr[] = {"Hello world"};
    char *p[3] = {NULL, arr, NULL};

    printf("%s %c\n", p[1], p[1][3]);
}

约翰

【讨论】:

  • 我的错,我把我原来的问题分成两半,忘记改变 char (*p_arr)[1] = &arr;到 char (*p_arr)[5] = &arr;在这部分(现在完成)。但是, char (*p_arr)[1] = &arr;不会产生任何编译错误(请参阅我的另一个问题),我不明白为什么。我已经把这个评论放在我的问题下,告诉我在这里复制它是好是坏。如果需要,我会删除它
猜你喜欢
  • 2017-04-26
  • 1970-01-01
  • 2017-06-11
  • 1970-01-01
  • 2013-01-26
  • 2016-05-08
  • 2021-08-23
  • 1970-01-01
相关资源
最近更新 更多