【问题标题】:Assigning (a pointer to an array of integers) to (a pointer to an integer array cast as a void pointer) in C在 C 中将(指向整数数组的指针)分配给(指向转换为 void 指针的整数数组的指针)
【发布时间】:2017-02-12 00:39:40
【问题描述】:

我遇到过类似以下的 C 代码:

#include <stdio.h>
#include <stdlib.h>

int main(void) {

    int *a = malloc(200*sizeof(int));

    int i;

    for (i = 0; i < 200; i++)
    {
      a[i] = i;
    }

    int (*b)[10] = (void*) a;

    printf("\nsizeof(int):\t%d\n", sizeof(int));

    printf("\nb[0]:\t%d\n", b[0]);
    printf("\na:\t%d\n", a);

    printf("\nb[19]:\t%d\n", b[19]);
    printf("\na+190:\t%d\n", a+190);

    printf("\nb[0][8]:\t%d\n", b[0][8]);
    printf("\nb[19][9]:\t%d\n", b[19][9]);

    return 0;
}

根据我的理解,int (*b)[10] = (void*) a; 行试图将指针b(它应该指向一个10 个整数的数组)分配给数组a 的起始地址作为空指针类型转换。我本来希望b[i]i=0..9a[i] 保存相同的数据(对于b,除0 到9 之外的任何索引都会导致一些未定义的行为)。但是,该程序会产生类似于以下示例的输出:

sizeof(int):    4

b[0]:   9768976

a:      9768976

b[19]:  9769736

a+190:  9769736

b[0][8]:        8

b[19][9]:       199

很明显,b 已经变成了一个包含 20 个指针的数组,每个元素指针都指向 a 数组的唯一部分,每个部分对应 10 个整数(或 40 个字节)。有人可以解释一下int (*b)[10] = (void*) a; 到底是做什么的吗?具体来说,类型转换(void *) 如何帮助将整个a 分布到b 的多个元素中?如果没有 (void *) 演员表,上述代码将无法编译。

【问题讨论】:

  • “我本来希望 b 是一个只能访问 a 的前 10 个元素的指针” - 那么您是否还期望 int *b = (void *)a; 只能访问一个 int
  • 没有。我想我在那句话中没有很好地表达自己。我将编辑我的问题以更好地解释我的意思。
  • b[0] 是一个由前 10 个整数组成的数组,b[1] 是接下来的 10 个整数,以此类推
  • 正确。这就是我从我发布的输出中得出的结论。我只是不确定这在int (*b)[10] = (void*) a; 语句中是如何工作的。我在在线搜索中找不到其他此类示例。这似乎是一种为大型数组块创建单独指针的方法,这是我之前没有遇到过的。
  • 原理和int *b = (void *)a一样,只是你指向的是10个整数而不是1个整数。

标签: c arrays pointers void-pointers


【解决方案1】:

显然,b 已成为一个包含 20 个指针的数组,每个元素指针指向 a 数组的唯一部分,每个部分对应 10 个整数(或 40 个字节)。

不,b 只是一个指针,而不是一个数组。 b 指向的类型是 array-of-10-ints。如果您认为它可能会更简单:

typedef int row[10]; // A `row` is an array of 10 ints
row* b = (void*) a;

对于任何指针T* pp[n] 是从p[n - 1] 偏移的sizeof (T) 字节(当然,假设nn - 1 是有效索引)。这种情况也不例外。这里T 是一个10 元素int 数组,所以b 的每个元素与其相邻元素相距10 * sizeof (int) 个字节。

具体来说,类型转换(void *) 如何帮助将整个a 分布到b 的多个元素中?如果没有(void *) 演员表,上述代码将无法编译。

在 C 中,指针可以在 void*T* 之间转换,而无需显式转换。为方便起见,上述案例使用void* 强制转换,减少了输入;它允许将右侧转换为左侧所需的任何指针类型。

【讨论】:

  • 你的例子终于让我有所收获。因此,int (*b)[10] 应该被解释为 b 是一个指向 由 10 个整数数组组成的类型 的指针。然后,在将b 分配给更大的内存部分(具有>10 个整数,例如我的问题中a 指向的200 个整数)之后,我们可以通过使用@ 的索引以10 个元素块的形式访问该内存987654348@,前 10 个元素从 b[0][0..9](*(b))[0..9] 开始,后 10 个元素从 b[19][0..9](*(b+19))[0..9] 开始。
  • @NI3 另外,如果您发现我的回答有帮助,您应该点赞。 =)
  • @jamesdin 谢谢。我赞成你的回答。但是,由于我的新帐户,它还不可见。我需要至少 15 的声望才能让我的支持发挥作用。
【解决方案2】:

这样看:

int *a;
a = malloc(200 * sizeof(int));

int (*b)[10];
b = (void *)a;

所以,第一行说a 应该指向int。第二行分配一块内存,可以容纳200个ints,并将这个块的开始地址分配给a。第三行说b 应该指向一个包含 10 个ints 的数组。第四行说应该为b 分配a 持有的地址。

所以现在b 指向一个包含 10 个ints 的数组,该数组从对malloc() 的调用返回的地址开始。由于内存分配给 200 个ints,b 指向一个内存块,该内存块可以容纳 20 个 10 元素数组 int

需要转换为void,因为a 被声明为指向int 的指针;这允许您将a 持有的值存储在b 中,即使它们属于不同类型。我认为这仅仅是为数组分配适当数量的内存的便利。另一种完全避免声明a 的方法是:

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

这第二种方法可能看起来更神秘,也可能不会看起来更神秘,这取决于读者的口味。我想第一种方法的一个优点是它允许您按顺序访问数组的元素,也可以作为二维数组访问。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-16
    • 1970-01-01
    • 2011-04-02
    • 2019-10-27
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 2012-05-16
    相关资源
    最近更新 更多