Как объявить массив, созданный с использованием malloc, быть volatile в С++

Я предполагаю, что следующее даст мне 10 volatile ints

volatile int foo[10];

Однако я не думаю, что следующее будет делать то же самое.

volatile int* foo;
foo = malloc(sizeof(int)*10);

Пожалуйста, исправьте меня, если я ошибаюсь в этом и как могу иметь изменчивый массив элементов, используя malloc.

Спасибо.

Ответы

Ответ 1

int volatile * foo;

читать справа налево "foo - это указатель на изменчивый int"

так что независимо от того, что вы получаете через foo, int будет неустойчивым.

P.S.

int * volatile foo; // "foo is a volatile pointer to an int"

==

volatile int * foo; // foo is a pointer to an int, volatile

Значение foo нестабильно. Второй случай - это просто остаточное правило общего права налево. Урок, который нужно извлечь, - это привычка использовать

char const * foo;

вместо более общего

const char * foo;

Если вы хотите, чтобы более сложные вещи, такие как "указатель на функцию, возвращающий указатель на int", имели какой-то смысл.

P.S., и это большая (и основная причина, по которой я добавляю ответ):

Я отмечаю, что вы включили "многопоточность" в качестве тега. Вы понимаете, что волатильность мало/ничего хорошего в отношении многопоточности?

Ответ 2

volatile int* foo;

- это путь. Тест volatile работает так же, как и квалификатор типа const. Если вам нужен указатель на постоянный массив целых чисел, вы должны написать:

const int* foo;

тогда

int* const foo;

- постоянный указатель на целое число, которое может быть изменено. volatile работает одинаково.

Ответ 3

Да, это сработает. В фактической памяти volatile нет ничего особенного. Это просто способ сообщить компилятору, как взаимодействовать с этой памятью.

Ответ 4

Я думаю, что вторая заявляет, что указатель будет изменчивым, а не тем, на что он указывает. Чтобы получить это, я думаю, что это должно быть

int * volatile foo;

Этот синтаксис допустим для gcc, но у меня возникла проблема убедить себя в том, что он делает что-то другое.

Я нашел разницу с gcc -O3 (полная оптимизация). Для этого (глупого) тестового кода:

volatile int  v [10];
int * volatile p;

int main (void)
{
        v [3] = p [2];
        p [3] = v [2];
        return 0;
}

С volatile и отсутствующими инструкциями (x86):

    movl    p, %eax
    movl    8(%eax), %eax
    movl    %eax, v+12
    movl    p, %edx
    movl    v+8, %eax
    movl    %eax, 12(%edx)

Без изменчивости он пропускает перезагрузку p:

    movl    p, %eax
    movl    8(%eax), %edx    ; different since p being preserved
    movl    %edx, v+12
    ; 'p' not reloaded here
    movl    v+8, %edx
    movl    %edx, 12(%eax)   ; p reused

После многих научных экспериментов, пытающихся найти разницу, я заключу, что нет никакой разницы. volatile отключает все оптимизации, связанные с переменной, которая будет повторно использовать установленное впоследствии значение. По крайней мере, с x86 gcc (GCC) 4.1.2 20070925 (Red Hat 4.1.2-33).: -)

Ответ 5

Большое спасибо wallyk, я смог разработать какой-то код, используя его метод, чтобы сгенерировать некоторую сборку, чтобы доказать себе разницу между различными методами указателя.

с помощью кода: и компиляция с -03

int main (void)
{
        while(p[2]);
        return 0;
}

когда p просто объявляется как указатель, мы застреваем в цикле, из которого невозможно выйти. Обратите внимание: если это была многопоточная программа, а другой поток написал p [2] = 0, то программа вырвалась бы из цикла while и завершила бы нормально.

int * p;
============
LCFI1:
        movq    _p(%rip), %rax  
        movl    8(%rax), %eax   
        testl   %eax, %eax
        jne     L6              
        xorl    %eax, %eax
        leave
        ret
L6:
        jmp     L6

заметим, что единственной инструкцией для L6 является переход к L6.

==

когда p является volatile pointer

int * volatile p;
==============
L3:
        movq    _p(%rip), %rax
        movl    8(%rax), %eax
        testl   %eax, %eax
        jne     L3
        xorl    %eax, %eax
        leave
        ret 

здесь указатель p получает перезагрузку каждой итерации цикла и, как следствие, элемент массива также перезагружается. Тем не менее, это было бы неправильно, если бы мы хотели получить массив летучих целых чисел, поскольку это было бы возможно:

int* volatile p;
..
..
int* j;
j = &p[2];
while(j);

и приведет к тому, что цикл, который невозможно будет завершить в многопоточной программе.

==

наконец, это правильное решение, как хорошо объяснил Тони.

int volatile * p;
LCFI1:
        movq    _p(%rip), %rdx
        addq    $8, %rdx
        .align 4,0x90
L3:
        movl    (%rdx), %eax
        testl   %eax, %eax
        jne     L3
        leave
        ret 

В этом случае адрес p [2] сохраняется в регистре и не загружается из памяти, но значение p [2] перезагружается из памяти в каждом цикле цикла.

также отметим, что

int volatile * p;
..
..
int* j;
j = &p[2];
while(j);

создаст ошибку компиляции.