Qual é o melhor para usar entre as instruções abaixo em C?
static const int var = 5;
ou
#define var 5
ou
enum { var = 5 };
Qual é o melhor para usar entre as instruções abaixo em C?
static const int var = 5;
ou
#define var 5
ou
enum { var = 5 };
Respostas:
Depende do valor que você precisa. Você (e todo mundo até agora) omitiu a terceira alternativa:
static const int var = 5;
#define var 5
enum { var = 5 };
Ignorando problemas sobre a escolha do nome, então:
Portanto, na maioria dos contextos, prefira o 'enum' sobre as alternativas. Caso contrário, o primeiro e o último marcador provavelmente serão os fatores de controle - e você precisará pensar mais se precisar satisfazer os dois ao mesmo tempo.
Se você estivesse perguntando sobre C ++, usaria a opção (1) - a const estática - sempre.
enum
é que eles são implementados como int
([C99] 6.7.2.2/3). A #define
permite que você especifique sem assinatura e com U
e com L
sufixos, além de especificar const
um tipo. enum
pode causar problemas nas conversões de tipo comuns.
enum
nem #define
usa espaço extra, por si só. O valor aparecerá no código do objeto como parte das instruções, em vez de ser alocado para armazenamento no segmento de dados, no heap ou na pilha. Você terá algum espaço alocado para o static const int
arquivo, mas o compilador poderá otimizá-lo se você não usar um endereço.
enum
s (e static const
): eles não podem ser alterados. a define
pode ser #undefine
onde d enum
e static const
são fixados no valor fornecido.
De um modo geral:
static const
Porque respeita o escopo e é seguro para o tipo.
A única ressalva que pude ver: se você deseja que a variável seja possivelmente definida na linha de comando. Ainda existe uma alternativa:
#ifdef VAR // Very bad name, not long enough, too general, etc..
static int const var = VAR;
#else
static int const var = 5; // default value
#endif
Sempre que possível, em vez de macros / elipses, use uma alternativa segura para o tipo.
Se você realmente precisa usar uma macro (por exemplo, deseja __FILE__
ou __LINE__
), é melhor nomear sua macro com muito cuidado: em sua convenção de nomenclatura Boost recomenda todas as letras maiúsculas, começando pelo nome do projeto (aqui BOOST_ ), ao ler a biblioteca, você notará que isso é (geralmente) seguido pelo nome da área específica (biblioteca) e, em seguida, com um nome significativo.
Geralmente faz para nomes longos :)
static
cujo endereço é usado devem permanecer; e se o endereço for escolhido, não seria possível usar um #define
ou enum
(sem endereço) ... então, eu realmente não consigo ver qual alternativa poderia ter sido usada. Se você pode acabar com a "avaliação do tempo de compilação", pode estar procurando extern const
.
#if
pode ser preferível ao longo #ifdef
de bandeiras booleanas, mas, neste caso, seria impossível para definir var
como 0
a partir da linha de comando. Portanto, neste caso, #ifdef
faz mais sentido, desde que 0
seja um valor legal para var
.
Em C, especificamente? Em C, a resposta correta é: use #define
(ou, se apropriado enum
)
Embora seja benéfico ter as propriedades de escopo e digitação de um const
objeto, na realidade os const
objetos em C (em oposição ao C ++) não são constantes verdadeiras e, portanto, geralmente são inúteis na maioria dos casos práticos.
Portanto, em C, a escolha deve ser determinada pela forma como você planeja usar sua constante. Por exemplo, você não pode usar um const int
objeto como um case
rótulo (enquanto uma macro funcionará). Você não pode usar umconst int
objeto como uma largura de campo de bits (enquanto uma macro funcionará). No C89 / 90, você não pode usar um const
objeto para especificar um tamanho de matriz (enquanto uma macro funcionará). Mesmo em C99, você não pode usar um const
objeto para especificar um tamanho de matriz quando precisar de um VLA não matriz que .
Se isso é importante para você, ele determinará sua escolha. Na maioria das vezes, você não terá escolha a não ser usar #define
em C. E não se esqueça de outra alternativa, que produz constantes verdadeiras em C - enum
.
No C ++, os const
objetos são constantes verdadeiras; portanto, no C ++, é quase sempre melhor preferir a const
variante ( static
embora não seja necessário explicitar o C ++).
const int
objetos em rótulos de maiúsculas e minúsculas é ilegal em todas as versões da linguagem C. (Obviamente, o seu compilador é livre para suportá-lo como uma extensão de linguagem semelhante ao C ++ não padrão.) #
const
significa somente leitura. const int r = rand();
é perfeitamente legal.
constexpr
em comparação com const
especialmente com os stl
contêineres como array
ou bitset
.
switch()
declaração, não em case
uma. Eu também fui pego nessa também ☺
A diferença entre static const
e #define
é que o primeiro usa a memória e o posterior não usa a memória para armazenamento. Em segundo lugar, você não pode passar o endereço de um#define
enquanto você pode passar o endereço de umstatic const
. Na verdade, depende de em que circunstâncias estamos, precisamos selecionar um entre esses dois. Ambos estão no seu melhor em diferentes circunstâncias. Por favor, não assuma que um é melhor que o outro ... :-)
Se esse fosse o caso, Dennis Ritchie teria mantido o melhor sozinho ... hahaha ... :-)
const
use memória. O GCC (testado com 4.5.3 e algumas versões mais recentes) otimiza facilmente o const int
literal direto em seu código ao usar -O3. Portanto, se você desenvolver um desenvolvimento incorporado com pouca RAM (por exemplo, AVR), poderá usar com segurança consts C se usar o GCC ou outro compilador compatível. Eu não testei, mas espero que o Clang faça a mesma coisa.
Em C #define
é muito mais popular. Você pode usar esses valores para declarar tamanhos de matriz, por exemplo:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
O ANSI C não permite que você use static const
s neste contexto, tanto quanto eu sei. Em C ++, você deve evitar macros nesses casos. Você pode escrever
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
e até deixar de fora static
porque a ligação interna const
já está implícita [somente em C ++].
const int MY_CONSTANT = 5;
em um arquivo e acessá-lo extern const int MY_CONSTANT;
em outro. Não pude encontrar nenhuma informação no padrão (C99 pelo menos) sobre a const
alteração do comportamento padrão "6.2.2: 5 Se a declaração de um identificador para um objeto tiver escopo de arquivo e nenhum especificador de classe de armazenamento, seu vínculo é externo".
bar
é um VLA (array de comprimento variável); é provável que o compilador gere código como se seu comprimento fosse constante.
Outra desvantagem de const
C é que você não pode usar o valor na inicialização de outro const
.
static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;
// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND
* NUMBER_OF_HANDS;
Mesmo isso não funciona com uma const, pois o compilador não a vê como uma constante:
static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!
Eu ficaria feliz em usar digitado const
nesses casos, caso contrário ...
static uint8_t const ARRAY_SIZE = 16;
repentino não compila mais pode ser um pouco desafiador, principalmente quando ele #define ARRAY_SIZE 256
está enterrado a dez camadas de profundidade em uma teia emaranhada de cabeçalhos. Que o nome todo em maiúsculas ARRAY_SIZE
está causando problemas. Reserve ALL_CAPS para macros e nunca defina uma macro que não esteja no formato ALL_CAPS.
const
. Isso poderia ser mais votado!
Se você pode se safar, static const
tem muitas vantagens. Obedeça aos princípios normais do escopo, é visível em um depurador e geralmente obedece às regras que as variáveis obedecem.
No entanto, pelo menos no padrão C original, na verdade não é uma constante. Se você usar #define var 5
, poderá escrever int foo[var];
como uma declaração, mas não poderá fazê-lo (exceto como uma extensão do compilador " static const int var = 5;
. Com este não é o caso em C ++, onde a static const
versão pode ser usada em qualquer lugar do#define
versão puder, e acredito que isso também é o caso do C99.
No entanto, nunca nomeie uma #define
constante com um nome em minúsculas. Ele substituirá qualquer uso possível desse nome até o final da unidade de tradução. As constantes de macro devem estar no que é efetivamente seu próprio espaço para nome, que é tradicionalmente todas as letras maiúsculas, talvez com um prefixo.
const
em C99 ainda não é uma constante real. Você pode declarar o tamanho da matriz com a const
em C99, mas apenas porque C99 suporta matrizes de comprimento variável. Por esse motivo, ele funcionará apenas onde os VLAs são permitidos. Por exemplo, mesmo em C99, você ainda não pode usar a const
para declarar o tamanho de uma matriz de membros em a struct
.
const int
tamanho como se fosse uma const C ++ ou uma macro. Se você deseja depender desse desvio do GCC do padrão, é certamente sua escolha, eu pessoalmente o escolheria, a menos que você possa realmente usar outro compilador que não o GCC ou o Clang, este último tem o mesmo recurso aqui (testado com o Clang 3.7)
É SEMPRE preferível usar const, em vez de #define. Isso porque const é tratado pelo compilador e #define pelo pré-processador. É como se o #define em si não faça parte do código (grosso modo).
Exemplo:
#define PI 3.1416
O nome simbólico PI nunca pode ser visto pelos compiladores; pode ser removido pelo pré-processador antes que o código-fonte chegue ao compilador. Como resultado, o nome PI pode não ser inserido na tabela de símbolos. Isso pode ser confuso se você receber um erro durante a compilação envolvendo o uso da constante, porque a mensagem de erro pode se referir a 3.1416, não ao PI. Se o PI fosse definido em um arquivo de cabeçalho que você não escreveu, você não teria ideia de onde veio esse 3.1416.
Esse problema também pode surgir em um depurador simbólico, porque, novamente, o nome com o qual você está programando pode não estar na tabela de símbolos.
Solução:
const double PI = 3.1416; //or static const...
#define var 5
causará problemas se você tiver coisas assim mystruct.var
.
Por exemplo,
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
O pré-processador irá substituí-lo e o código não será compilado. Por esse motivo, o estilo tradicional de codificação sugere que todas as constantes #define
s usam letras maiúsculas para evitar conflitos.
Eu escrevi um programa de teste rápido para demonstrar uma diferença:
#include <stdio.h>
enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};
#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32
int main(int argc, char *argv[]) {
printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);
return(0);
}
Isso compila com estes erros e avisos:
main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
^
Observe que o enum dá um erro quando define dá um aviso.
A definição
const int const_value = 5;
nem sempre define um valor constante. Alguns compiladores (por exemplo, tcc 0.9.26 ) apenas alocam a memória identificada com o nome "const_value". Usando o identificador "const_value", você não pode modificar esta memória. Mas você ainda pode modificar a memória usando outro identificador:
const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.
Isso significa a definição
#define CONST_VALUE 5
é a única maneira de definir um valor constante que não pode ser modificado por nenhum meio.
#define
também pode ser modificado editando o código da máquina.
5
. Mas não se pode modificar #define
porque é uma macro de pré-processador. Não existe no programa binário. Se alguém quisesse modificar todos os locais onde CONST_VALUE
era usado, era necessário fazê-lo um por um.
#define CONST 5
, então if (CONST == 5) { do_this(); } else { do_that(); }
, eo compilador elimina o else
ramo. Como você propõe editar o código da máquina para mudar CONST
para 6?
#define
não é à prova de balas.
#define
. A única maneira real de fazer isso é editar o código fonte e recompilar.
Embora a pergunta fosse sobre números inteiros, vale a pena notar que #define e enums são inúteis se você precisar de uma estrutura ou string constante. Ambos são geralmente passados para funções como ponteiros. (Com strings é necessário; com estruturas é muito mais eficiente.)
Quanto aos números inteiros, se você estiver em um ambiente incorporado com memória muito limitada, talvez seja necessário se preocupar com o local onde a constante é armazenada e como os acessos a ela são compilados. O compilador pode adicionar duas consts em tempo de execução, mas adicionar dois # define em tempo de compilação. Uma constante #define pode ser convertida em uma ou mais instruções MOV [imediatas], o que significa que a constante é efetivamente armazenada na memória do programa. Uma constante const será armazenada na seção .const na memória de dados. Em sistemas com arquitetura de Harvard, pode haver diferenças no desempenho e no uso da memória, embora provavelmente sejam pequenos. Eles podem ser importantes para a otimização rígida dos loops internos.
Não pense que há uma resposta para "o que é sempre melhor", mas, como Matthieu disse
static const
é do tipo seguro. Minha maior preocupação com o animal de estimação #define
é que, ao depurar no Visual Studio, você não pode assistir à variável. Dá um erro que o símbolo não pode ser encontrado.
Aliás, uma alternativa para #define
, que fornece escopo adequado, mas se comporta como uma constante "real", é "enum". Por exemplo:
enum {number_ten = 10;}
Em muitos casos, é útil definir tipos enumerados e criar variáveis desses tipos; se isso for feito, os depuradores poderão exibir variáveis de acordo com o nome da enumeração.
Uma ressalva importante ao fazer isso, no entanto: no C ++, os tipos enumerados têm compatibilidade limitada com números inteiros. Por exemplo, por padrão, não se pode executar aritmética sobre eles. Acho que esse é um comportamento padrão curioso para enumerações; embora fosse bom ter um tipo de "enum estrito", dado o desejo de ter C ++ geralmente compatível com C, eu pensaria que o comportamento padrão de um tipo de "enum" deveria ser intercambiável com números inteiros.
int
, portanto, o "enum hack" não pode ser usado com outros tipos inteiros. (A enumeração tipo é compatível com algum tipo inteiro definido pela implementação, não necessariamente int
, mas, neste caso, o tipo é tão anônimo que não importa.)
int
uma variável do tipo enumeração (que os compiladores podem fazer) e se tentar atribuir a essa variável um membro de sua própria enumeração. Eu gostaria que os comitês de padrões adicionassem maneiras portáteis de declarar tipos inteiros com semântica especificada. QUALQUER plataforma, independentemente do char
tamanho, deve poder, por exemplo, declarar um tipo que envolverá o mod 65536, mesmo que o compilador precise adicionar muitas AND R0,#0xFFFF
instruções ou instruções equivalentes.
uint16_t
, embora é claro que não seja um tipo de enumeração. Seria bom permitir ao usuário especificar o tipo inteiro usado para representar um determinado tipo de enumeração, mas você pode obter o mesmo efeito com a typedef
para uint16_t
e uma série de #define
s para valores individuais.
2U < -1L
como verdadeiras e outras como falsas, e agora estamos presos ao fato de que algumas plataformas implementarão uma comparação entre uint32_t
e int32_t
como assinado e alguns como não assinados, mas isso não significa que o Comitê não possa definir um sucessor compatível com C que inclua tipos cuja semântica seria consistente em todos os compiladores.
Uma diferença simples:
No tempo de pré-processamento, a constante é substituída por seu valor. Portanto, você não pode aplicar o operador de desreferência a uma definição, mas pode aplicar o operador de desreferência a uma variável.
Como você supõe, define é mais rápido que a const estática.
Por exemplo, tendo:
#define mymax 100
você não pode fazer printf("address of constant is %p",&mymax);
.
Mas ter
const int mymax_var=100
você pode fazer printf("address of constant is %p",&mymax_var);
.
Para ser mais claro, a definição é substituída por seu valor no estágio de pré-processamento, portanto, não temos nenhuma variável armazenada no programa. Temos apenas o código do segmento de texto do programa em que a definição foi usada.
No entanto, para const estático, temos uma variável que está alocada em algum lugar. Para o gcc, a const estática é alocada no segmento de texto do programa.
Acima, eu queria falar sobre o operador de referência, para substituir a desreferência por referência.
const
qualificador. C não possui constantes simbólicas que não sejam constantes enum . A const int
é uma variável. Você também confunde linguagem e implementações específicas. Não há exigência de onde colocar o objeto. E isso nem é verdade para o gcc: normalmente ele coloca const
variáveis qualificadas na .rodata
seção. Mas isso depende da plataforma de destino. E você quer dizer o endereço do operador &
.
Examinamos o código assembler produzido no MBF16X ... Ambas as variantes resultam no mesmo código para operações aritméticas (ADD Imediato, por exemplo).
Portanto, const int
é preferível para a verificação de tipo enquanto #define
estiver no estilo antigo. Talvez seja específico do compilador. Portanto, verifique o código do assembler produzido.
Não tenho certeza se estou certo, mas na minha opinião chamando #define
valor d é muito mais rápido do que chamar qualquer outra variável normalmente declarada (ou valor const). É porque quando o programa está sendo executado e ele precisa usar alguma variável declarada normalmente, ele precisa pular para o local exato na memória para obter essa variável.
Ao contrário, quando usa o #define
valor d, o programa não precisa pular para nenhuma memória alocada, apenas aceita o valor. Se #define myValue 7
e a chamada do programa myValue
, ele se comporta exatamente da mesma maneira que quando apenas chama 7
.