Pergunta

I tempo se pensou que em C, todas as variáveis ??tiveram de ser declarado no início da função. Eu sei que na C99, as regras são as mesmas que em C ++, mas quais são as regras de posicionamento declaração variáveis ??para C89 / ANSI C?

O código a seguir compila com sucesso com gcc -std=c89 e gcc -ansi:

#include <stdio.h>
int main() {
    int i;
    for (i = 0; i < 10; i++) {
        char c = (i % 95) + 32;
        printf("%i: %c\n", i, c);
        char *s;
        s = "some string";
        puts(s);
    }
    return 0;
}

não deve declarações de c e s causar um erro no modo de C89 / ANSI?

Foi útil?

Solução

Ele compila com êxito porque GCC permite-lo como uma extensão GNU, mesmo que ele não é parte do padrão C89 ou ANSI. Se você quiser cumprir rigorosamente essas normas, você deve passar a bandeira -pedantic.

Outras dicas

Para C89, você deve declarar todas as suas variáveis ??no início de um bloco de escopo .

Assim, sua declaração char c é válido, uma vez que está no topo do bloco para escopo loop. Mas, a declaração char *s deve ser um erro.

O agrupamento de declarações de variáveis ??no topo do bloco é um legado provavelmente devido a limitações dos antigos, compiladores C primitivas. Todas as línguas modernas recomendar e às vezes até mesmo reforçar a declaração de variáveis ??locais no ponto mais recente: onde eles estão inicializado primeiro. Porque este se livrar do risco do uso de um valor aleatório por engano. Separando declaração e inicialização também impede que você use "const" (ou "final") quando podia.

C ++, infelizmente, continua aceitando a maneira antiga, superior declaração para compatibilidade com C (um C compatibilidade arrastar para fora de muitos outros ...), mas tenta C ++ para afastar-se dela:

  • O design de referências C ++ não permitem mesmo que tais topo do bloco de agrupamento.
  • Se você separar declaração e inicialização de um C ++ locais objeto , em seguida, você paga o custo de uma empresa de construção extra para nada. Se o construtor não-arg não existir, em seguida, novamente você não sequer são autorizados a separar os dois!

C99 começa a se mover C nesta mesma direção.

Se você está preocupado de não encontrar onde as variáveis ??locais são declarados, então isso significa que você tem um problema muito maior:. Bloco anexando é muito longa e deve ser dividido

https : //www.securecoding.cert.org/confluence/display/cplusplus/DCL19-CPP.+Initialize+automatic+local+variables+on+declaration

A partir de uma manutenção, ao invés de sintática, ponto de vista, há pelo menos três linhas de pensamento:

  1. Declare todas as variáveis ??no início da função para que eles vão estar em um lugar e você será capaz de ver a lista completa de relance.

  2. Declare todas as variáveis ??tão perto quanto possível do local eles são usados ??em primeiro lugar, para que você saiba por cada um é necessário.

  3. Declare todas as variáveis ??no início do bloco escopo interno, então eles vão sair do âmbito mais rapidamente possível e permitir que o compilador para a memória otimizar e dizer-lhe se você acidentalmente usá-los onde você não teve pretendido.

I geralmente preferem a primeira opção, como eu encontrar os outros, muitas vezes me forçar a caça através de código para as declarações. Definir todas as variáveis ??na frente também torna mais fácil para inicializar e vê-los a partir de um depurador.

Eu vou às vezes declarar variáveis ??dentro de um bloco menor alcance, mas apenas por uma boa razão, de que eu tenho muito poucos. Um exemplo pode ser depois de um fork(), a variáveis ??de declaração apenas necessário pelo processo filho. Para mim, este indicador visual é um lembrete útil de sua finalidade.

Como conhecida por outros, GCC é permissiva a este respeito (e possivelmente outros compiladores, dependendo dos argumentos eles são chamados com) mesmo quando em modo 'C89', a menos que você use 'pedante' corrente. Para ser honesto, não há muitas boas razões para não ter pedante diante; código moderno de qualidade deve sempre compilar sem avisos (ou muito poucos onde você sabe que está fazendo algo específico que é suspeito para o compilador como um possível erro), então se você não pode fazer seu código compilar com uma configuração pedante provavelmente precisa de alguma atenção.

C89 exige que as variáveis ??ser declarada antes de quaisquer outras declarações dentro de cada âmbito, as normas posteriores permitem declaração mais perto de uso (que pode ser tanto mais intuitiva e mais eficiente), especialmente a declaração simultânea e inicialização de uma variável de controle de loop em 'para 'loops.

Como foi observado, há duas escolas de pensamento sobre isso.

1) Declare tudo no topo da função porque o ano é 1987.

2) Declare mais próximo da primeira utilização e no menor espaço possível.

A minha resposta para isso é fazer as duas coisas! Deixe-me explicar:

Para funções de comprimento, 1) torna a refatoração muito difícil. Se você trabalha em uma base de código onde os desenvolvedores são contra a idéia de sub-rotinas, então você vai ter 50 declarações de variáveis ??no início da função e alguns deles pode ser apenas um "eu" para um loop for que está no muito parte inferior da função.

Por isso, desenvolveu declaração-at-the-top-PTSD com isso e tentei fazer a opção 2) religiosamente.

voltei ao redor para uma opção por causa de uma coisa: funções curtas. Se suas funções são curtas o suficiente, então você vai ter algumas variáveis ??locais e desde que a função é curto, se você colocá-los no topo da função, eles vão ainda estar perto da primeira utilização.

Além disso, o anti-padrão de "declarar e definido como NULL" quando você quer declarar no topo, mas você não fez alguns cálculos necessários para a inicialização é resolvido porque as coisas que você precisa para inicializar provavelmente será recebido como argumentos.

Então agora o meu pensamento é que você deve declarar no topo de funções e tão próximo quanto possível da primeira utilização. Assim, ambos! E a maneira de fazer isso é com sub-rotinas bem divididas.

Mas se você estiver trabalhando em uma longa função, em seguida, colocar as coisas mais próximas do primeiro uso, porque dessa forma será mais fácil aos métodos de extrato.

Minha receita é esta. Para todas as variáveis ??locais, pegue a variável e movê-lo de declaração para a parte inferior, compilar, em seguida, mover a declaração pouco antes de o erro de compilação. Essa é a primeira utilização. Faça isso para todas as variáveis ??locais.

int foo = 0;
<code that uses foo>

int bar = 1;
<code that uses bar>

<code that uses foo>

Agora, definir um bloco de escopo que se inicia antes da declaração e mover o final até que o programa compila

{
    int foo = 0;
    <code that uses foo>
}

int bar = 1;
<code that uses bar>

>>> First compilation error here
<code that uses foo>

Esta não compila porque há mais algum código que usos foo. Podemos notar que o compilador foi capaz de ir através do código que usos bar porque ele não usa foo. Neste ponto, há duas opções. O mecânico é apenas para mover os "}" para baixo até que compila, ea outra opção é para inspecionar o código e determinar se a ordem pode ser alterada para:

{
    int foo = 0;
    <code that uses foo>
}

<code that uses foo>

int bar = 1;
<code that uses bar>

Se a ordem pode ser mudado, isso é provavelmente o que você quer porque encurta o tempo de vida de valores temporários.

Outra coisa a nota, faz o valor da necessidade foo a ser preservada entre os blocos de código que usam, ou poderia ser apenas um foo diferente em ambos. Por exemplo

int i;

for(i = 0; i < 8; ++i){
    ...
}

<some stuff>

for(i = 3; i < 32; ++i){
    ...
}

Essas situações precisam de mais do que o meu procedimento. O desenvolvedor terá que analisar o código para determinar o que fazer.

Mas o primeiro passo é encontrar o primeiro uso. Você pode fazê-lo visualmente, mas às vezes, é apenas mais fácil de eliminar a declaração, tentar compilar e basta colocá-lo de volta acima do primeiro uso. Se esse primeiro uso está dentro de um if, colocá-lo lá e verificar se ele compila. O compilador irá, em seguida, identificar outros usos. Tente fazer um bloco de escopo que abrange ambos os usos.

Após esta parte mecânica é feita, então torna-se mais fácil de analisar onde os dados. Se uma variável é usada em um bloco grande alcance, analisar a situação e ver se você está apenas usando a mesma variável para duas coisas diferentes (como um "i" que é usado para dois loops). Se os usos são independentes, criar novas variáveis ??para cada um desses usos não relacionados.

Vou citar algumas declarações do manual para gcc versão 4.7.0 para uma explicação clara.

"O compilador pode aceitar vários padrões base, como 'c90' ou 'c ++ 98', e dialetos GNU dessas normas, tais como 'gnu90' ou 'gnu ++ 98'. Ao especificar um padrão de base , o compilador irá aceitar todos os programas seguinte ao padrão e aqueles que usam extensões GNU que não contradizê-la. Por exemplo, '-std = c90' desliga certas características do GCC que são incompatíveis com a norma ISO C90, como o asm e typeof palavras-chave , mas não outras extensões GNU que não têm um significado em ISO C90, tais como omitindo o termo meio de um:?. expressão "

Eu acho que o ponto-chave da sua pergunta é por isso que não gcc conformidade com C89, mesmo se a opção "-std = c89" é usado. Eu não sei a versão do gcc, mas acho que não haverá grande diferença. O desenvolvedor do gcc nos disse que a opção "-std = c89" significa apenas as extensões que contradizem C89 estão desligados. Então, não tem nada a ver com algumas extensões que não têm um significado em C89. E a extensão que não restringem a colocação de declaração de variável pertence às extensões que não contradizem C89.

Para ser honesto, todo mundo vai pensar que ele deve estar em conformidade C89 totalmente à primeira vista da opção "-std = c89". Mas isso não acontece. Quanto ao problema que declarar todas as variáveis ??no início é melhor ou pior é apenas uma questão de hábito.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top