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 / 79428667
Accepted
Zebrafish
Zebrafish
Asked: 2025-02-11 09:18:04 +0800 CST2025-02-11 09:18:04 +0800 CST 2025-02-11 09:18:04 +0800 CST

Como os compiladores C e C++ distinguem operadores unários?

  • 772

Se eu tiver o sinal de menos ao lado de uma variável como:

int a;
int b = -a; // UNARY OPERATOR
b = - a; // UNARY OPERATOR

O menos antes do 'a' é considerado um operador unário e o valor negativo de a é tomado. No entanto, neste:

int a, b;
a -b; // SUBTRACTION
a - b; // SUBTRACTION

Então, disso deduzo que:

  1. É irrelevante se o operador está ou não separado do operando por um espaço.
  2. A dedução de se é uma subtração ou um operador unário depende da presença de um operando anterior e é altamente contextual.

Alguém pode dar um resumo simples das regras de como o compilador decide isso?

c++
  • 3 3 respostas
  • 267 Views

3 respostas

  • Voted
  1. Best Answer
    Eric Postpischil
    2025-02-11T09:32:26+08:002025-02-11T09:32:26+08:00

    A teoria sobre análise sintática e linguagens formais é abordada em vários cursos de ciência da computação, então apenas uma pequena introdução pode ser fornecida em uma resposta do Stack Overflow. Compiladores podem usar um analisador LALR .

    Para dar uma breve visão disto, há essencialmente uma lista de padrões de coisas que podem aparecer na linguagem que está sendo compilada. Por exemplo, em C, uma expressão aditiva é uma de:

    • expressão multiplicativa
    • expressão-aditiva + expressão-multiplicativa
    • expressão-aditiva - expressão-multiplicativa

    Cada um dos itens na regra pode ser um token para outro padrão, como expressão multiplicativa , ou um símbolo terminal, como +ou int.

    Conforme o compilador lê o código-fonte, ele tenta combinar o texto que vê com padrões. Quando ele encontra uma correspondência adequada, ele reduz os itens que correspondem ao token cujo padrão eles corresponderam. Então a - bserá reduzido para additional-expression , e nunca corresponderá a um padrão unary-expression- b . Enquanto sem outro additional-expression antes será reduzido para unary-expression e nunca corresponderá a um padrão additional-expression .

    Novamente, essa é uma visão simplificada de parte do processo. Antes mesmo de o compilador processar tokens na gramática, ele realiza uma análise lexical que agrupa caracteres em tokens gramaticais. Então, transformar int foo;em “palavra-chave int” e “identificador foo” e transformar a---bem “identificador a”, “operador --”, “operador -” e “identificador b” acontece em um nível anterior/inferior de análise. E há também, conceitualmente pelo menos, um nível de pré-processador separado.

    Além disso, o compilador não tem realmente os padrões listados, e não está diretamente combinando sequências de entrada com os padrões. Em vez disso, as regras gramaticais são usadas para construir uma “máquina” conceitual que implementa essa correspondência de padrões. Essa máquina é chamada de parser, e é construída no compilador. Cada novo token de entrada altera o estado da máquina, e a máquina é projetada de tal forma que os estados correspondem ao reconhecimento dos padrões.

    Há matemática formal que mostra como gramáticas formais desse tipo podem ser usadas para construir “máquinas” (modelos matemáticos de computação) que realizam a análise sintática. Para entender isso completamente, deve-se fazer cursos em matemática discreta, máquinas de estados finitos e autômatos, linguagens formais (teoria da análise sintática) e construção de compiladores.

    • 7
  2. user7860670
    2025-02-11T14:16:40+08:002025-02-11T14:16:40+08:00

    O primeiro passo da compilação é uma passagem com pré-processador de texto. O pré-processador divide o texto de entrada em tokens e se o token corresponder a um nome de macro, ele executa a substituição de token até que não haja mais tokens para substituir. O importante é que o pré-processador é ganancioso, ou seja, ao extrair um token, ele tenta extrair um token de comprimento máximo possível. Símbolos de espaço em branco fora dos literais de string sempre agem como separadores de token.

    • a+bé tokenizado paraa + b
    • a++bé tokenizado para a ++ b(ganancioso!)
    • a+ +bé tokenizado para a + + b(o espaço é relevante!)
    • a+++bé tokenizado para a ++ + b(novamente ganancioso!)

    O próximo compilador tenta descobrir o significado da sequência de tokens, dependendo do contexto. Se +aparecer como o primeiro token da (sub) expressão, então ele é assumido como um operador unário, caso contrário, ele é assumido como o binário +. O próximo token é assumido como um primeiro token da próxima subexpressão.

    • 4
  3. Lundin
    2025-02-11T16:58:02+08:002025-02-11T16:58:02+08:00

    Em relação à parte do pré-processador da pergunta:

    É irrelevante se o operador está ou não separado do operando por um espaço.

    A dedução de se é uma subtração ou um operador unário depende da presença de um operando anterior e é altamente contextual.

    A forma como a expressão aparece é decidida no início do pré-processamento, conforme descrito em C23 6.4.1, que também contém a definição formal do termo espaço em branco :

    Um token de pré-processamento é o elemento lexical mínimo da linguagem nas fases de tradução 3 a 6. As categorias de tokens de pré-processamento são: nomes de cabeçalho, identificadores, números de pré-processamento, constantes de caracteres, literais de string, pontuadores e nomes de caracteres universais únicos, bem como caracteres únicos sem espaço em branco que não correspondem lexicamente às outras categorias de tokens de pré-processamento. > Se um caractere ' ou " corresponder à última categoria, o comportamento é indefinido. Os tokens de pré-processamento podem ser separados por espaço em branco ; isso consiste em comentários (descritos posteriormente) ou caracteres de espaço em branco (espaço, tabulação horizontal, nova linha, tabulação vertical e avanço de formulário) ou ambos.

    Segue-se então o que os programadores C chamam informalmente de "regra da mastigação máxima":

    Se o fluxo de entrada tiver sido analisado em tokens de pré-processamento até um determinado caractere, o próximo token de pré-processamento será a sequência mais longa de caracteres que pode constituir um token de pré-processamento.

    Em inglês simples: durante o pré-processamento, o pré-processador percorre um pedaço de texto (da esquerda para a direita) e tenta formar a sequência mais longa de caracteres que formaria um dos vários itens mencionados, como identificadores como ae bno seu exemplo. Mas também intpor enquanto, porque ainda não tem um significado especial durante o pré-processamento. Quando posteriormente traduzido para um token , inté categorizado como uma palavra-chave de idioma .

    =, -e ;são pontuadores conforme definido em 6.4.7. Por exemplo -=é um pontuador válido, então se estivesse presente em uma expressão como a-=b, a "regra do máximo" teria sido lida -=como um token de pré-processador. =-entretanto não é um pontuador válido em si mesmo, então é lido como dois diferentes: =e -.

    E entre todos eles pode haver espaço em branco, que é usado para separar tokens do pré-processador, mas depois descartado.

    Foi assim que todo o código foi analisado em tokens do pré-processador. Mais tarde, durante a compilação, quando os itens são traduzidos para tokens, eles começam a ter significado de acordo com a gramática da linguagem. Isso acontece na "fase de tradução 7", 5.2.1.2:

    1. Caracteres de espaço em branco separando tokens não são mais significativos. Cada token de pré-processamento é convertido em um token. Os tokens resultantes são analisados ​​sintaticamente e semanticamente e traduzidos como uma unidade de tradução.
    • 1

relate perguntas

  • Por que os compiladores perdem a vetorização aqui?

  • Erro de compilação usando CMake com biblioteca [fechada]

  • Erro lançado toda vez que tento executar o premake

  • Como criar um tipo de octeto semelhante a std::byte em C++?

  • Somente operações bit a bit para std::byte em C++ 17?

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