【问题标题】:Pointers in C - 1D and 2DC - 1D 和 2D 中的指针
【发布时间】:2014-07-16 20:08:33
【问题描述】:

我知道,对于以下几点:

int a[10];

aint * 类型的指针,指向 a[0],而 &aint (*)[10] 类型的指针。

现在我的问题是针对以下二维数组:

int b[20][30];
  1. bint ** 类型的指针吗?还是int (*)[30] 类型的指针?

  2. &bint (*)[20][30] 类型的指针吗?

【问题讨论】:

  • 不不不。 a 是一个数组。 b 是一个数组数组。也不是指针。阅读arrays
  • SO 经验法则 #2:“我知道 X”通常伴随着完全的误解。
  • “我知道,对于以下 […]”——在大多数情况下,是的(例如,作为 sizeof 的操作数是一个明显的例外);广告 1.:这是int (*)[30];广告 2.:是的。查看 Chris 提供的链接。
  • a is a pointer of the type int * 当我发出sizeof(a) 时,这是一个巨大的指针。

标签: c pointers


【解决方案1】:

数组不是指针(这一点怎么强调都不过分)。

话虽如此,数组衰减为指向其第一个元素的指针。例如:

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

void print_a(int *);
void print_b(int (*)[30]);

print_a(a);
print_b(b);

a 的第一个元素是a[0],同样b 的第一个元素是b[0]。你基本上把第一个维度拿走了,把它改成(*);稍后我会解释更多,因为它比这更复杂。

指针和数组之间的关系充满了微妙的上下文,这些微妙之处并不难掌握,但不同范围内的大小信息使它变得有趣,也有助于让您了解衰减是如何工作的:

#include <stdio.h>
int h(int *pa)
{
    printf("h(int *): sizeof pa=%zu\n", sizeof pa);
    printf("h(int *): sizeof *pa=%zu\n", sizeof *pa);
    return *pa;
}
int g(int (*pa)[5])
{
    printf("g(int (*)[5]): sizeof pa=%zu\n", sizeof pa);
    printf("g(int (*)[5]): sizeof *pa=%zu\n", sizeof *pa);
    return h(*pa);
}
int f(int (*pa)[3][5])
{
    printf("f(int (*)[3][5]): sizeof pa=%zu\n", sizeof pa);
    printf("f(int (*)[3][5]): sizeof *pa=%zu\n", sizeof *pa);
    return g(*pa);
}
int main(void)
{
    int arr[2][3][5] = {{{11235}}};
    printf("main: sizeof arr=%zu\n", sizeof arr);
    printf("main: sizeof *arr=%zu\n", sizeof *arr);
    printf("%d\n", f(arr));
}

每个指针的大小都相同(在所有平台上可能并不总是如此!),但是通过取消引用指针,您可以看到数组中给定元素的大小,无论您是否使用一元 * 运算符取消引用或[N] 数组表示法,根据定义相当于*((array)+(N))

不管怎样,回到指针和数组的区别,你应该明白int[20][30]int **是不一样的。这是为什么?由于int[20][30] 衰减为int(*)[30] 类型的指针这一事实,因此在取消引用指针之前不会发生更多衰减。此外,int ** 实际上是int *(*),这意味着它可以指向指针数组的第一个元素。也就是说,int ** 可能曾经是 int *[N]

int foo[x][y][z] <=> int (*foo)[y][z]

int *foo[m][n] <=> int *(*foo)[n]

int (*foo[a])[b] <=> int (**foo)[b]

在第一种情况下,我们有一个 3-D 数组,它衰减为指向 2-D 数组的指针;换句话说,除了大小问题之外,数组数组和指向数组的指针在许多情况下密切相关并且可以互换。第一个维度x 是衰减的维度,留下yz 维度。

在第二种情况下,我们有一个二维指针数组。这衰减为指向指针数组的指针。同样,数组数组与指向数组的指针密切相关,维度m衰减,留下维度n

在第三种情况下,我们有一个指向数组的指针数组。它衰减为指向数组指针的指针。由于维度a 最接近变量名,即衰减的那个,留下维度b。请注意,由于它是一个指向数组的指针数组,因此指针可以指向数组本身的第一个元素:

int arr[2][3][5];
int (*foo[2])[5] = { arr[0], arr[1] };
int (**foo_ptr)[5] = foo;

回顾:

  • 数组(大小 B)的数组(大小 A) 指向数组的指针(大小 B)
  • 指针数组(大小 A) 指向指针的指针
  • 衰减/增长的数组始终是最内层的数组/指针,最内层是最接近变量声明中的变量名的数组/指针,数组具有比指针更高的关联性,当然圆括号会有所不同。

这个兔子洞显然会让人困惑,但我希望我至少能帮上一点忙!

【讨论】:

    【解决方案2】:

    首先要明确数组不是指针。指针和数组之间的关系很微妙。我建议你先阅读tutorial on pointers by Ted Jensen first 的第二章。

    我想简单地告诉您这一章中所描述的内容。考虑以下示例。

    int a[10];
    int *p;
    

    现在你可以写了

    p=a; 
    

    相当于

    p=&a[0];
    

    这件事让很多文字说数组是指针的名称。但是最好说“数组的名字是数组中第一个元素的地址”

    因为虽然你会写

    p=a;
    

    但你不能写

    a=p;
    

    现在来回答你的问题:

    从上面的讨论中应该清楚 b 不是 int** 类型的指针。例如:

    int b[10][10];
    int **x;
    int *p;
    
    b=&p; // this is error
    
    x=&p; // this fine
    

    对于您的其他问题,您可以使用online CDECL

    如果你写

    int b[10][10]; --> 将 b 声明为 int 的数组 10 的数组 10

    int (*p)[10]; --> 将 p 声明为指向 int 数组 10 的指针

    int (*p)[10][20]; --> 将 p 声明为指向 int 数组 20 的数组 10 的指针

    【讨论】:

      【解决方案3】:

      不,a 不是int* 类型,而是int [10] 类型(即数组类型)。这就是为什么sizeof(a) 会给你数组的大小(40 字节,假设是 32 位整数)。

      同样,b 的类型为 int [20][30],它只不过是一个数组数组。 IE。 sizeof(b[0])是一个线阵的大小,本例为120,整个数组(sizeof(b))的大小是线阵大小的20倍,本例为2400字节。

      诀窍在于,在几乎所有上下文中,数组都会衰减为指向其第一个元素的指针。所以当你对二维数组进行指针运算(如b[3])时,b 会衰减为int (*)[30] 类型的指针,因此指针运算会跳过,增加三倍的大小一行(在这种情况下为 360 字节)- 行是 2D 数组的元素。 b[3] 的结果类型是 int [30],即。 e.取消引用的指针。

      使用b[3] 取消对行数组的引用后,您可以再次调用指针算法来选择该行中的正确元素(b[3][5])。再次调用数组指针衰减,机制相同。

      请注意,当您使用int** 模拟二维数组时,不涉及指针数组。双重取消引用 b[3][5] 借助数组指针衰减转换为 ((int*)b)[3*30 + 5] 之类的东西,只有元素本身可以从内存中访问。

      【讨论】:

      • int ()[10] 是一个函数类型,没有或未指定参数,返回一个 10 个整数的数组。 a 是一个 int [10] -- 一个 10 个整数的数组。
      • 我不喜欢最后一段;正确的代码是((int *)b)[3*30+5],这不是“由于衰减”
      • @MattMcNabb 包括您对更精确代码的建议,谢谢。昨天写的有点匆忙,不准确的请见谅。但我坚持“凭借数组指针衰减”这一短语:衰减是使这种转换发生的机制——正是这种机制使得双重取消引用b[3][5] 的行为如此根本不同,具体取决于bint [20][30] 类型或int** 类型。
      【解决方案4】:
      int* temp;
      int arraySize = 20;
      temp = (int *)malloc(arraySize * sizeof(int));
      

      这将在内存中创建一个 20“整数”长的部分,就像您提到的那样。

      int** temp;
      int arraySize = 20;
      int rowSize = 10;
      temp = (int **)malloc(arraySize * sizeof(int *));
      for(i=0; i<arraySize; i++){
         temp[i] = (int *)malloc(rowSize * sizeof(int));
      }
      

      这就是二维数组的实际样子。
      temp[0] 会给你第一个“数组”的地址。您可以在其中执行类似于上述int *array = temp[0] 的操作,然后像普通数组一样访问它,但使用*array[0] 来获取值。

      二维数组确实不能很好地与指针啮合,并说*temp[0] 来获取第一个数组的值。你可以试着弄乱它,你会弄明白的,但是现在没有一台机器可以和我一起编译C。

      可能有帮助的参考:http://www.cs.swarthmore.edu/~newhall/unixhelp/C_arrays.html

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-16
        相关资源
        最近更新 更多