'const int' vs. 'int const' como parâmetros de função em C ++ e C

116

Considerar:

int testfunc1 (const int a)
{
  return a;
}

int testfunc2 (int const a)
{
  return a;
}

Essas duas funções são iguais em todos os aspectos ou há uma diferença?

Estou interessado em uma resposta para a linguagem C, mas se houver algo interessante na linguagem C ++, gostaria de saber também.

Nils Pipenbrinck
fonte
Existe uma palavra-chave const em C agora? Não costumava haver, mas não estou tão familiarizado com o padrão C 99.
Onorio Catenacci,
8
Você não precisa ser. C90 é o suficiente. Mas não estava no K&R C original.
Mark Baker,
3
É uma palavra-chave em C89 e ANSI. Não sei se era uma palavra-chave na época de Kerningham e Richie.
Nils Pipenbrinck,
7
Este site traduz "gibberish C" para o inglês cdecl.org
Motti
5
Eu diria "gibberish C to English gibberish", mas ainda bem :)
Kos

Respostas:

175

const Te T constsão idênticos. Com os tipos de ponteiro, fica mais complicado:

  1. const char* é um ponteiro para uma constante char
  2. char const* é um ponteiro para uma constante char
  3. char* const é um ponteiro constante para um (mutável) char

Em outras palavras, (1) e (2) são idênticos. A única maneira de fazer a ponta (em vez da ponta) consté usar um sufixo- const.

É por isso que muitas pessoas preferem sempre colocar constdo lado direito do tipo (estilo "Const do Leste"): isso torna sua localização em relação ao tipo consistente e fácil de lembrar (também anedoticamente parece tornar mais fácil de ensinar para iniciantes )

Konrad Rudolph
fonte
2
C tem const, dado: static const char foo [] = "foo"; é melhor você não alterar foo.
James Antill,
4
K&R C não tinha const; C90 (e C99) sim. É um pouco limitado em comparação com C ++, mas é útil.
Mark Baker,
isso também é válido para referências?
Ken,
1
@Ken Sim, é o mesmo.
Konrad Rudolph
1
@ étale-cohomology Bom argumento, adicionado. Deveria ter estado lá o tempo todo.
Konrad Rudolph
339

O truque é ler a declaração de trás para frente (da direita para a esquerda):

const int a = 1; // read as "a is an integer which is constant"
int const a = 1; // read as "a is a constant integer"

Ambos são a mesma coisa. Portanto:

a = 2; // Can't do because a is constant

O truque da leitura reversa é especialmente útil quando você está lidando com declarações mais complexas, como:

const char *s;      // read as "s is a pointer to a char that is constant"
char c;
char *const t = &c; // read as "t is a constant pointer to a char"

*s = 'A'; // Can't do because the char is constant
s++;      // Can do because the pointer isn't constant
*t = 'A'; // Can do because the char isn't constant
t++;      // Can't do because the pointer is constant
Ates Goral
fonte
5
e "char const * u"? Isso indica "Um ponteiro para um caractere constante" ou "um ponteiro que é constante para um caractere"? Parece ambíguo. O padrão diz o primeiro, mas para descobrir isso, você deve levar em consideração as regras de precedência e associatividade.
Panayiotis Karabassis
5
@PanayiotisKarabassis Todos devem ser tratados como uma cadeia de adjetivos, sem trocar de lugar. char const *, lido da esquerda para a direita é: "ponteiro, const, char". É um ponteiro para const char. Quando você diz "um ponteiro que é constante", o adjetivo "constante" está no ponteiro. Então, para esse caso, sua lista de adjetivos deveria ser: "const, pointer, char". Mas você está certo, esse truque é ambíguo. É realmente um "truque", mais do que uma "regra" definitiva.
Ates Goral
5
Quando você declara uma combinação selvagem de array, função, ponteiro e ponteiro de função, a leitura retroativa não funciona mais (infelizmente). No entanto, você pode ler essas declarações confusas em um padrão espiral . Outros ficaram tão frustrados com eles que inventaram o Go.
Martin JH de
@Martin JH: Eles não poderiam ser separados por meio de typedefs? E / ou usando referências para eliminar caminhos indiretos?
Peter Mortensen
14

Não há diferença. Ambos declaram que "a" é um número inteiro que não pode ser alterado.

O lugar onde as diferenças começam a aparecer é quando você usa ponteiros.

Ambos estes:

const int *a
int const *a

declare "a" como um ponteiro para um inteiro que não muda. "a" pode ser atribuído, mas "* a" não.

int * const a

declara "a" como um ponteiro constante para um inteiro. "* a" pode ser atribuído, mas "a" não.

const int * const a

declara "a" como um ponteiro constante para um número inteiro constante. Nem "a" nem "* a" podem ser atribuídos.

static int one = 1;

int testfunc3 (const int *a)
{
  *a = 1; /* Error */
  a = &one;
  return *a;
}

int testfunc4 (int * const a)
{
  *a = 1;
  a = &one; /* Error */
  return *a;
}

int testfunc5 (const int * const a)
{
  *a = 1;   /* Error */
  a = &one; /* Error */
  return *a;
}
Andru Luvisi
fonte
O último exemplo é a explicação mais simples, ótimo!
exru de
7

Prakash está correto ao dizer que as declarações são as mesmas, embora um pouco mais de explicação sobre o caso do ponteiro possa ser necessário.

"const int * p" é um ponteiro para um int que não permite que o int seja alterado por meio desse ponteiro. "int * const p" é um ponteiro para um int que não pode ser alterado para apontar para outro int.

Consulte https://isocpp.org/wiki/faq/const-correctness#const-ptr-vs-ptr-const .

Fred Larson
fonte
A âncora ("faq-18.5.) Está quebrada. A qual deve se referir (há várias com" const "e" * ")?
Peter Mortensen
@PeterMortensen: Sim, link podre. Obrigado. Eu atualizei o link.
Fred Larson
5

const inté idêntico a int const, como é verdadeiro com todos os tipos escalares em C. Em geral, declarar um parâmetro de função escalar como constnão é necessário, uma vez que a semântica de chamada por valor de C significa que quaisquer alterações na variável são locais para sua função envolvente.

Emerick Rogul
fonte
4

Esta não é uma resposta direta, mas uma dica relacionada. Para manter as coisas retas, sempre uso a convecção "colocar constdo lado de fora", onde por "fora" quero dizer extrema esquerda ou extrema direita. Dessa forma, não há confusão - a const se aplica à coisa mais próxima (o tipo ou o *). Por exemplo,



int * const foo = ...; // Pointer cannot change, pointed to value can change
const int * bar = ...; // Pointer can change, pointed to value cannot change
int * baz = ...; // Pointer can change, pointed to value can change
const int * const qux = ...; // Pointer cannot change, pointed to value cannot change
Pat Notz
fonte
6
Talvez seja melhor usar a regra "const faz const tudo o que resta dele". Por exemplo, "int * const foo" torna o ponteiro "const", porque o ponteiro é deixado para ele. No entanto, você escreveria a segunda linha "int const * bar", torna o int const, porque é deixado para ele. "int const * const * qux", torna ambos, o int e o ponteiro const, porque qualquer um é deixado para ele.
Mecki
4

Eles são iguais, mas em C ++ há um bom motivo para sempre usar const à direita. Você será consistente em todos os lugares porque as funções-membro const devem ser declaradas desta forma:

int getInt() const;

Ele muda o thisponteiro na função de Foo * constpara Foo const * const. Veja aqui.

Nick Westgate
fonte
3
Este é um tipo completamente diferente de const.
Justin Meiners
1
Por que isso é completamente diferente? Diferente o suficiente para ganhar um voto negativo.
Nick Westgate
1
Sim, a pergunta é sobre a diferença entre "const int" e "int const", sua resposta não tem nada a ver com isso.
Justin Meiners
1
Eu disse que eles são iguais. Ainda assim, as respostas aceitas e mais votadas também fornecem informações extras sobre os tipos de ponteiro. Você votou contra eles também?
Nick Westgate
3

Sim, eles são iguais por apenas int

e diferente para int*

prakash
fonte
5
(const int *) e (int const *) são iguais, apenas diferentes de (int * const).
James Antill,
3

Acho que, neste caso, são iguais, mas aqui está um exemplo em que a ordem é importante:

const int* cantChangeTheData;
int* const cantChangeTheAddress;
user7545
fonte
2
Na verdade, mas int const * é o mesmo que o primeiro, então a ordem de int e const não importa, é apenas a ordem de * e const que importa.
Mark Baker,