'Header guards ignored by preprocessor using gcc [duplicate]

Im building a simple error checking system and get the error:

/tmp/ccNY7emi.o:(.data+0x0): multiple definition of `error_desc'
/tmp/ccMI89tg.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status

using gcc on Linux, the code is next, i believe is very self explanatory, im not an expert on C, must be error_desc declared as extern? and how?

error.h

#ifndef ERROR_H_
#define ERROR_H_

typedef enum error_e {
  ERR_DB_CONN_FAIL, ERR_BAD_PARAM, ERR_NET_CONN, ERR_STR_TO_LONG
} error_e;

char *error_desc[] = {
  "Fail to connect database",
  "invalid parameter",
  "Cannot connect to the network",
  "String length exceed established limit",
};

char *error_str(error_e err);

#endif

error.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "error.h"

unsigned get_arr_size(char **arr) {
  int count = 1;
  while (*++arr) {
    ++count;
  }
  return count;
}

char *error_str(error_e err) {                                                                                               
  unsigned arr_size = get_arr_size(error_desc);
  if ((unsigned)err < arr_size) {
    return error_desc[err];
  }
  return "Invalid error code";
}

use-error.c

#include <stdio.h>

#include "error.h"                                                                                                         

int main(void){
  printf("Error: %s\n", error_str(ERR_NET_CONN));
  printf("Error: %s\n", error_str(ERR_STR_TO_LONG));
  printf("%s\n", error_str(6));
}

If i move declaration/initialization of error_str to error.c it works, but i prefer have error_e and error_str due to "mapping" relation between them.

I modify main.c:

#include "error.h"                                                                                                         

int main(void){}

And examine the preprocessor output and get this:

gcc use-error.c error.c -E > prep.c

prep.c

# 1 "use-error.c"
# 1 "<command-line>"
# 1 "use-error.c"


# 1 "error.h" 1

typedef enum error_e {
  ERR_DB_CONN_FAIL, ERR_BAD_PARAM, ERR_NET_CONN, ERR_STR_TO_LONG
} error_e;

static char *error_desc[] = {
  "Fail to connect database",
  "invalid parameter",
  "Cannot connect to the network",
  "String length exceed established limit",
};

char *error_str(error_e err);
# 4 "use-error.c" 2

int main(void){
}
# 1 "error.c"
# 1 "<command-line>"
# 1 "error.c"
# 1 "error.h" 1

typedef enum errors {
  ERR_DB_CONN_FAIL, ERR_BAD_PARAM, ERR_NET_CONN, ERR_STR_TO_LONG
} errors_e;

static char *error_desc[] = {
  "Fail to connect database",
  "invalid parameter",
  "Cannot connect to the network",
  "String length exceed established limit",
};

char *error_str(errors_e err);
# 2 "error.c" 2

static unsigned get_arr_size(char **arr) {
  int count = 1;
  while (*++arr) {
    ++count;
  }
  return count;
}

char *error_str(error_e err) {
  unsigned arr_size = get_arr_size(error_desc);
  if ((unsigned)err < arr_size) {
    return error_desc[err];
  }
  return "Invalid error code";
}

It looks like preprocessor is ignoring the header guards, but why?

By the way, if declare static (that doesn't make many sense, i'll make it only to explore) the variable error_desc the code gets built, but if i use -Wall option on gcc i get the message:

warning: ‘error_desc’ defined but not used [-Wunused-variable]

If the code on error.c isn't using error_desc from error.h, then from where?



Solution 1:[1]

Header guards only protect a header from being included more than once in the same file. In this case it means that "error.h" will be included a maximum of once in "prep.c" and "error.c" leading to each of them having their own copy of error_desc which leads to your compile error.

The reason making error_desc static fixes this is because it tells the compiler to make it not visible outside of the file it's compiled in, removing the duplicate definition conflict. You get the warning because you have a version of error_desc in both "prep.c" and "error.c" but the version in "prep.c" is never used.

To fix this you should either move the definition of error_desc to "error.c" or if you want error_desc to be usable by any file that includes "error.h" change

static char *error_desc[] = {
  "Fail to connect database",
  "invalid parameter",
  "Cannot connect to the network",
  "String length exceed established limit",
};

in "error.h" to

extern char *error_desc[4];

and in "error.c" add

char *error_desc[] = {
  "Fail to connect database",
  "invalid parameter",
  "Cannot connect to the network",
  "String length exceed established limit",
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 rdtsc