Как объявить массив, созданный с помощью malloc, как изменчивый в c ++ - PullRequest
15 голосов
/ 21 февраля 2010

Я предполагаю, что следующее даст мне 10 летучих целых

volatile int foo[10];

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

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

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

Спасибо.

Ответы [ 5 ]

12 голосов
/ 23 февраля 2010
int volatile * foo;

read from right to left "foo - указатель на volatile int"

, поэтому независимо от того, к чему вы обращаетесь через foo, int будет volatile.

PS

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«Чтобы иметь какой-либо смысл.

PS, и это большая проблема (и главная причина, по которой я добавляю ответ):

Я отмечаю, что вы включили« многопоточность »в качестве тега.Понимаете ли вы, что volatile мало или ничего хорошего не имеет в отношении многопоточности?

6 голосов
/ 21 февраля 2010
volatile int* foo;

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

const int* foo;

тогда

int* const foo;

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

3 голосов
/ 21 февраля 2010

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

2 голосов
/ 21 февраля 2010

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

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). : -)

1 голос
/ 23 февраля 2010

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

с использованием кода: и компиляция с -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.

==

когда р является летучим указателем

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);

приведет к ошибке компиляции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...