Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit declaration of standard function in C

This question comes from an attempt to understand the "C philosophy" and not from any "real problem".

Suppose that in a C program I use sin(x):

#include <stdio.h>

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

I have deliberately made two mistakes:

  1. I have not #includeed math.h (or provided any declaration for sin).
  2. I made s of type char * (just something that cannot be cast meaningfully to a double).

I compile using GCC with the -lm flag.

As expected, I get one warning and one error.

Of course, there is the warning of implicit declaration of function blah blah:

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>

A little research seems to indicate that if a function is not declared beforehand, then C assumes a "default declaration" (the so called "implicit declaration") which is like int undeclaredfunction(void).

But I also get the following error:

    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 *’

This shows that the "implicit declaration" expects an argument of type double.

  1. It seems that C maintains a list of correct "implicit declarations" for "standard functions". Where can I find the comprehensive list of such correct implicit declarations for a given compiler (say gcc or clang)?
  2. If C already has the correct declarations of the standard math functions built in, then why does it expect the programmer to #include<math.h>? Just because of tradition? Or neatness?

The answer(s) that I looking for are about the exact rules used by the compiler, and not about subjective comments about "good programming practices".

like image 721
Arnab Chakraborty Avatar asked Feb 16 '26 13:02

Arnab Chakraborty


2 Answers

This shows that the "implicit declaration" expects an argument of type double.

No, it does not show that. The reason the compiler expected a double argument is not because there was an implicit declaration. It is because the name sin is reserved.

C 2024 7.1.3 says:

… All identifiers with external linkage in any of the following subclauses (including the future library directions) and errno are always reserved for use as identifiers with external linkage…

Reserving the name for use by the C standard library means that, if a program uses it other than in the reserved way, the behavior is not defined by the C standard. It does not specifically say that a C implementation must treat the name as predeclared and known to the compiler, but it allows that. In this case, GCC did know about the name and issued an error on that basis. That is, it did not use a generic implicit declaration; it used its own knowledge of what the sin routine particularly should be.

… "default declaration" (the so called "implicit declaration") which is like int undeclaredfunction(void).

In C 1990, an undeclared identifier used for a function call would be implicitly declared as extern int identifier();, per C 1990 6.3.2.2. This was removed in C 1999. Some compilers have retained support for it, particularly in modes that do not conform to the current C standard.

The () in that declaration meant the parameter types were not specified. (That changed in C 2024; it would now mean there are no parameters.)

  1. It seems that C maintains a list of correct "implicit declarations" for "standard functions". Where can I find the comprehensive list of such correct implicit declarations for a given compiler (say gcc or clang)?

The C 2024 standard specifies reserved identifiers and forms of reserved identifiers primarily in clause 7, with some mentions elsewhere. For example, while is reserved because it is a keyword, not because it is a library routine. Some reserved identifiers are specified as patterns, not specific single strings. For example, C 2024 6.4.3.1 says all identifiers beginning with two underscores or one underscore and an uppercase letter are reserved for any use.

GCC supports many built-in functions beginning with __builtin, and those are documented here. However, that is not a complete list of all the reserved identifiers it uses. GCC and Clang also predefine certain macros. Clang documents those here. I do not think there is a single place where you will find all the predefined (or otherwise special) identifiers that either GCC and Clang uses. Compilers, libraries, and system headers may use reserved identifiers for internal purposes without documenting them.

  1. If C already has the correct declarations of the standard math functions built in, then why does it expect the programmer to #include<math.h>? Just because of tradition? Or neatness?

When a person is writing code for the operating system kernel and wants to call its routine to write to the system log, they want log to link to that routine, and have its declaration, and not be the mathematical log routine declared in <math.h>. The standard library routines were grouped into subsets to reduce name collisions for people working in different domains. When compiling for a freestanding environment (such as operating system code) instead of a hosted environment (the ordinary programs you are most familiar with), the reserved identifiers are reduced.

Additionally, while sin is reserved for use with external linkage, a programmer may define their own sin routine with internal linkage (declared with static), and that declaration may differ from the math library routine.

like image 119
Eric Postpischil Avatar answered Feb 18 '26 01:02

Eric Postpischil


The C standard gives little if any mention of built-in functions, however some C implementations have a set of built-in functions that are "predeclared", so to speak.

For GCC in particular, the following page lists the built-in functions:

7 Built-in functions provided by GCC

And this page in particular lists C library functions that are built in:

7.1 Builtins for C library functions

The sin function is among this list, so the proper declaration of double sin(double) exists as a built-in.

If this same code is compiled under MSVC, it does not generate an error. Their list of built-in functions can be found here:

Compiler intrinsics

And apparently sin is not among its list of built-in functions, so it uses the implicit declaration of int sin() (note that prior to C23 this is not the same as int sin(void), as the former has an unspecified number of arguments while the latter has zero). Subsequently running the program would trigger undefined behavior as the sin function is being called through an incompatible function pointer.

like image 34
dbush Avatar answered Feb 18 '26 01:02

dbush



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!