【问题标题】:Several disturbance about the pointer关于指针的几个干扰
【发布时间】:2019-01-30 23:43:25
【问题描述】:

我已经阅读了几个关于网站指针和互联网 PDF 的主题。但是有很多事情我到现在还不是很清楚:

在 PDF 中写到,可以将具有 10 行和 20 列的二维数组声明为 int (*ary)[20] 而不是 int array[10][20]

他们解释:

"ary被定义为指向一组连续的指针, 一维、20 元素的整数数组。”

在互联网上的许多网站中,他们只是说int (*ary)[20] 是指向 20 个整数数组的指针的声明。

【问题讨论】:

  • 这也是一个很好的问答集,尤其是关于 C 中数组的动态分配:stackoverflow.com/questions/42094465/…
  • 看来您不应该信任该 PDF 的内容。
  • 感谢您的回复。 To molbdnilo:因为数组不是指针,对吗?
  • 对。数组不是指针。而学习数组和指针之间的实际关系是学习 C 中最棘手的事情之一。(但是一旦你掌握了它,它就非常有用了。)
  • 10 行 20 列的二维数组可以声明为int (*ary)[20] 而不是int array[10][20] 不,不能。阅读许多关于为什么这是错误的答案。主要区别在于 int array[10][20] 实际上 is 是一个数组 - 已分配内存。 int (*ary)[20] 是一个可以指向任何地方的指针。它可以指向一个数组并用于访问它,也可以指向一个无效的地址。声明该指针不会为实际数组创建或分配存储空间。 (作为评论发布以提前说明。)

标签: c pointers


【解决方案1】:

主要是要了解 C 除了一维数组之外没有其他任何东西。是的,二维数组(例如矩阵)在 C 中并不存在。

但是,数组有元素,所有元素都具有相同的字节大小(由sizeof 给出)和类型。因此,您可以拥有数字数组、指针数组、struct-s 数组,甚至是数组数组(例如整数)。

所以滥用,你可以说一个数组数组是一个矩阵(所以有些人声称二维数组存在于 C 中。我觉得这是夸大其词)。但它(例如声明为int a[10][20]; 的东西)实际上仍然是一维数组(由 10 个元素组成,每个元素是一个数组),由一维数组(由 20 个整数组成)组成。在许多情况下,指针数组很有用(例如“字符串”数组)。

C99(或更高版本)中一个有趣的特性是flexible array members。有了它,您可以巧妙地定义您自己的Matrixabstract data type,用作opaque pointer(有关一些代码,请参阅this 答案)。

在许多情况下(特别是当您将数组(在赋值右侧)分配给某个左值(如变量,或将数组作为参数传递给某个函数)时,数组会衰减为指针。但他们的sizeof 和类型不同。

查看一些C reference 站点(很久以后,查看n1570 C 标准,例如它的§6.5.2.1 数组下标)。阅读How to debug small programs

顺便说一句,C 中很少需要像 int a[10][20]; 这样的所谓 2D 数组(除非您专门研究编译时已知的 固定 维矩阵的数值计算;我猜那个游戏开发人员可能经常使用typedef double transform3Dmatrix_ty[3][3]; 之类的类型)。在 30 多年的编程经验中,我几乎从未使用过二维数组。

我不会假装在几段中教你 C。你会发现很多好书(也有一些非常糟糕的)。

【讨论】:

  • 非常感谢您的回答,但我可以再问一个问题:第二个是第一个的一部分。我的意思是第二个语句是二维数组中的许多一维数组之一。
  • 主要是要了解 C 除了一维数组之外没有其他任何东西。 我想说这更像是一个幕后的实现细节. 6.5.2.1 Array subscripting 确实提到了“多维数组对象”,然后确实描述了它们实际上是一维的,但我认为需要更多解释,因为总括性声明“C 除了一维数组之外别无他物。”可能会使没有经验的 C 用户感到困惑。
  • 你确定它迷惑他们吗?
  • 考虑到这里发布的一些问题?我敢肯定,有很多人感到困惑。还有一些更糟... ;-)
【解决方案2】:

这些是数组:

int a[10];
int b[10][20];

这些是指针:

int *p1;
int (*p2)[20];

它们是不同的。它们不一样。

但是,即使它们不同,您也可以这样做:

p1 = a;
p2 = b;

现在p1 指向ap2 指向b

当您说p1 = a 时,它是简写。就像你写的一样

p1 = &a[0];

严格来说,p1 指向a 的第一个元素。类似地,p2 = bp2 = &b[0] 类似,并使 p2 指向 b 的第一个元素。但是,由于b 是一个数组数组,所以b 的第一个元素是一个20 个整数的数组。

另外,如果您将ab 传递给函数,如下所示:

f(a, b);

函数的定义应该是什么样的?事实证明,有两种方法可以查看它。你可以声明

void f(int a[], int b[20][]) { ... }

这使得f 的参数看起来像你认为你传递的那样。但事实证明传递的实际上是指针,所以f也可以这样声明:

void f(int *a, int (*b)[20]) { ... }

无论如何,这就是为什么你读到的那页表明数组int a[10][20] 和指针int (*p2)[20] 之间存在某种等价性。存在“某种等价”,但这并不意味着它们是同一类型。 (完全没有。出于这个原因,大多数人宁愿在这种情况下不再使用“等价”这个词。)

【讨论】:

    【解决方案3】:

    声明

    int (*ary)[20];
    

    int ary[10][20];
    

    非常不同。第一个确实声明了一个指向由 20 个 int 元素组成的数组的指针。第二个声明了一个包含 10 个数组的数组,该数组包含 20 个 int 元素。


    您必须记住的是,数组自然衰减为指向其第一个元素的指针。

    对于像这样的简单数组

    int simple_array[10];
    

    那么在表达式中使用普通的simple_array&simple_array[0]相同,表达式的类型为int *

    现在是数组数组

    int complex_array[10][20];
    

    如果你在表达式中使用complex_array,它仍然会产生一个指向它的第一个元素的指针,即&complex_array[0]。现在的区别在于表达式的类型&complex_array[0](或普通等效的complex_array)是int (*)[20]

    利用这些信息,您可以轻松地创建一个指向complex_array 的指针,并使其指向complex_array 的第一个元素:

    int (*complex_array_pointer)[20];
    
    // Make complex_array_pointer point to the first element of complex_array
    // i.e. make complex_array_pointer point to complex_array[0]
    complex_array_pointer = complex_array;
    

    【讨论】:

    • 非常感谢您的帮助。现在我明白了
    • @BoyMarine 如果这是对您有用的答案,请点击该复选标记接受。谢谢!
    【解决方案4】:

    您只需要考虑ary 是指向其第一个元素的指针。请注意语法ary[index] 等同于*(ary + index),它将帮助您了解它的工作原理。

    【讨论】:

    • 在某些情况下,数组衰减为指向其第一个元素的指针,但数组本身不是指针。
    • 您想说的是,当您在表达式中使用ary 时,您得到的是指向其第一个元素的指针。但是数组本身并不是一个指针——数组本身一个数组。
    • int main(int argc, int **argv) int main(int argc, int *argv[]) int main(int argc, int argv[][]) 那你能解释一下为什么这3个语法实际上经常被使用吗?
    • 请注意,“等效”并不意味着“完全相同”——语法产生相同的值,但 *(ary + index) 涉及到 *(&ary[0] + index) 的隐式转换。
    • @L.Chaumartin 见herehere
    【解决方案5】:

    int array[10][20] 是一个有 10 个项目的数组,每个项目是一个 20 个整数的数组。或者,如果您愿意:它是一个 10x20 int 的二维数组。

    int (*ptr)[20] 是一个指向 20 个整数数组的数组指针。它不为数组分配任何内存,但它可以指向一个。

    如果你写

    int array[10][20];
    int (*ptr)[20] = array;
    

    然后array“衰减”为指向第一个元素的指针。 int [10][20] 的第一个元素是 int[20]。而int (*ptr)[20] 是指向这样一个元素的指针,所以这很好。

    您可能会因为动态分配二维数组而感到困惑,可以这样做:

    int (*ptr)[20] = malloc( sizeof(int[10][20]) );
    

    这里,ptr 指向 int[10][20] 数组的第一个元素(int[20])。

    【讨论】:

    • 非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2020-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-16
    相关资源
    最近更新 更多