在C

时间:2016-10-04 00:03:43

标签: c arrays pointers void-pointers

我遇到过类似以下的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]a[i]保留与i=0..9相同的数据(以及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 *)强制转换,上述代码将无法编译。

2 个答案:

答案 0 :(得分:0)

这样看:

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

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

因此,第一行表示a应指向int。第二行分配一个可容纳200 int s的内存块,并将此块的起始地址分配给a。第三行表示b应该指向一个10 int的数组。第四行表示b应分配a所持的地址。

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

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

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

根据读者的口味,这第二种方法可能会或可能不会更加神秘。我想第一种方法的一个优点是,它允许您按顺序和作为二维数组访问数组的元素。

答案 1 :(得分:0)

  

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

不,b只是一个指针,而不是一个数组。类型b指向的数组为10 - int s。如果您将其视为:

,也许这会更简单
typedef int row[10]; // A `row` is an array of 10 ints
row* b = (void*) a;

对于任何指针T* pp[n]sizeof (T)个字节偏离p[n - 1](假设nn - 1是有效索引,课程)。这个案子没有什么不同;此处T是一个10个元素的int数组,因此b的每个元素都距离其相邻元素10 * sizeof (int)个字节。

  

具体来说,类型转换(void *)如何帮助在a的多个元素之间分发整个b?如果没有(void *)强制转换,上述代码将无法编译。

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