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 / 79555310
Accepted
Arnab Chakraborty
Arnab Chakraborty
Asked: 2025-04-04 20:29:17 +0800 CST2025-04-04 20:29:17 +0800 CST 2025-04-04 20:29:17 +0800 CST

Declaração implícita de função padrão em C

  • 772

Esta questão vem de uma tentativa de entender a "filosofia C" e não de nenhum "problema real".

Suponha que em um programa C eu use sin(x):

#include <stdio.h>

int main() {
  char s[20] = "aha";
  printf("%f", sin(s));
  return 0;
}

Cometi dois erros deliberadamente:

  1. Não #includeeditei math.h (nem forneci nenhuma declaração para pecado).
  2. Eu fiz sum tipo char *(apenas algo que não pode ser convertido significativamente para um double).

Eu compilo usando GCC com o -lmsinalizador.

Como esperado, recebo um aviso e um erro.

Claro, há o aviso de declaração implícita de função blá blá blá:

bad.c: In function ‘main’:

bad.c:5:15: warning: implicit declaration of function ‘sin’ [-Wimplicit-function-declaration]
    5 |   printf("%f", sin(s));
      |                ^~~
bad.c:5:16: warning: incompatible implicit declaration of built-in function ‘sin’
bad.c:2:1: note: include ‘<math.h>’ or provide a declaration of ‘sin’
    1 | #include <stdio.h>
  +++ |+#include <math.h>

Uma pequena pesquisa parece indicar que se uma função não for declarada previamente, então C assume uma "declaração padrão" (a chamada "declaração implícita") que é como int undeclaredfunction(void).

Mas também recebo o seguinte erro:

    2 |
bad.c:5:19: error: incompatible type for argument 1 of ‘sin’
    5 |   printf("%f", sin(s));
      |                    ^
      |                    |
      |                    char *
bad.c:5:20: note: expected ‘double’ but argument is of type ‘char *’

Isso mostra que a "declaração implícita" espera um argumento do tipo double.

  1. Parece que C mantém uma lista de "declarações implícitas" corretas para "funções padrão". Onde posso encontrar a lista abrangente de tais declarações implícitas corretas para um determinado compilador (digamos gccou clang)?
  2. Se C já tem as declarações corretas das funções matemáticas padrão incorporadas, então por que ele espera que o programador as tenha #include<math.h>? Só por tradição? Ou por limpeza?

A(s) resposta(s) que procuro são sobre as regras exatas usadas pelo compilador, e não sobre comentários subjetivos sobre "boas práticas de programação".

c
  • 4 4 respostas
  • 925 Views

4 respostas

  • Voted
  1. Eric Postpischil
    2025-04-04T20:54:06+08:002025-04-04T20:54:06+08:00

    Isso mostra que a "declaração implícita" espera um argumento do tipo double.

    Não, não mostra isso. A razão pela qual o compilador esperava um doubleargumento não é porque havia uma declaração implícita. É porque o nome siné reservado.

    C 2024 7.1.3 diz:

    … Todos os identificadores com ligação externa em qualquer uma das seguintes subcláusulas (incluindo as futuras instruções da biblioteca) errnosão sempre reservados para uso como identificadores com ligação externa…

    Reservar o nome para uso pela biblioteca padrão C significa que, se um programa o usa de outra forma que não a reservada, o comportamento não é definido pelo padrão C. Ele não diz especificamente que uma implementação C deve tratar o nome como pré-declarado e conhecido pelo compilador, mas permite isso. Neste caso, o GCC sabia sobre o nome e emitiu um erro com base nisso. Ou seja, ele não usou uma declaração implícita genérica; ele usou seu próprio conhecimento do que a sinrotina deveria ser em particular.

    … "declaração padrão" (a chamada "declaração implícita") que é como int undeclaredfunction(void).

    Em C 1990, um identificador não declarado usado para uma chamada de função seria implicitamente declarado como , conforme C 1990 6.3.2.2. Isso foi removido em C 1999. Alguns compiladores mantiveram o suporte para ele, particularmente em modos que não estão em conformidade com o padrão C atual.extern int identifier();

    O ()naquela declaração significava que os tipos de parâmetros não foram especificados. (Isso mudou no C 2024; agora significaria que não há parâmetros.)

    1. Parece que C mantém uma lista de "declarações implícitas" corretas para "funções padrão". Onde posso encontrar a lista abrangente de tais declarações implícitas corretas para um determinado compilador (digamos gccou clang)?

    O padrão C 2024 especifica identificadores reservados e formas de identificadores reservados principalmente na cláusula 7, com algumas menções em outros lugares. Por exemplo, whileé reservado porque é uma palavra-chave, não porque é uma rotina de biblioteca. Alguns identificadores reservados são especificados como padrões, não como strings únicas específicas. Por exemplo, C 2024 6.4.3.1 diz que todos os identificadores que começam com dois sublinhados ou um sublinhado e uma letra maiúscula são reservados para qualquer uso.

    O GCC suporta muitas funções internas que começam com __builtin, e elas estão documentadas aqui . No entanto, essa não é uma lista completa de todos os identificadores reservados que ele usa. O GCC e o Clang também predefinem certas macros. O Clang documenta essas aqui . Não acho que haja um único lugar onde você encontrará todos os identificadores predefinidos (ou especiais) que o GCC e o Clang usam. Compiladores, bibliotecas e cabeçalhos de sistema podem usar identificadores reservados para propósitos internos sem documentá-los.

    1. Se C já tem as declarações corretas das funções matemáticas padrão incorporadas, então por que ele espera que o programador #include<math.h>? Só por tradição? Ou por limpeza?

    Quando uma pessoa está escrevendo código para o kernel do sistema operacional e quer chamar sua rotina para escrever no log do sistema, ela quer logvincular a essa rotina, e ter sua declaração, e não ser a logrotina matemática declarada em <math.h>. As rotinas da biblioteca padrão foram agrupadas em subconjuntos para reduzir colisões de nomes para pessoas trabalhando em domínios diferentes. Ao compilar para um ambiente independente (como código do sistema operacional) em vez de um ambiente hospedado (os programas comuns com os quais você está mais familiarizado), os identificadores reservados são reduzidos.

    Além disso, embora sinseja reservado para uso com vinculação externa, um programador pode definir sua própria sinrotina com vinculação interna (declarada com static), e essa declaração pode ser diferente da rotina da biblioteca matemática.

    • 16
  2. Best Answer
    dbush
    2025-04-04T20:46:33+08:002025-04-04T20:46:33+08:00

    O padrão C faz pouca ou nenhuma menção a funções integradas, no entanto, algumas implementações C têm um conjunto de funções integradas que são "pré-declaradas", por assim dizer.

    Para o GCC em particular, a página a seguir lista as funções integradas:

    7 Funções integradas fornecidas pelo GCC

    E esta página em particular lista as funções da biblioteca C que são incorporadas:

    7.1 Builtins para funções de biblioteca C

    A sinfunção está nesta lista, portanto a declaração correta dela double sin(double)existe como algo interno.

    Se esse mesmo código for compilado sob MSVC, ele não gera um erro. A lista de funções internas pode ser encontrada aqui:

    Intrínsecos do compilador

    E aparentemente sinnão está entre sua lista de funções internas, então ele usa a declaração implícita de int sin()(note que antes do C23 isso não é o mesmo que int sin(void), pois o primeiro tem um número não especificado de argumentos enquanto o último tem zero). Executar o programa subsequentemente acionaria um comportamento indefinido , pois a sinfunção está sendo chamada por meio de um ponteiro de função incompatível.

    • 13
  3. Ted Lyngmo
    2025-04-04T20:36:19+08:002025-04-04T20:36:19+08:00

    Primeiro de tudo, declarações implícitas são obsoletas. Uma coisa do passado. Nunca confie nelas.

    Parece que C mantém uma lista de "declarações implícitas" corretas para "funções padrão". Onde posso encontrar a lista abrangente de tais declarações implícitas corretas para um determinado compilador (digamos gcc ou clang)?

    Uma declaração implícita é feita quando você usa a função pela primeira vez (sem uma declaração explícita anterior). No entanto, você a usou e ela se tornará a declaração implícita. intcomo um valor de retorno aparentemente parecia um bom palpite no momento em que declarações implícitas foram aceitas, então a declaração implícita significa que a função deve retornar int.

    Se C já tem as declarações corretas das funções matemáticas padrão incorporadas, então por que ele espera que o programador #include<math.h>? Só por tradição? Ou por limpeza?

    Você está cavando um túmulo. Não há uma lista de declarações corretas. Compiladores modernos sintonizados com os padrões modernos de C recusarão programas que dependem de declarações implícitas. Historicamente, o compilador errou muitas vezes e fez uma bagunça no seu programa final, então eles removeram declarações implícitas da linguagem.

    • 7
  4. Lundin
    2025-04-04T20:54:33+08:002025-04-04T20:54:33+08:00

    Uma pequena pesquisa parece indicar que se uma função não for declarada previamente, então C assume uma "declaração padrão"

    Não, isso está errado. Costumava ser assim nos anos 1990, mas foi um grande erro de design da linguagem, razão pela qual a declaração implícita/int implícito foi removida da linguagem no ano de 1999.

    Parece que C mantém uma lista de "declarações implícitas" corretas para "funções padrão". Onde posso encontrar a lista abrangente de tais declarações implícitas corretas para um determinado compilador (digamos gcc ou clang)?

    Primeiro de tudo, o compilador gcc pode, infelizmente, como um "recurso" usar certas funções/macros padrão, mesmo quando você esqueceu de incluir o cabeçalho relevante. Isso introduz um potencial para que bugs permaneçam sem serem detectados sempre que usar o gcc, já que ele "apenas" dá um aviso, até a versão mais recente 14.x que dá um erro. (Eu acho que isso tem a ver com o C23 tornando certas declarações de estilo K&R inválidas.)

    A capacidade de usar versões "embutidas" de funções de lib padrão pode estar relacionada a se a lib padrão implementa a função como uma macro do tipo função ou não. Quando eu altero seu código para o C ainda válido (sin)(s), o compilador perde a capacidade de expandir isso como uma macro do tipo função e, de repente, ele não sabe nada sobre sin(porque não incluímos math.h).

    Se C já tem as declarações corretas das funções matemáticas padrão incorporadas, então por que ele espera que o programador #include<math.h>? Só por tradição? Ou por limpeza?

    Não, isso é só uma coisa do gcc.

    A(s) resposta(s) que procuro são sobre as regras exatas usadas pelo compilador

    O compilador pode não seguir o padrão C. Na verdade, o gcc não vem pronto para uso. Se você quiser que ele se comporte como um compilador C padrão, você tem que dizer para ele compilar com, por exemplo:

    -std=c17 -pedantic-errors
    
    • 3

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