Хорошо ли использовать memset для динамического массива bool? - PullRequest
0 голосов
/ 06 ноября 2018

Является ли этот код хорошо определенным поведением с точки зрения строгого алиасинга?

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

Правило эффективного типа имеет особые случаи для memcpy и memmove (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 голосов
/ 06 ноября 2018
  1. memset не не меняет эффективный тип. C11 (C17) 6,5p6:

    1. Эффективный тип объекта для доступа к его сохраненному значению: объявленный тип объекта, если таковой имеется. [Это явно не тот случай. Выделенный объект не имеет объявленного типа. ]

      Если значение сохраняется в объект, не имеющий объявленного типа через lvalue, имеющий тип, который если не тип символа , то тип lvalue становится эффективный тип объекта для этого доступа и для последующего доступы, которые не изменяют сохраненное значение. [это не тот случай, поскольку l 101 значение типа символа используется memset! ]

      Если значение скопировано в объект без объявленного типа, используя memcpy или memmove, или копируется как массив типа символа , затем эффективный тип измененный объект для этого доступа и для последующих обращений, которые делают не изменять значение - это эффективный тип объекта, из которого значение копируется, если оно есть. [здесь тоже не так - он не копируется с memcpy, memmove или массивом символов]

      Для всех других доступов к объект не имеет объявленного типа, эффективный тип объекта просто тип lvalue, используемый для доступа. [следовательно, это должно применяться в нашем случае. Обратите внимание, что это относится к доступу к нему как к символам внутри memset, а также к разыменованию array. ]

    Поскольку значения хранятся с , lvalue, который имеет тип символа внутри memset, а не , имеют байты, скопированные из другого объекта с l-значениями типа символа ( Предложение существует, чтобы приравнять memcpy и memmove к выполнению того же с явным циклом 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
    
...