1

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!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
a kind person
  • 329
  • 1
  • 6
  • 17

2 Answers2

4

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.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • 2
    Quibble: *merely* declaring a variable `extern` does not define it or cause it to be defined, yes, but an `extern` declaration does serve as a definition if it includes an initializer. – John Bollinger May 02 '19 at 11:46
  • @John Ah, yes I forgot about that case. I never use it because it feels so contradictory. – Jonathon Reinhart May 02 '19 at 11:48
  • I agree. The purpose of explicitly declaring a variable `extern` is normally to avoid that declaration serving as a definition, so overcoming that by providing an initializer is a bit odd. But it is also consistent with related language features. I submit that the truly odd thing is that it can make a difference to explicitly declare an object to have an attribute that it would have by default anyway. – John Bollinger May 02 '19 at 11:55
  • @JohnBollinger I suppose it is a bit odd, but `extern` is a storage class specifier, so not allowing it in a definition with an initializer would also be odd, even though in practice, its main use is to avoid turning an external variable declaration into a tentative definition. – Ian Abbott May 02 '19 at 12:25
1

This

extern int var; /* declaration, it doesn't exist until its not defined somewhere */

just a declaration, you need to define it. Since you didn't provide definition for var, linker is unable to locate it, hence the error

undefined reference to `var'

Note that in source1.c

int var = 1;

its not the definition of extern variable, its a locally created variable having functional scope.

Achal
  • 11,821
  • 2
  • 15
  • 37