在动态布尔数组上使用memset是否定义明确?

时间:2018-11-06 12:21:05

标签: c language-lawyer strict-aliasing memset

就严格的别名而言,此代码的行为是否定义明确?

_Bool* array = malloc(n);
memset(array, 0xFF, n);
_Bool x = array[0];

有效类型的规则在memcpymemmove中有特殊情况(C17 6.5§6),在memset中则没有。

我认为有效类型变为unsigned char。因为memset的第二个参数需要转换为unsigned char(C17 7.24.6.1),并且由于有效类型的规则,所以(C17 6.5§6):

  

...或被复制为字符类型的数组,然后是有效类型   修改后的对象的访问权限以及不修改   值是从中复制值的对象的有效类型(如果有)。

  • 问题1:在array调用之后,存储在memset中的数据的有效类型是什么?
  • 问题2:array[0]访问是否因此违反严格的别名?由于_Bool不是严格的别名规则所排除的类型(与字符类型不同)。

1 个答案:

答案 0 :(得分:7)

  1. memset不会更改有效类型。 C11 (C17) 6.5p6:

      
        
    1. 用于访问其存储值的对象的有效类型为   对象的声明类型(如果有)。 [显然不是这样。分配的对象没有声明的类型。 ]

           

      如果将值存储到   通过具有以下类型的左值而没有声明类型的对象   是不是字符类型,则左值的类型变为   该访问以及后续访问的对象的有效类型   不会修改存储值的访问。 [情况并非如此,因为memset使用字符类型的左值! ]

           

      如果已复制一个值   使用memcpymemmove进入没有声明类型的对象   复制为字符类型数组,然后输入有效类型   修改的对象,用于该访问以及随后的访问   不修改值是从哪个有效对象类型   值被复制(如果有)。    [这里也不是这种情况-不会使用memcpymemmove或字符数组来复制]

           

      用于对其他所有   没有声明类型的对象,则该对象的有效类型为   只是用于访问的左值的类型。 [因此,这在我们的情况下必须适用。请注意,这适用于在memset中作为字符进行访问以及取消对array的引用。 ]

    2.   

    由于值存储在lvalue中且字符类型为memset的{​​{1}}中,并且没有从另一个具有字符类型左值的对象复制的字节(该子句的存在是将memcpymemmove等同于通过显式的for循环执行!),它没有得到有效的类型,而没有有效的类型对于通过_Bool 访问的元素,元素是array

    C17标准中的某些部分可能未得到充分说明,但这当然不是其中一种。

  2. array[0]不会违反有效类型规则。

    使用array[0]的值不再合法。它可以(而且很可能是)陷阱值!

    我尝试了以下功能

    #include <stdio.h>
    #include <stdbool.h>        
    
    void f1(bool x, bool y) {
        if (!x && !y) {
            puts("both false");
        }
    }
    
    
    void f2(bool x, bool y) {
        if (x && y) {
            puts("both true");
        }
    }
    
    void f3(bool x) {
        if (x) {
            puts("true");
        }
    }
    
    void f4(bool x) {
        if (!x) {
            puts("false");
        }
    }
    

    使用array[0]作为任何参数-为避免编译时优化,将其分别编译。用-O3编译时,将显示以下消息:

    both true
    true
    

    如果没有任何优化

    both false
    both true
    true
    false