AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79598706
Accepted
ALICEZzz
ALICEZzz
Asked: 2025-04-29 23:14:59 +0800 CST2025-04-29 23:14:59 +0800 CST 2025-04-29 23:14:59 +0800 CST

Como funciona o escopo do bloco? Como o padrão explica isso?

  • 772

Por exemplo, temos o seguinte código:

#include <stdio.h>
int main(void)
{
    int n = 4;
    {
        n++;
    }
    printf("%d\n",n);
    return 0;
}

Como o bloco interno vê a variável n? Como o padrão da linguagem C explica isso? Há algum tempo venho procurando uma resposta para essa pergunta. Na minha opinião, a resposta é a seguinte: em C17, temos (6.8 #3):

Um bloco permite que um conjunto de declarações e instruções seja agrupado em uma unidade sintática. Os inicializadores de objetos com duração de armazenamento automática e os declaradores de array de comprimento variável de identificadores comuns com escopo de bloco são avaliados e os valores são armazenados nos objetos (incluindo o armazenamento de um valor indeterminado em objetos sem um inicializador) cada vez que a declaração é alcançada, na ordem de execução, como se fosse uma instrução, e dentro de cada declaração na ordem em que os declaradores aparecem.

Se você não considerar os inicializadores, descobrirá que um bloco é composto apenas de algumas instruções. Usamos um bloco para avaliar todos esses operadores como uma unidade sintática. Em outras palavras, se você remover os caracteres {} do bloco, esses serão os mesmos operadores e o resultado será exatamente o mesmo, mas esses operadores não serão avaliados em uma unidade sintática:

#include <stdio.h>
int main(void)
{
    int n = 4;
    
        n++; // same effect but without {}
    
    printf("%d\n",n);
    return 0;
}

Também temos (6.2.1 # 4):

Se o declarador ou especificador de tipo que declara o identificador aparecer dentro de um bloco ou dentro da lista de declarações de parâmetros em uma definição de função, o identificador terá escopo de bloco, que termina no final do bloco associado.

A partir disso entendemos que n tem um escopo de bloco.

Se combinarmos tudo isso, descobrimos que n é incrementado como se o operador de incremento nunca tivesse aparecido no bloco interno.

Esta resposta está correta? Se não, explique o porquê. E cite um parágrafo do padrão da linguagem C.

c
  • 3 3 respostas
  • 100 Views

3 respostas

  • Voted
  1. Best Answer
    dbush
    2025-04-29T23:29:37+08:002025-04-29T23:29:37+08:00

    Como o bloco interno vê a variável n?

    Porque o bloco interno é parte do bloco externo (ou seja, o corpo da função neste caso).

    A seção 6.2.1p4 do padrão C descreve o escopo do bloco :

    Se o declarador ou especificador de tipo que declara o identificador aparecer dentro de um bloco ou dentro da lista de declarações de parâmetros em uma definição de função, o identificador tem escopo de bloco , que termina no final do bloco associado

    Portanto, o escopo de né o bloco que é o corpo da mainfunção. Isso inclui o bloco que contém a n++instrução.

    Observe que o seguinte, que pode parecer mais familiar, é o mesmo em termos de escopo:

    int n = 4;
    if (n>2) {
        n++;
    }
    

    Neste caso, o bloco é a parte "instrução" da ifinstrução e, como acima, este bloco é um escopo "interno" ao corpo da função.

    • 4
  2. John Bollinger
    2025-04-29T23:51:50+08:002025-04-29T23:51:50+08:00

    Como o bloco interno vê a variável n?

    Isto é uma questão de escopo:

    Para cada entidade diferente que um identificador designa, o identificador é visível (ou seja, pode ser usado) somente dentro de uma região do texto do programa chamada escopo .

    (C23 6.2.1/2)

    Você já citou a seção (de 6.2.1/4) que (em grande parte) define o escopo de variáveis ​​com escopo de bloco, como a sua n. Ela termina no final do bloco envolvente mais interno. Todo o bloco aninhado está dentro desse escopo, portanto, né visível lá.

    se você remover os caracteres {} do bloco, esses serão os mesmos operadores e o resultado será exatamente o mesmo, mas esses operadores não serão avaliados em uma unidade de sintaxe

    Sim, no seu exemplo. Um bloco vazio como o seu não faz muita coisa, embora possa ser útil limitar o escopo das variáveis ​​declaradas nele.

    ifMas você não pode remover livremente as chaves nos usos mais comuns de blocos. Um bloco é obrigatório para corpos de função. Blocos com múltiplas instruções elsee corpos de laço teriam significados diferentes se as chaves de seus blocos fossem removidas. Por exemplo, isto...

    char *result = "good";
    
    if (1 == 0) {
        puts("false");
        result = "bad";
    }
    puts(result);
    

    ... produz uma saída diferente desta:

    char *result = "good";
    
    if (1 == 0)
        puts("false");
        result = "bad";
    
    puts(result);
    

    E, de fato, reduzir o risco de introduzir tais erros é uma das razões para a regra de estilo comum de que if, elsee corpos de loop devem sempre ser blocos, mesmo quando o bloco contém apenas uma única instrução.

    Se combinarmos tudo isso, descobrimos que n é incrementado como se o operador de incremento nunca tivesse aparecido no bloco interno.

    Esta resposta está correta?

    Mais ou menos. Não está claro qual seria a alternativa. Mas sim, a avaliação de n++dentro do bloco tem o mesmo efeito que sem o bloco imediatamente envolvente. Se quiser diferente, você pode declarar um diferente ndentro do bloco.

    int n = 0;
    {
        int n = 0;
        n++;
    }
    printf("%d\n", n); // prints 0
    
    • 2
  3. Vlad from Moscow
    2025-04-29T23:52:17+08:002025-04-29T23:52:17+08:00

    Do Padrão C23 (6.2.1 Escopos de identificadores, nomes de tipos e literais compostos)

    2 Para cada entidade diferente que um identificador designa, o identificador é visível (ou seja, pode ser usado) apenas dentro de uma região do texto do programa chamada de escopo. Entidades diferentes designadas pelo mesmo identificador têm escopos diferentes ou estão em namespaces diferentes. Existem quatro tipos de escopos: função, arquivo, bloco e protótipo de função. (Um protótipo de função é uma declaração de uma função.)

    e

    4....Se o declarador ou especificador de tipo que declara o identificador aparecer dentro de um bloco ou dentro da lista de declarações de parâmetros em uma definição de função, o identificador terá escopo de bloco, que termina no final do bloco associado.

    4... Se um identificador designa duas entidades diferentes no mesmo namespace, os escopos podem se sobrepor. Nesse caso, o escopo de uma entidade (o escopo interno) terminará estritamente antes do escopo da outra entidade (o escopo externo). Dentro do escopo interno, o identificador designa a entidade declarada no escopo interno; a entidade declarada no escopo externo fica oculta (e não visível) dentro do escopo interno.

    No seu exemplo, a variável nnão está oculta no escopo do bloco interno. Se, em vez disso, você escrevesse, por exemplo

        int n = 4;
        {
            int n = 10;
            n++;
        }
    

    então a variável ndeclarada no escopo do bloco interno ocultaria a variável com o mesmo nome declarada no escopo externo.

    Ou um exemplo mais interessante

        int n = 4;
        {
            n++;
            int n = 10;
            n++;
        }
    

    Neste caso você obterá o mesmo resultado do seu programa original

    A propósito, você pode reintroduzir em um escopo interno uma variável que tem escopo de arquivo e estava oculta em algum bloco envolvente do escopo do bloco interno.

    Olha Você aqui.

    #include <stdio.h>
    
    int n = 1;
    
    int main(void) 
    {
        int n = 3;
        
        {
            ++n;
            
            extern int n;
            
            ++n;
        }
        
        printf( "n = %d\n", n );
        
        {
            extern int n;
        
            printf( "extern n = %d\n", n );
        }
    
        return 0;
    }
    
    • 2

relate perguntas

  • Multiplicação mais rápida que *

  • Usando uma macro para comprimento de string no especificador de formato scanf () em C

  • Como você pode definir o tipo de dados de #define para long double?

  • Ponteiros const incompatíveis

  • Mudança de cor não gradual no OpenGL

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve