Eu presumo que o seguinte me dará 10 ints voláteis
volatile int foo[10];
No entanto, não acho que o seguinte faça a mesma coisa.
volatile int* foo; foo = malloc(sizeof(int)*10);
Por favor, corrija-me se eu estiver errado sobre isso e como posso ter uma série volátil de itens usando malloc.
Obrigado.
int volatile * foo;
leia da direita para a esquerda “foo é um ponteiro para um int volátil”
Assim, seja qual for o int que você acessar através do foo, o int será volátil.
PS
int * volatile foo; // "foo is a volatile pointer to an int"
==
volatile int * foo; // foo is a pointer to an int, volatile
Significado foo é volátil. O segundo caso é realmente apenas uma sobra da regra geral da direita para a esquerda. A lição a ser aprendida é adquirir o hábito de usar
char const * foo;
em vez do mais comum
const char * foo;
Se você quer coisas mais complicadas como “ponteiro para funcionar retornando ponteiro para int” para fazer algum sentido.
PS, e isso é um biggy (e a principal razão pela qual estou adicionando uma resposta):
Observo que você incluiu “multithreading” como tag. Você percebe que volátil faz pouco / nada de bom em relação ao multithreading?
volatile int* foo;
é o caminho a percorrer. O qualificador de tipo volátil funciona exatamente como o qualificador do tipo const . Se você quisesse um ponteiro para um array constante de inteiro, você escreveria:
const int* foo;
enquanto que
int* const foo;
é um ponteiro constante para um inteiro que pode ser alterado. volátil funciona da mesma maneira.
Sim, isso vai funcionar. Não há nada diferente sobre a memory real que é volatile
. É apenas uma maneira de dizer ao compilador como interagir com essa memory.
Eu acho que o segundo declara que o ponteiro é volátil, não o que ele aponta. Para conseguir isso, acho que deveria ser
int * volatile foo;
Essa syntax é aceitável para o gcc
, mas estou tendo problemas em convencer-me de que faz algo diferente.
Eu encontrei uma diferença com o gcc -O3
(otimização total). Para este código de teste (bobo):
volatile int v [10]; int * volatile p; int main (void) { v [3] = p [2]; p [3] = v [2]; return 0; }
Com instruções volatile
e omitindo (x86) que não mudam:
movl p, %eax movl 8(%eax), %eax movl %eax, v+12 movl p, %edx movl v+8, %eax movl %eax, 12(%edx)
Sem volátil, ele pula o recarregamento de 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
Depois de muitos outros experimentos científicos tentando encontrar uma diferença, concluo que não há diferença. volatile
desativa todas as otimizações relacionadas à variável que reutilizaria um valor definido subseqüentemente. Pelo menos com x86 gcc (GCC) 4.1.2 20070925 (Red Hat 4.1.2-33). 🙂
Muito obrigado a wallyk, eu era capaz de conceber algum código usar seu método para gerar alguma assembly para provar a mim mesmo a diferença entre os diferentes methods de ponteiro.
usando o código: e compilando com -03
int main (void) { while(p[2]); return 0; }
Quando p é simplesmente declarado como ponteiro, ficamos presos em um loop que é impossível sair. Note que se este fosse um programa multiencadeado e um thread diferente escrevesse p [2] = 0, então o programa sairia do loop while e terminaria normalmente.
int * p; ============ LCFI1: movq _p(%rip), %rax movl 8(%rax), %eax testl %eax, %eax jne L6 xorl %eax, %eax leave ret L6: jmp L6
observe que a única instrução para L6 é ir para L6.
quando p é ponteiro volátil
int * volatile p; ============== L3: movq _p(%rip), %rax movl 8(%rax), %eax testl %eax, %eax jne L3 xorl %eax, %eax leave ret
aqui, o ponteiro p é recarregado em cada iteração de loop e, como conseqüência, o item da matriz também é recarregado. No entanto, isso não estaria correto se quiséssemos uma matriz de inteiros voláteis, pois isso seria possível:
int* volatile p; .. .. int* j; j = &p[2]; while(j);
e resultaria no loop que seria impossível terminar em um programa multithread.
finalmente, esta é a solução correta como tony bem explicado.
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
Neste caso, o endereço de p [2] é mantido no valor de registro e não é carregado da memory, mas o valor de p [2] é recarregado da memory em cada ciclo de loop.
Observe também que
int volatile * p; .. .. int* j; j = &p[2]; while(j);
irá gerar um erro de compilation.