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 5enum { 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 #definepermite que você especifique sem assinatura e com Ue com Lsufixos, além de especificar constum tipo. enumpode causar problemas nas conversões de tipo comuns.
enumnem #defineusa 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 intarquivo, mas o compilador poderá otimizá-lo se você não usar um endereço.
enums (e static const): eles não podem ser alterados. a definepode ser #undefineonde d enume static constsã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 :)
staticcujo endereço é usado devem permanecer; e se o endereço for escolhido, não seria possível usar um #defineou 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.
#ifpode ser preferível ao longo #ifdefde bandeiras booleanas, mas, neste caso, seria impossível para definir varcomo 0a partir da linha de comando. Portanto, neste caso, #ifdeffaz mais sentido, desde que 0seja 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 constobjeto, na realidade os constobjetos 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 intobjeto como um caseró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 constobjeto para especificar um tamanho de matriz (enquanto uma macro funcionará). Mesmo em C99, você não pode usar um constobjeto 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 #defineem C. E não se esqueça de outra alternativa, que produz constantes verdadeiras em C - enum.
No C ++, os constobjetos são constantes verdadeiras; portanto, no C ++, é quase sempre melhor preferir a constvariante ( staticembora não seja necessário explicitar o C ++).
const intobjetos 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.) #
constsignifica somente leitura. const int r = rand();é perfeitamente legal.
constexprem comparação com constespecialmente com os stlcontêineres como arrayou bitset.
switch()declaração, não em caseuma. Eu também fui pego nessa também ☺
A diferença entre static conste #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 ... :-)
constuse memória. O GCC (testado com 4.5.3 e algumas versões mais recentes) otimiza facilmente o const intliteral 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 consts 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 staticporque a ligação interna constjá 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 constalteraçã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 constC é 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 256está enterrado a dez camadas de profundidade em uma teia emaranhada de cabeçalhos. Que o nome todo em maiúsculas ARRAY_SIZEestá 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 constversã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 #defineconstante 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.
constem C99 ainda não é uma constante real. Você pode declarar o tamanho da matriz com a constem 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 constpara declarar o tamanho de uma matriz de membros em a struct.
const inttamanho 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 5causará 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 #defines 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.
#definetambém pode ser modificado editando o código da máquina.
5. Mas não se pode modificar #defineporque é uma macro de pré-processador. Não existe no programa binário. Se alguém quisesse modificar todos os locais onde CONST_VALUEera 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 elseramo. Como você propõe editar o código da máquina para mudar CONSTpara 6?
#definenã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.)
intuma 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 chartamanho, deve poder, por exemplo, declarar um tipo que envolverá o mod 65536, mesmo que o compilador precise adicionar muitas AND R0,#0xFFFFinstruçõ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 typedefpara uint16_te uma série de #defines para valores individuais.
2U < -1Lcomo verdadeiras e outras como falsas, e agora estamos presos ao fato de que algumas plataformas implementarão uma comparação entre uint32_te int32_tcomo 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.
constqualificador. 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 constvariáveis qualificadas na .rodataseçã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 #defineestiver 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 #definevalor d, o programa não precisa pular para nenhuma memória alocada, apenas aceita o valor. Se #define myValue 7e a chamada do programa myValue, ele se comporta exatamente da mesma maneira que quando apenas chama 7.