3

We have a very simple bit of code which is giving a warning but seems like it should be fine, and runs fine. We understand that in general there may be issues with order of initialisation between compilation units, but in this case we are initialising with a pointer, so don't understand how order can cause a problem, or how any problems might arise with this code. Note that in the real code we have more complex scenario with structs, but code below shows the basic issue.

EDIT: have removed const qualifiers as they don't affect the warning

file1.c

int foo=42;

file2.c

#include <stdio.h>

extern int foo;
// Clang-Tidy: Initializing non-local variable with non-const expression depending on uninitialized non-local variable 'foo'
int *bar=&foo;

int main() {
  printf("%d\n", *bar); // prints '42'
}

Warning is not from gcc but from clang-tidy. To reproduce run:

clang-tidy -checks=* file1.c file2.c
jugglingcats
  • 698
  • 9
  • 29
  • 3
    Basically it is saying that spaghetti programming with globals is fishy practice. Don't write fishy code and your static analyser will be happy. – Lundin Jun 29 '21 at 08:42
  • 1
    The problem is EXACTLY what the compiler is telling you the problem is. The problem it can cause is that there is no way to enforce that file1.c will have a declaration that is a `const` for the symbol `foo` or that there would be a value assigned to the constant and the compiler is trying to help you recognize that. The resolution of the value in this case would be done during linking, the best the compiler can do is warn you about the lack of value in file2.c ... If this is how you have code in production you have bigger problems. – Ahmed Masud Jun 29 '21 at 08:56
  • Problem is not related to const qualifiers - have removed and warning remains – jugglingcats Jun 29 '21 at 09:43
  • As I mentioned in the post, our actual code is more complex and we have a valid need for this pattern, so comments saying our code is bad is not super helpful! – jugglingcats Jun 29 '21 at 09:50
  • 1
    @jugglingcats: `clang-tidy` is warning about something that just is not a problem: whether `foo` is initialized or not does not make a difference in this initializer where only its address is taken. Furthermore `foo` is a global variable, hence it is either statically initialized in the module that defines it or it does not have an initializer and is initialized to 0 by the loader at run time. – chqrlie Jan 06 '22 at 21:48

2 Answers2

1

clang-tidy is warning about something that just is not a problem: whether foo is initialized or not does not make a difference in this initializer where only its address is taken.

Furthermore foo is a global variable, hence it is either statically initialized in the module that defines it or it does not have an initializer and is initialized to 0 by the loader at run time. If the program was compiled as C++, foo might be initialized at runtime, before main is called, potentially by running initializer code, but again this does not make a difference as only its address is used in int *bar = &foo;

You should just disable this warning, as explained by Cameron Tacklind.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
0

As others have mentioned, clang-tidy is telling you precisely what is wrong.

In this case, I think the clincher is the first part of the sentence: "Initializing non-local variable ...".

The particular warning will go away if bar is declared (and defined) inside main():

#include <stdio.h>

extern int foo;

int main() {
  int *bar=&foo; // Still warns about const issues

  printf("%d\n", *bar);
}

Alternatively, you can ignore the specific rule for those specific lines pretty easily:

#include <stdio.h>

extern int foo;

// NOLINTNEXTLINE(cppcoreguidelines-interfaces-global-init)
int *bar=&foo;

int main() {
  printf("%d\n", *bar);
}
Cameron Tacklind
  • 5,764
  • 1
  • 36
  • 45
  • 1
    The sentence is a statement of something that is being done, not a statement of anything that is wrong. C 2018 6.6 7 says that an address constant maybe used as a constant expression in an initializer, and 6.6 9 says an *address constant* is, among other things, a pointer to an lvalue designating an object of static storage duration and that “it shall be created explicitly using the unary `&` operator or…”. – Eric Postpischil Jan 06 '22 at 21:36
  • 1
    Maybe the problem it is intended to warn about is using `*bar` before `foo` is initialized, but it does not say that. Actually, that would not be possible; `foo` necessarily has static storage duration, so it is initialized before anything that could use `*bar` executes. – Eric Postpischil Jan 06 '22 at 21:36
  • @EricPostpischil: this warning seems bogus to me too. – chqrlie Jan 06 '22 at 21:44
  • @EricPostpischil I'm imagining some situations where `bar` could also be declared `extern` in some header file, which enables `foo`'s definition to depend on `bar`, creating a possibly unresolvable circular definition. But maybe that's not possible for the reasons you're describing and I'm not seeing it. Regardless, this sounds like something that should be brought up with Clang. I'm just giving options to make that particular warning go away with how these tools currently work. – Cameron Tacklind Jan 06 '22 at 22:37