【问题标题】:Pointer to 2D arrays in CC中指向二维数组的指针
【发布时间】:2013-01-26 08:43:59
【问题描述】:

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

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 

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

  • 什么是最好的方法,第一个还是第二个?
  • 对于编译器来说都相等吗? (速度、性能...)
  • 其中一种解决方案是否比其他解决方案占用更多内存?
  • 开发人员更常用的是什么?

【问题讨论】:

  • 请记住,指针数组(如int *pointer[280];)与数组数组不同。
  • 你好,费迪南德。我用括号更正了第一个解决方案。道歉。

标签: c arrays pointers


【解决方案1】:
//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 生成相同的二进制文件,除了++pointer2 所指出的WhozCraig 指出的操作。

我推荐使用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 => 该数组将被放置在调用堆栈内存中。但是堆栈大小是有限的。使用大于可用内存堆栈的数组会产生stack overflow crash

完整的 sn-p 可在 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;
}

【讨论】:

  • 您好,请查看我的第一个解决方案:我之前在创建指针时忘记了括号。道歉。
  • @user216993 在第二个版本中,您还写了(pointer*) 而不是(*pointer)。 ;-) 好的,我更新了我的答案。干杯
  • 感谢 olibre 的明确回复。 (并再次为拼写错误道歉......)。
  • “使用 pointer2pointer3 生成相同的二进制文件” - 在执行指针数学运算时不是正确的。 ++pointer2 将使指针前进一“行”。 ++pointer3 将使指针前进一个完整的矩阵。它们是不同的类型
  • 你是对的@WhozCraig 我的句子是错误的。感谢您的反馈:) 我会改变它。干杯;)
【解决方案2】:

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

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

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

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

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

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

这里的语义更清晰:*pointer 是一个二维数组,所以你需要使用(*pointer)[i][j] 来访问它。

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

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

【讨论】:

  • 感谢您的回复,费迪南德。我同意你的看法:第二种解决方案似乎更明确。我会继续使用这个。
【解决方案3】:

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

【讨论】:

  • 你好,请查看我的第一个解决方案:我之前在创建指针时忘记了括号。道歉。
【解决方案4】:

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

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

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

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

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

开发者最常使用的是什么?

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

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

这取决于您优化的目标:

  • 惯用代码使用变体 1。声明缺少外部维度,但所有用法都完全符合 C 程序员的预期。

  • 1234563对于大多数程序员来说,没有数组维度会觉得很奇怪。

【讨论】:

    猜你喜欢
    • 2017-06-11
    • 2021-08-23
    • 1970-01-01
    • 1970-01-01
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多