Search code examples
ccmakelinkerexternundefined-reference

Why do I get "undefined reference"-error with a variable marked as 'extern'? Linker problem?


I know this question has already been asked (in similar ways) several times, but I don't think it's a duplicate, because my code already implements the solution offered to the other questioners. If I overlooked something, my question may of course be marked as a duplicate and downgraded.

I have to use an external variable, because according to the task, I am not allowed to pass it as a parameter. The problem: If I want to compile the code, the "undefined reference"-error is thrown.

The code:

header.h

#ifndef TEST_HEADER_H
#define TEST_HEADER_H

extern int var;

void increment();

#endif //TEST_HEADER_H

source1.c

#include <stdio.h>
#include "header.h"

int main ()
    {
        int var = 1;
        printf("1) %d\n", var);

        increment();
        printf("2) %d\n", var);

        return 0;
    }

source2.c

#include "header.h"

void increment()
    {
        var++;
    }

The compile error:

====================[ Build | test | Debug ]====================================
/root/clion-2019.1/bin/cmake/linux/bin/cmake --build /root/CLionProjects/test/cmake-build-debug --target test -- -j 2
Scanning dependencies of target test
[ 33%] Building C object CMakeFiles/test.dir/source1.c.o
[ 66%] Building C object CMakeFiles/test.dir/source2.c.o
[100%] Linking C executable test
/usr/bin/ld: CMakeFiles/test.dir/source2.c.o: in function `increment':
/root/CLionProjects/test/source2.c:5: undefined reference to `var'
/usr/bin/ld: /root/CLionProjects/test/source2.c:5: undefined reference to `var'
collect2: error: ld returned 1 exit status
make[3]: *** [CMakeFiles/test.dir/build.make:100: test] Fehler 1
make[2]: *** [CMakeFiles/Makefile2:73: CMakeFiles/test.dir/all] Fehler 2
make[1]: *** [CMakeFiles/Makefile2:85: CMakeFiles/test.dir/rule] Fehler 2
make: *** [Makefile:118: test] Fehler 2

The CMakeLists.txt:

cmake_minimum_required(VERSION 3.14)
project(test C)

set(CMAKE_C_STANDARD 11)

add_executable(test source1.c header.h source2.c)

I also tried adding the following to the CMakeLists.txt, because it helped in another case of the "undefined reference"-error, but in this case it makes no difference.

find_library(LIBRT rt)
if(LIBRT)
    target_link_libraries(test ${LIBRT})
endif()

I don't think the problem's in the code, is it? I think it's in the linking process. Can somebody help? Thanks in advance!

EDIT:

It runs now. My problem wasn't that I can't distinguish declaration and definition, as some assume, but that I set the definition in 'source1.c' in the wrong place within the 'main' (local) and not outside (global). So, to be precise, my misunderstanding was the estimation of the scope between an external declaration and its definition, if you understand what I mean. Admittedly, that wasn't a stroke of genius. ;-) Thank you for your help!


Solution

  • You misunderstand what extern means.

    Marking a variable (or function) as extern does not define (create) that variable. Rather it just declares that it exists somewhere. You do not have a definition for var anywhere.

    In your main you have int var = 1; but that definition is local to main. It is not global, and not accessible to any other functions.

    You should move that variable definition out of main:

    #include <stdio.h>
    #include "header.h"
    
    int var = 1;
    
    int main ()
    {
        printf("1) %d\n", var);
    
        increment();
        printf("2) %d\n", var);
    
        return 0;
    }
    

    On a related note: you should avoid using global variables. They lead to code that is difficult to understand, debug, and maintain.