Как объявить массив, созданный с использованием 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);
создаст ошибку компиляции.