简体   繁体   中英

How can a #define from another headder file be used if the headderfile is not included?

I'm currently working with FreeRTOS, and have noticed something, which i have not encountered before.

The file "projdefs.h" uses a define from the file "FreeRTOSConfig.h" without including "FreeRTOSConfig.h" without including it. "projdefs.h" does not include any other files for that matter.

How is this possible?

The case is shown below:

//projdefs.h

#ifndef pdMS_TO_TICKS
    #define pdMS_TO_TICKS( xTimeInMs )    ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000U ) )
#endif

//FreeRTOSConfig.h
#ifndef configTICK_RATE_HZ
#define configTICK_RATE_HZ (1000)
#endif

I've tried to google my way to the answer but nothing has been forthcoming.

How can a #define from another header file be used if the header file is not included?

Candidate answers.

  1. Even though projdefs.h did not direclty include FreeRTOSConfig.h , projdefs.h did include some.h file that included FreeRTOSConfig.h . Or maybe projdefs.h did include some.h file that included some.h file that include FreeRTOSConfig.h , etc. (OP did assert: "projdefs.h" does not include any other files for that matter.")

  2. projdefs.h itself or included directly (or indirectly) something that defined configTICK_RATE_HZ and the assertion that FreeRTOSConfig.h was included is incorrect.

  3. configTICK_RATE_HZ was defined by the compiler.


Design tip:

It is often a chore to find where a define , object, function or constant was declared/defined.

I have found using a common prefix_ for them all inside "same_prefix.h" to ease this issue.

How can a #define from another headder file be used if the headderfile is not included?

Macro definitions come from one of these sources:

  • source files, including headers, read by the compiler
  • compiler / standard library built-ins
  • compiler command-line arguments

C compilers do not process source files unasked, so a macro definition will be processed from header.h only if the compiler has a reason to read header.h . Usually, that reason is that the compiler processes an #include directive that directs it to the header. A header can also be named directly as a file to compile, but this will put its contents into their own translation unit, so it's probably not what you're observing. Some compilers also provide a means to specify headers via the command line, such as GCC's -include option , which perhaps fits your definition of "not included". Or not.

It can be the case that you get inclusion of the header in question via an obscure route, such as behind multiple levels of #include directives, or a reliance on prior #include directives to have been processed, or from an #include with a macro-generated header name, or via a symbolic-link-supported different name.

It's also possible to see a macro with the same name and replacement text that would be provided by inclusion of one header resulting from any of

  • inclusion of a different header;
  • direct definition via command-line argument; or
  • as a compiler built-in,

and none of those are well characterized as being from a header that is not included, but they might give that impression.


As for...

The file "projdefs.h" uses a define from the file "FreeRTOSConfig.h" without including "FreeRTOSConfig.h" without including it. "projdefs.h" does not include any other files for that matter.

... the fact that projdefs.h uses symbols that it does not define, neither directrly nor via including any other files, leads me to suppose that it simply is not a standalone header. It depends on being #include d only at a point where either

  • the macro pdMS_TO_TICKS is already defined, or
  • the macro configTICK_RATE_HZ is already defined, and the symbol TickType_t is defined either as a type designator or as a macro expanding to a type designator

In the beginning of FreeRTOS.h , you will notice:

/* Application specific configuration options. */
#include "FreeRTOSConfig.h"

/* Basic FreeRTOS definitions. */
#include "projdefs.h"

Also, pdMS_TO_TICKS() macro is not used by the kernel itself. FreeRTOS kernel always uses ticks. The macro is provided for users (we, application developers) only.

There are some good answers already posted, but just to add some details about the pre-processor and header files:

Everything in a header file gets expanded into each.c file that includes it. Formally ac file and all.h files it includes form a translation unit . So.h files don't really have a life of their own, their contents are always "glued" into the.c file by the pre-processor.

This can lead to various strange or unintended side effects. For example your FreeRTOSConfig.h will be able to access everything from projdefs.h if both were included from the same.c file and FreeRTOSConfig.h was included first. Relying on this is very bad practice though, since it forces the user of the header files to include them in a certain order. Instead each.h file should include every other.h file that it needs.

And that's where the so-called "header guards" #ifndef MYHEADER_H #define MYHEADER_H ... #endif come in. Because if you don't have these in every.h file, your.c file may end up with multiple declarations/definitions of the same thing after expanding the headers. This is also why it is problematic to define variables or functions inside a header file - the user may end up with multiple definitions.

A little list of some good/idiomatic practices:

  • Never force the caller to include header files in a certain order.
  • A header file should include all other header files it uses.
  • A header file should always contain header guards.
  • Never declare variables in header files. The last resort is to declare them as extern but global variables are also bad practice.
  • Avoid defining functions in header files. Some special cases like manual optimizations with static inline may be acceptable, but also as a last resort.

The file "projdefs.h" uses a define from the file "FreeRTOSConfig.h" without including "FreeRTOSConfig.h" without including it. "projdefs.h" does not include any other files for that matter.

This is possible as well as you finally end in a compilation unit including both files. If you don't include one of them, the undefined macros will expand as themselves.

This was historically used in the file <stdio.h> for some functions that were redefined as macros, to avoid a call to a function. For example, if you do #include <stdio.h> the macro putchar() was defined as:

#define putchar(c) fputc(c, stdout)

so in case you include the header, the call to putchar() is translated directly into a call to fputc() while if you didn't include it, a function that did the mapping was linked into the executable.

#include <stdio.h>
int putchar(int c)
{
    return fputc(c, stdout);
}

and you didn't note the difference.

This is still used in several ncurses library functions that use default screens in case you use the abbreviated form. This results in a less overhead of function calls.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM