1
/*
 * structs.h
 *
 *  Created on: 14 Jun 2019
 */
#ifndef STRUCTS_H_
#define STRUCTS_H_
    
#include <stdint.h>
#include "line_mask.h"
    
#define CONFIG_LINE_NUM                 80
    
#endif
/*
* line_mask.h
*
*  Created on: 15 Nov 2019
*/
    
#ifndef LINE_MASK_H_
#define LINE_MASK_H_
    
#include "../Inc/structs.h"
    
typedef struct
{
#if CONFIG_LINE_NUM <= 64
    uint64_t line_mask[1];
#elif (CONFIG_LINE_NUM <= 128)
    uint64_t line_mask[2];
#elif (CONFIG_LINE_NUM <= 192)
    uint64_t line_mask[3];
#elif (CONFIG_LINE_NUM <= 256)
    uint64_t line_mask[4];
#else
#error "Unsupported CONFIG_LINE_NUM value"
#endif
} LineMask;
#endif

Hello. Could you explain something to me? I wrote code where header files refer to each other in order to use definitions. As a result, it seems that the file line_mask.h was compiled before structs.h (which was confirmed when I added a check for #ifdef CONFIG_LINE_NUM in the file line_mask.h). As a result, the variable CONFIG_LINE_NUM didn't exist at the time the structure LineMask was being defined.

Why, in this case, did the compiler not report an error but instead likely substituted 0 for this variable, creating a structure with the array line_mask[1]?

And a second question: After it created such a structure, during the initialization of a certain module, the structure was initialized with line_mask[1], but later in the program, in a different part, the structure was created with line_mask[2], leading to a memory leak during copying.

Why does the definition of the LineMask structure change after the CONFIG_LINE_NUM directive is declared, despite the #ifndef LINE_MASK_H_ condition in the header file?

6
  • 1
    Circular include dependencies show a bad design. If you enable all warnings, you will probably get a warning that the value of the undefined symbol CONFIG_LINE_NUM is set to 0. You can also instruct the compiler to treat all warnings as errors. You could also add a check using #ifndef CONFIG_LINE_NUM Commented Sep 19, 2024 at 11:41
  • 3
    structs.h, as shown, does not need the two includes. Commented Sep 19, 2024 at 11:44
  • In a preprocessor conditional, if a symbol is not defined, it is assumed to have the value zero. Commented Sep 19, 2024 at 11:45
  • We cannot answer your second question, because you haven't shown the relevant code. It's all dependent on the order in which you include these headers and use their definitions. Commented Sep 19, 2024 at 11:47
  • You could have declared the line_mask member as uint64_t line_mask[(CONFIG_LINE_NUM + 63) / 64]; and avoided all those preprocessor conditions. That would have resulted in an error if CONFIG_LINE_NUM was undefined. Commented Sep 19, 2024 at 15:40

2 Answers 2

1

Why, in this case, did the compiler not report an error but instead likely substituted 0 for this variable, creating a structure with the array line_mask[1]?

C 2018 6.10.1 4 says:

… After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0, and then each preprocessing token is converted into a token.

Therefore, in #if CONFIG_LINE_NUM <= 64, CONFIG_LINE_NUM was replaced with 0.

And a second question: After it created such a structure, during the initialization of a certain module, the structure was initialized with line_mask[1], but later in the program, in a different part, the structure was created with line_mask[2], leading to a memory leak during copying.

This was caused by different code which you have not shown and therefore cannot be answered definitively.

Sign up to request clarification or add additional context in comments.

Comments

1

In 100% of all use-cases, circular include dependencies are caused by incorrect design. And so it's not really relevant to even look at the details in the code because in a proper designed program there is never a need for two files to mutually include each other.

Each .h and .c pair should form a "module" or "class" or "abstact data type" or whatever you like to call it. This module should have a purpose of its own related to the application, for example some print.h/print.c with the sole purpose of printing stuff on a console. This module doesn't know anything else but how to print stuff.

You may of course have headers without a corresponding C file for the purpose of defining macros, types or constants etc. But it should still be related to the application; you shouldn't for example create some "struct.h" for the purpose of defining structs - there exists nothing in your end application use-cases called "structs", that's a C programming language term. Rather you could have some header called print_formats.h defining various struct types used by print.h/print.c. Meaning print.h knows about print_formats.h, includes it & uses it. But print_formats.h doesn't know a thing about print.h.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.