char数组可以与任何数据类型一起使用吗?

时间:2016-07-21 17:20:13

标签: c arrays types c11

malloc()函数返回类型为void*的指针。它根据作为参数传递的size_t值以字节为单位分配内存。生成的分配是原始字节,可以与C中的任何数据类型一起使用(无需转换)。

在返回char的函数中声明类型为void *的数组是否可以与任何数据类型一起使用,例如malloc的结果分配?

例如,

#include <stdio.h>

void *Stat_Mem();

int main(void)
{
    //size : 10 * sizeof(int)
    int buf[] = { 1,2,3,4,5,6,7,8,9,10 };

    int *p = Stat_Mem();

    memcpy(p, buf, sizeof(buf));

    for (int n = 0; n < 10; n++) {
        printf("%d ", p[n]);
    }
    putchar('\n');

    return 0;
}

void *Stat_Mem()
{
    static char Array[128];
    return Array;
}

3 个答案:

答案 0 :(得分:5)

静态对象Array的声明类型为char。该对象的有效类型是它的声明类型。无法更改静态对象的有效类型,因此对于程序的其余部分,Array的有效类型为char

如果您尝试访问类型与此列表 1 不兼容的对象的值,则行为未定义。

您的代码尝试使用Array类型访问int的存储值。此类型与类型char不兼容,并且不在异常列表中,因此当您使用int指针p读取数组时,行为未定义:

printf("%d ", p[n]);

1 (引自:ISO:IEC 9899:201X 6.5表达式7)
对象的存储值只能由左值访问  具有以下类型之一的表达式:
 - 一种  与对象的有效类型兼容,
  - 合格的  与对象的有效类型兼容的类型的版本,
  - 对应于的有符号或无符号类型的类型  有效的对象类型,
  - 签名或未签名的类型  类型对应于有效类型的限定版本  对象,
- 包含其中一个的聚合或联合类型  其成员之间的上述类型(包括,递归,a  分包或联合的成员)或
- 字符类型。

答案 1 :(得分:3)

不可以因为可能的对齐问题而无法将任意字节数组用于任意类型。标准在6.3.2.3转换/指针(强调我的)中说:

  

指向对象或不完整类型的指针可能会转换为指向其他对象的指针   对象或不完整的类型。 如果生成的指针未正确对齐   指向类型,行为未定义。否则,当再次转换回来时,   结果应与原始指针进行比较。

作为最小对齐要求的char,您无法确保您的char数组将正确对齐任何其他类型。这就是为什么malloc保证malloc获得的缓冲区(即使它是void *)具有最大可能的对齐要求,以便能够接受任何其他类型。

我认为

union {
    char buf[128];
    long long i;
    void * p;
    long double f;
};

应该对任何类型都有正确的对齐方式,因为它与最大的基本类型兼容(如6.2.5类型中所定义)。我很确定它适用于所有常见的实现(gcc,clang,msvc,...)但遗憾的是我无法找到标准允许的任何确认。基本上是因为6.5表达式§7中定义的严格别名规则:

  

对象的存储值只能由具有其中一个的左值表达式访问   以下类型:

     
      
  • 与对象的有效类型兼容的类型
  •   
  • 与对象的有效类型兼容的类型的限定版本,
  •   
  • 对应于有效类型的有符号或无符号类型的类型   对象,
  •   
  • 对应于合格版本的有符号或无符号类型的类型   有效的对象类型,
  •   
  • 聚合或联合类型,其中包含上述类型之一   成员(包括,递归地,子集合或包含的联合的成员),或
  •   
  • 字符类型。
  •   

所以恕我直言,没有使用malloc构建自定义分配器的可移植和标准符合方式。

答案 2 :(得分:-1)

如果读取C89标准的基本原理,存在类型别名规则的唯一原因是避免要求编译器做出“最坏情况的混叠假设”。给出的例子是:

    int a;
    void f( double * b )
    {
        a = 1;
        *b = 2.0;
        g(a);
    }

如果程序在一个联合内创建一个“char”数组,其中包含哪些符号适合任何类型,取其地址,并且永远不会访问该结构的存储,除非通过结果指针,应该没有理由别名规则应该会造成任何困难。

值得注意的是,标准的作者认识到实施可以同时兼容但无用;见C89 2.2.4.1的基本原理:

  

虽然缺乏执行可能会设法满足这一要求的计划,但仍然没有成功,但委员会认为,这种聪明才智可能需要做更多的工作而不是做一些有用的工作。委员会的意义在于,实施者不应将翻译限制解释为硬连线参数的值,而应将其作为判断实施的一套标准。

虽然该特定声明是关于实施限制的,但将C89解释为与之前的C语言甚至远程兼容的唯一方法是将其视为更广泛地应用:标准不尝试详尽地指出程序应该能够做的所有事情,但依赖于编译器编写者的一些常识。

使用字符类型数组作为任何类型的后备存储,假设有一个确保对齐问题得到处理,不应该导致任何非钝写编译器的问题。标准没有要求编译器编写者允许这样的事情,因为他们认为没有理由期望他们这样做。不幸的是,他们未能预见到21世纪语言的发展方向。