Significado de int (*) (int *) = 5 (ou qualquer valor inteiro)


87

Eu não consigo descobrir isso:

int main() {
    int (*) (int *) = 5;
    return 0;
}

A atribuição acima é compilada com g ++ c ++ 11. Eu sei que int (*) (int *)é um ponteiro para uma função que aceita um (int *)como argumento e retorna um int, mas não entendo como você poderia igualá-lo a 5. No início, pensei que fosse uma função que retornava constantemente 5 (do meu aprendizado recente em F #, provavelmente, haha), então pensei, brevemente, que o ponteiro de função aponta para o local de memória 5, mas isso não funciona, claramente, e nem os valores hexadecimais.

Pensando que poderia ser porque a função retorna um int e que atribuir um int está ok (de alguma forma), eu também tentei isso:

int * (*) (int *) = my_ptr

onde my_ptré do tipo int *, o mesmo tipo deste segundo ponteiro de função, como no primeiro caso com o tipo int. Isso não compila. Atribuir 5 ou qualquer valor int, em vez de my_ptr, também não compila para este ponteiro de função.

Então, o que significa a atribuição?

Atualização 1

Temos a confirmação de que se trata de um bug, conforme demonstrado na melhor resposta. No entanto, ainda não se sabe o que realmente acontece com o valor que você atribui ao ponteiro de função, ou o que acontece com a atribuição. Quaisquer (boas) explicações sobre isso seriam muito apreciadas! Consulte as edições abaixo para obter mais clareza sobre o problema.

Editar 1

Estou usando o gcc versão 4.8.2 (no Ubuntu 4.8.2)

Editar 2

Na verdade, igualar a qualquer coisa funciona no meu compilador. Mesmo igualando-o a uma variável std :: string, ou um nome de função que retorna um duplo, funciona.

Editar 2.1

Curiosamente, torná-lo um ponteiro de função para qualquer função que retorna um tipo de dados que não seja um ponteiro, permitirá que ele seja compilado, como

std::string (*) () = 5.6;

Mas assim que o ponteiro de função é para uma função que retorna algum ponteiro, ele não compila, como com

some_data_type ** (*) () = any_value;

3
Hmm ... não parece certo e o clang não aceita isso. Pode ser uma extensão gcc (ou bug).
— Wintermute de

4
g ++ compila, mas gcc não funciona:error: expected identifier or '(' before ')' token
— tivn

3
@ 0x499602D Observe que o código não dá um nome ao ponteiro. Comint *x = 5 você o nomeou x. Com int * (*x) (int *) = 5ele não vai compilar. (embora isso seja compilado como código C).
— nos

5
Caso de teste reduzido: int(*) = 5; eint(*);
— Johannes Schaub - litb

Respostas:


59

É um bug no g ++.

 int (*) (int *) 

é um nome de tipo.

Em C ++, você não pode ter uma declaração com um nome de tipo sem um identificador.

Portanto, isso compila com g ++.

 int (*) (int *) = 5;

e isso também compila:

 int (*) (int *);

mas ambas são declarações inválidas.

EDITAR :

TC menciona nos comentários o bugzilla bug 60680 com um caso de teste semelhante, mas ainda não foi aprovado . O bug é confirmado no bugzilla.

EDIT2 :

Quando as duas declarações acima estão no escopo do arquivo, o g ++ emite corretamente um diagnóstico (ele falha em emitir o diagnóstico no escopo do bloco).

EDIT3 :

Eu verifiquei e posso reproduzir o problema na última versão do g ++ versão 4 (4.9.2), na última versão de pré-lançamento 5 (5.0.1 20150412) e na última versão experimental 6 (6.0.0 20150412).


5
MSVC rejeitou o código publicado editado comerror C2059: syntax error : ')'
— Weather Vane

Se for um nome de tipo, por que 'int (*) (int *) int_func;' trabalhos?
— Konrad Kapp

1
Para o bugzilla do GCC, "NEW" é um bug confirmado. (Bugs não confirmados são "NÃO CONFIRMADOS").
— TC

4
@KonradKapp: funciona muito bem se você disser o int (*int_func)(int *); que declara um ponteiro de função chamado int_func.
— Edward

3
@KonradKapp C ++ usa notação infixa para colocar o identificador; mesma razão que é int x[5];e nãoint[5] x;
— MM

27

Não é C ++ válido. Lembre-se de que, como seu compilador específico compila, ele não o torna válido. Compiladores, como todo software complexo, às vezes têm bugs e este parece ser um.

Em contraste, clang++reclama:

funnycast.cpp:3:11: error: expected expression
    int (*) (int *) = 5;
          ^
funnycast.cpp:3:18: error: expected '(' for function-style cast or type construction
    int (*) (int *) = 5;
             ~~~ ^
funnycast.cpp:3:19: error: expected expression
    int (*) (int *) = 5;
                  ^
3 errors generated.

Este é o comportamento esperado porque a linha incorreta não é C ++ válido. Ele pretende ser uma atribuição (devido ao =), mas não contém nenhum identificador.


9

Como outras respostas apontaram, é um bug que

int (*) (int *) = 5;

compila. Uma aproximação razoável desta declaração que se esperaria ter um significado é:

int (*proc)(int*) = (int (*)(int*))(5);

Agora procé um ponteiro para função que espera que o endereço 5seja o endereço base de uma função que recebe um int*e retorna um int.

Em alguns microcontroladores / microprocessadores 5 pode haver um endereço de código válido e pode ser possível localizar essa função lá.

Na maioria dos computadores de uso geral, a primeira página de memória (endereços 0-1023para páginas de 4 K) é propositalmente inválida (não mapeada) para capturarnull acessos de ponteiro.

Assim, embora o comportamento dependa da plataforma, pode-se esperar razoavelmente que uma falha de página ocorra quando *procfor invocado (por exemplo, (*proc)(&v)). Antes da hora em que*proc é invocado, nada de anormal acontece.

A menos que você esteja escrevendo um vinculador dinâmico, quase certamente não deveria estar calculando endereços numericamente e atribuindo-os a variáveis ​​de ponteiro para função.


2
/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/cc1plus.exe -da so.cpp

Esta linha de comando gera muitos arquivos intermediários. O primeiro deles so.cpp.170r.expand, diz:

...
int main() ()
{
  int D.2229;
  int _1;

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  _1 = 0;
;;    succ:       3

;;   basic block 3, loop depth 0
;;    pred:       2
<L0>:
  return _1;
;;    succ:       EXIT

}
...

Isso ainda não responde exatamente ao que acontece, mas deve ser um passo na direção certa.


Interessante. Qual é o propósito desses arquivos intermediários?
— Konrad Kapp

@KonradKapp Produzir código de máquina a partir de código humano é um processo bastante complexo (especialmente se você deseja que seu compilador otimize sua saída). Como a compilação é muito complexa, não é feita em uma etapa, a maioria dos compiladores tem alguma forma de Representação Intermediária (IR).
— 11684

2
Outra razão para ter um IR é que, se você tiver um IR bem definido, poderá separar o front-end e o back-end de seu compilador. (Por exemplo, o front-end compila C para o seu IR, o back-end compila o IR para o código de máquina Intel. Agora, se você quiser adicionar suporte a ARM, você só precisa de um segundo back-end. E se quiser compilar Go, você só precisa de um segundo front-end e, além disso, o compilador Go suporta imediatamente Intel e ARM, já que você pode reutilizar os dois back-ends.
— 11684

@ 11684 OK, faz sentido. Muito interessante. Não consegui determinar que linguagem Roland deu nesta resposta ... parece algum tipo de montagem misturada com C.
— Konrad Kapp

IR não precisa ser imprimível; Não tenho ideia do que o gcc usa, pode ser apenas uma representação para impressão @KonradKapp
— 11684
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.