39

我知道有几个问题可以提供好的(和有效的)解决方案,但恕我直言,没有一个明确说明实现这一目标的最佳方法是什么。所以,假设我们有一些二维数组:

int tab1[100][280];

我们想要创建一个指向这个二维数组的指针。为此,我们可以这样做:

int (*pointer)[280]; // pointer creation
pointer = tab1; //assignation
pointer[5][12] = 517; // use
int myint = pointer[5][12]; // use

或者,或者:

int (*pointer)[100][280]; // pointer creation
pointer = &tab1; //assignation
(*pointer)[5][12] = 517; // use
int myint = (*pointer)[5][12]; // use 

好的,两者似乎都运作良好。现在我想知道:

  • 最好的方法是什么,第一个还是第二个?
  • 编译器都相等吗?(速度,性能...)
  • 这些解决方案中的一种是否比另一种消耗更多的内存?
  • 开发人员更常用的是什么?
4

4 回答 4

36
//defines an array of 280 pointers (1120 or 2240 bytes)
int  *pointer1 [280];

//defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
int (*pointer2)[280];      //pointer to an array of 280 integers
int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers

使用pointer2pointer3生成相同的二进制文件,除了WhozCraig++pointer2指出的操作。

我建议使用typedef(产生与上面相同的二进制代码pointer3

typedef int myType[100][280];
myType *pointer3;

注意:从 C++11 开始,您也可以使用关键字using代替typedef

using myType = int[100][280];
myType *pointer3;

在你的例子中:

myType *pointer;                // pointer creation
pointer = &tab1;                // assignation
(*pointer)[5][12] = 517;        // set (write)
int myint = (*pointer)[5][12];  // get (read)

注意:如果数组tab1在函数体中使用 => 这个数组将被放置在调用堆栈内存中。但是堆栈大小是有限的。使用大于可用内存堆栈的数组会产生堆栈溢出崩溃

完整的代码片段可在gcc.godbolt.org上在线编译

int main()
{
    //defines an array of 280 pointers (1120 or 2240 bytes)
    int  *pointer1 [280];
    static_assert( sizeof(pointer1) == 2240, "" );

    //defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
    int (*pointer2)[280];      //pointer to an array of 280 integers
    int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers  
    static_assert( sizeof(pointer2) == 8, "" );
    static_assert( sizeof(pointer3) == 8, "" );

    // Use 'typedef' (or 'using' if you use a modern C++ compiler)
    typedef int myType[100][280];
    //using myType = int[100][280];

    int tab1[100][280];

    myType *pointer;                // pointer creation
    pointer = &tab1;                // assignation
    (*pointer)[5][12] = 517;        // set (write)
    int myint = (*pointer)[5][12];  // get (read)

    return myint;
}
于 2013-02-11T09:17:23.090 回答
11

你的两个例子都是等价的。但是,第一个不那么明显并且更“hacky”,而第二个清楚地说明了您的意图。

int (*pointer)[280];
pointer = tab1;

pointer指向 280 个整数的一维数组。在您的作业中,您实际上分配了tab1. 这是可行的,因为您可以将数组隐式转换为指针(指向第一个元素)。

当您使用pointer[5][12]时,C 将pointer其视为数组数组(pointer[5]is of type int[280]),因此这里还有另一个隐式转换(至少在语义上)。

在第二个示例中,您显式创建了一个指向 2D 数组的指针:

int (*pointer)[100][280];
pointer = &tab1;

这里的语义更清楚:*pointer是一个二维数组,所以你需要使用(*pointer)[i][j].

两种解决方案都使用相同数量的内存(1 个指针),并且很可能运行速度相同。在后台,两个指针甚至都指向同一个内存位置(tab1数组的第一个元素),而且您的编译器甚至可能生成相同的代码。

第一个解决方案是“更高级的”,因为需要对数组和指针在 C 中的工作方式有相当深入的了解才能理解发生了什么。第二个更明确。

于 2013-02-11T10:00:37.993 回答
10

int *pointer[280];//创建 280 个 int 类型的指针。

在 32 位操作系统中,每个指针 4 个字节。所以 4 * 280 = 1120 字节。

int (*pointer)[100][280];// 仅创建一个指针,用于指向 [100][280] 个整数数组。

这里只有 4 个字节。

提出您的问题,int (*pointer)[280];尽管int (*pointer)[100][280];它指向 [100][280] 的相同二维数组,但它是不同的。

因为如果int (*pointer)[280];是递增的,那么它将指向下一个一维数组,但是 asint (*pointer)[100][280];穿过整个二维数组并指向下一个字节。如果该内存不属于您的进程,则访问该字节可能会导致问题。

于 2013-02-11T09:14:26.710 回答
0

好的,这实际上是四个不同的问题。我将一一解决:

对编译器来说都是平等的吗?(速度,性能...)

是的。指针解引用和从类型衰减int (*)[100][280]int (*)[280]对你的 CPU 来说总是一个问题。无论如何,我不会让它通过一个糟糕的编译器来生成虚假代码,但是一个好的优化编译器应该将两个示例编译成完全相同的代码。

这些解决方案中的一种是否比另一种消耗更多的内存?

作为我第一个答案的推论,不。

开发人员更常用的是什么?

绝对是没有额外(*pointer)取消引用的变体。对于 C 程序员来说,假设任何指针实际上可能是指向数组第一个元素的指针,这是一种习惯。

最好的方法是什么,第一个还是第二个?

这取决于您优化的内容:

  • 惯用代码使用变体 1。声明缺少外部维度,但所有使用都与 C 程序员期望的完全一样。

  • 如果你想明确指出你指向一个数组,你可以使用变体 2。但是,许多经验丰富的 C 程序员会认为在最里面的 . 后面隐藏着第三个维度*。对于大多数程序员来说,没有数组维度会觉得很奇怪。

于 2020-03-24T16:12:01.730 回答