你总能在C中访问包含结构数据的适当对齐的缓冲区吗?

时间:2016-12-26 21:34:10

标签: c c11

我在C中有结构类型的数据:

typedef struct {
  type0 field0;
  type1 field1;
} foo_struct;

foo_struct foo;

现在让我们说我在虚拟内存中以某种方式分配了缓冲区,大小为sizeof(foo_struct)

char *buf = <some allocation method>

然后我将数据从foo复制到buf,例如memcpy

然后我想访问buf中的字段,如下所示:

((foo_struct *)buf)->fieldn

这是否保证(通过C11标准)始终有效?

另一个问题的人(关于不同的主题) ,似乎在说,是的,这是有效的,特别是假设buf在页面边界上是完全一致的。

是的,实际上是有保证的。但我认为,无论它是多么好&#34;对齐&#34;,页面边界,或者不是,标准都没有100%的保证。在那儿?

3 个答案:

答案 0 :(得分:7)

  

您是否可以始终访问包含适当对齐缓冲区的结构   C?中的数据

如果缓冲区在动态内存中,则为是。

$.ajaxSetup({ traditional: true });

var uri = "";

$("#enginesOuputWaiter").show();    
$.ajax({
    type: "GET",
    url: uri,
    dataType: "jsonp",
    ContentType:'application/javascript',
    data :{'text' : article},
    error: function(result) {
        $("#enginesOuputWaiter").hide();
        if(result.statusText = 'success') {
            console.log("ok");
            console.log(result);
        } else {
            $("#enginesOuput").text('Invalid query.');
        }
    }
});

基本上就像

char *buf = malloc(1000000);
if(buf)
   ((foo_struct *)buf)->fieldn

这可以保证有效。

如果缓冲区是静态分配的或自动的,那么没有。 别名规则(6.5p7)阻止您执行:

foo_struct *buf = malloc(1000000);
if(buf)
    buf->fieldn

即使缓冲区的对齐就足够了。

(注意:1后跟多个0 ==足够大)

答案 1 :(得分:6)

这取决于cierre = Cierre.objects.annotate( count_ruta = Count('ruta') ).order_by( 'ruta' ).filter( count_ruta__gt=1 ).distinct() 是什么。以下是关于malloc的standard [7.22.3]所说的内容:

  

如果分配成功,则返回指针,以便可以将其分配给指向具有基本对齐要求的任何类型对象的指针,然后用于在分配的空间中访问此类对象或此类对象的数组(直到空间被明确释放)。

因此,根据标准,您可以在使用malloc时执行您所要求的操作。大多数其他编写良好的内存分配器也应满足此要求。

答案 2 :(得分:1)

C89的别名规则在历史上被解释为仅仅识别需要所有符合要求的实现来识别别名的最小情况,而这些情况又限于对所有实现都有用的情况。当时参与其中的每个人都很明显,其他形式的别名在许多平台上都很有用,质量实现应该在实际时识别这些形式。

虽然C99和C11假装更全面地描述质量实现应该识别别名的情况,但作者从未做过任何真正的努力来处理一些应该易于处理的情况,并且在系统编程中绝对至关重要。因为即使对这种情况的悲观处理通常也只会对性能产生很小的影响,编译器编写者过去常常谨慎对待这些事情,并且认为这些代码应该被视为常识。

如果编译器编写者使用某些常识,使用您描述的方法(而不是malloc / free)的内存管理将可靠地工作,因为大多数有用的别名优化(而不是代码破坏别名&#34;优化&#34;)不需要跨指针类型转换移动对象访问。不幸的是,无论出于何种原因,编译器包含一个常识&#34;并不是时髦的。别名模式。

虽然指定对象的对齐是没有用的。存储如果不能以受益于它的方式使用该存储,则标准不要求编译器允许使用通过对齐指令声明的存储底层对象。因为编译器编写者需要更多地关注标准所不需要的东西而不是需要什么,所以许多编译器确保任何类型的自定义内存管理代码都能工作的唯一方法就是要么使用特定于编译器的指令,要么完全禁用别名优化。