C语言 指向 C 中二维数组的指针

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14808908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:19:21  来源:igfitidea点击:

Pointer to 2D arrays in C

carrayspointers

提问by Chrysotribax

I know there is several questions about that which gives good (and working) solutions, but none IMHO which says clearly what is the best way to achieve this. So, suppose we have some 2D array :

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

int tab1[100][280];

We want to make a pointer that points to this 2D array. To achieve this, we can do :

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

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

or, alternatively :

或者,或者:

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

OK, both seems to work well. Now I would like to know :

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

  • what is the best way, the 1st or the 2nd ?
  • are both equals for the compiler ? (speed, perf...)
  • is one of these solutions eating more memory than the other ?
  • what is the more frequently used by developers ?
  • 什么是最好的方法,第一还是第二?
  • 对编译器来说两者是否相等?(速度,性能...)
  • 这些解决方案之一是否比其他解决方案占用更多内存?
  • 开发人员最常用的是什么?

采纳答案by olibre

//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

Using pointer2or pointer3produce the same binary except manipulations as ++pointer2as pointed out by WhozCraig.

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

I recommend using typedef(producing same binary code as above pointer3)

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

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

Note:Since C++11, you can also use keyword usinginstead of typedef

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

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

in your example:

在你的例子中:

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

Note:If the array tab1is used within a function body => this array will be placed within the call stack memory. But the stack size is limited. Using arrays bigger than the free memory stack produces a stack overflowcrash.

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

The full snippet is online-compilable at gcc.godbolt.org

完整片段可在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;
}

回答by Ferdinand Beyer

Both your examples are equivalent. However, the first one is less obvious and more "hacky", while the second one clearly states your intention.

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

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

pointerpoints to an 1D array of 280 integers. In your assignment, you actually assign the first rowof tab1. This works since you can implicitly cast arrays to pointers (to the first element).

pointer指向一个包含 280 个整数的一维数组。在你的任务,你实际上分配第一tab1。这是有效的,因为您可以隐式地将数组转换为指针(指向第一个元素)。

When you are using pointer[5][12], C treats pointeras an array of arrays (pointer[5]is of type int[280]), so there is another implicitcast here (at least semantically).

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

In your second example, you explicitly create a pointer to a 2D array:

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

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

The semantics are clearer here: *pointeris a 2D array, so you need to access it using (*pointer)[i][j].

这里的语义更清晰:*pointer是一个二维数组,因此您需要使用(*pointer)[i][j].

Both solutions use the same amount of memory (1 pointer) and will most likely run equally fast. Under the hood, both pointers will even point to the same memory location (the first element of the tab1array), and it is possible that your compiler will even generate the same code.

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

The first solution is "more advanced" since one needs quite a deep understanding on how arrays and pointers work in C to understand what is going on. The second one is more explicit.

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

回答by Jeyaram

int *pointer[280];//Creates 280 pointers of type int.

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

In 32 bit os, 4 bytes for each pointer. so 4 * 280 = 1120 bytes.

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

int (*pointer)[100][280];// Creates only one pointer which is used to point an array of [100][280] ints.

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

Here only 4 bytes.

这里只有4个字节。

Coming to your question, int (*pointer)[280];and int (*pointer)[100][280];are different though it points to same 2D array of [100][280].

来你的问题,int (*pointer)[280];并且int (*pointer)[100][280];是不同的,虽然它指向[100] [280]的相同2D阵列。

Because if int (*pointer)[280];is incremented, then it will points to next 1D array, but where as int (*pointer)[100][280];crosses the whole 2D array and points to next byte. Accessing that byte may cause problem if that memory doen't belongs to your process.

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

回答by cmaster - reinstate monica

Ok, this is actually four different question. I'll address them one by one:

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

are both equals for the compiler? (speed, perf...)

对编译器来说两者是否相等?(速度,性能...)

Yes. The pointer dereferenciation and decay from type int (*)[100][280]to int (*)[280]is always a noop to your CPU. I wouldn't put it past a bad compiler to generate bogus code anyways, but a good optimizing compiler should compile both examples to the exact same code.

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

is one of these solutions eating more memory than the other?

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

As a corollary to my first answer, no.

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

what is the more frequently used by developers?

开发者比较常用的是什么?

Definitely the variant without the extra (*pointer)dereferenciation. For C programmers it is second nature to assume that any pointer may actually be a pointer to the first element of an array.

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

what is the best way, the 1st or the 2nd?

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

That depends on what you optimize for:

这取决于您针对什么进行优化:

  • Idiomatic code uses variant 1. The declaration is missing the outer dimension, but all uses are exactly as a C programmer expects them to be.

  • If you want to make it explicit that you are pointing to an array, you can use variant 2. However, many seasoned C programmers will think that there's a third dimension hidden behind the innermost *. Having no array dimension there will feel weird to most programmers.

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

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