paulo1205
(usa Ubuntu)
Enviado em 28/01/2019 - 03:11h
Acho que sua pergunta está relacionada à questão dos escopos em que um nome é visível.
Existem quatro escopos de visibilidade em C: arquivo, bloco, função e protótipo de função.
O escopo de função se aplica apenas a rótulos (
labels ) usados com o comando
goto , então não vem muito ao caso aqui (mas a regra diz que todos os rótulos declarados em qualquer ponto dentro da definição de uma função devem ser visíveis em qualquer outro ponto da função, antes ou depois do ponto em que cada rótulo é declarado).
O escopo de protótipo de função diz respeito a declarações de funções que não sejam imediatamente seguidas de sua definição, e permite que os parâmetros usados na declaração enxerguem os parâmetros anteriores, e esse escopo acaba assim que se chega ao ponto-e-vírgula que encerra a declaração. Por exemplo:
bool ordena_array(int n, const char (*strings)[n]); // reutiliza parâmetro ‘n’ na declaração do parâmetro ‘strings’; escopo acaba no ‘;’.
O escopo de bloco limita tudo que for declarado dentro de um bloco delimitado por chaves (“
{ ” e “
} ”) ao interior desse bloco. Um nome passa a ser visível partir do ponto em que o elemento for declarado e deixa de ser visível assim que o bloco termina com o fechamento de chaves. Se o bloco for referente a uma definição de função ou a um comando
if ,
switch ou
for , os parâmetros da função ou a(s) variável(is) de controle declarada(s) entre os parênteses dos referidos comandos também são consideradas válidas dentro do bloco.
Por fim, o escopo de arquivo diz que a visibilidade do que for declarado fora de qualquer dos casos acima é visível a partir do ponto da declaração e até o final do arquivo.
Algumas observações quanto a escopos:
• Nomes declarados no escopo de arquivo ou num nível de bloco exterior são visíveis em blocos interiores.
• Um bloco pode declarar um ente de mesmo nome que outro que já tenha sido declarado no escopo de arquivo ou num escopo de bloco que lhe seja exterior. Nesse caso, o nome externo será ocultado no bloco interno a partir do ponto em que ocorrer a declaração com o mesmo nome, e assim permanecerá até o final desse bloco interno.
• Tanto no escopo de protótipo de funções quanto no de bloco, é inútil (embora sintaticamente aceitável) definir uma nova
struct ou
union como tipo de um parâmetro da função, em vez de usar uma que já tenha sido declarada em nível de arquivo, porque como o escopo é limitado apenas àquela declaração ou bloco, quem estiver fora não terá como chamar tal função, pois não terá como declarar um argumento do mesmo tipo do parâmetro (mesmo que tenha a mesma forma e até o mesmo especificador de nome, ainda será um tipo distinto porque vai existir em outro escopo).
•
Não existe um escopo específico para struct s e union s. Assim sendo, se você tiver uma declaração ou definição de uma
struct ou
union chamada
Y dentro da definição de outra
struct ou
union chamada
X , então o escopo de
Y será o mesmo escopo de
X , como se a definição de
Y tivesse ocorrido antes da declaração de
X nesse mesmo escopo. Isso significa que os dois trechos de código seguintes são sinônimos.
void f(){
// Note o escopo não apenas para as variáveis, mas para as estruturas e uniões.
struct A {
struct B {
union IC {
int i;
char c;
} ic;
} b;
} a;
// Dentro da função, posso usar a.b.ic.i ou a.b.ic.c.
} // Fora da função não existem mais a, nem struct A, struct B ou union IC. void f(){
// Note o escopo não apenas para as variáveis, mas para as estruturas e uniões.
union IC {
int i;
char c;
};
struct B { union IC ic; };
struct A { struct B b; };
struct A a;
// Dentro da função, posso usar a.b.ic.i ou a.b.ic.c.
} // Fora da função não existem mais a, nem struct A, struct B ou union IC.
• Contudo, é bom evitar definições e declarações aninhadas: em C, elas dificultam a visão de quais nomes estão visíveis no escopo.
• Outra boa razão para evitar o aninhamento de tipos em C é que se o mesmo código for compilado em C++ (como não raramente acontece, principalmente com cabeçalhos), a equivalência mencionada acima não vale, pois cada
struct ,
union ou
class introduz seu próprio escopo. O primeiro exemplo acima, em lugar de produzir três tipos no mesmo escopo, como em C, produziria os seguintes três tipos:
A ,
A::B e
A::B::IC (o operador
:: , que não existe em C, é chamado de operador de seleção de escopo).
Além da questão de escopo, existe também a de espaços de nomes (
name spaces ), que opera de modo praticamente ortogonal aos escopos. Há quatro espaços de nome em C, a saber:
• o de rótulos para uso com
goto ;
• o de especificadores (
tags ) de
struct s,
union s e
enum s;
• o de nomes dos campos de
struct s e
union s (cada estrutura ou união declarada tem o seu próprio espaço de nomes, de modo que é possível que tipos distintos tenham campos com mesmos nomes, sem provocar confusão entre eles); e
• o geral, que contém todos os demais identificadores, que incluem nomes de variáveis e funções, nomes de tipos atribuídos com
typedef , nomes das constantes definidas em uma
enum .
Os espaços de nomes diferentes são o que permite que haja, num mesmo escopo, um identificador de estrutura como mesmo nome de uma função, ou uma definição de tipo
X que o faz equivalente a uma
struct X , ou ainda um campo de uma estrutura com o mesmo nome que uma variável ou que o próprio especificador dessa mesma estrutura. Contudo, não é possível ter, num mesmo escopo e ao mesmo tempo, uma variável e um tipo de mesmo nome que ela definido com
typedef ou uma constante de enumeração e uma função com mesmo nome.
... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)