I am trying to compile a program which is divided into 3 modules, corresponding to 3 source files: a.c, b.c, and z.c. z.c contains the main() function, which calls functions in a.c and b.c. Furthermore, a function in a.c calls a function in b.c, and viceversa. Finally, there is a global variable count which is used by the three modules and is defined in a separate header file, global.h.
The code of the source files is the following:
a.c
#include "global.h"
#include "b.h"
#include "a.h"
int functAb() {
functB();
functA();
return 0;
}
int functA() {
count++;
printf("A:%d\n", count);
return 0;
}
b.c
#include "global.h"
#include "a.h"
#include "b.h"
int functBa() {
functA();
functB();
return 0;
}
int functB() {
count++;
printf("B:%d\n", count);
return 0;
}
z.c
#include "a.h"
#include "b.h"
#include "global.h"
int main() {
count = 0;
functAb();
functBa();
return 0;
}
The header files:
a.h
#ifndef A_H
#define A_H
#include <stdio.h>
int functA();
int functAb();
#endif
b.h
#ifndef B_H
#define B_H
#include <stdio.h>
int functB();
int functBa();
#endif
global.h
#ifndef GLOBAL_H
#define GLOBAL_H
extern int count;
#endif
And, finally, the makefile that reproduces my error:
CC = gcc
CFLAGS = -O3 -march=native -Wall -Wno-unused-result
z: a.o b.o z.o global.h
$(CC) -o z a.o b.o z.o $(CFLAGS)
a.o: a.c b.h global.h
$(CC) -c a.c $(CFLAGS)
b.o: b.c a.h global.h
$(CC) -c b.c $(CFLAGS)
z.o: z.c a.h global.h
$(CC) -c z.c $(CFLAGS)
With this, I can compile the objects a.o, b.o, and z.o fine, but, when linking with make z, I get undefined reference to 'count' in all of them:
z.o: In function `main':
z.c:(.text.startup+0x8): undefined reference to `count'
a.o: In function `functAb':
a.c:(.text+0xd): undefined reference to `count'
a.c:(.text+0x22): undefined reference to `count'
a.o: In function `functA':
a.c:(.text+0x46): undefined reference to `count'
a.c:(.text+0x5b): undefined reference to `count'
b.o:b.c:(.text+0xd): more undefined references to `count' follow
collect2: ld returned 1 exit status
I managed to reproduce the error in my actual code in this minimal example, so I guess there is a problem in the dependencies between modules, but I can't spot it. Can anyone point me in the right direction?
Change your z.c to
#include "a.h"
#include "b.h"
#include "global.h"
int count; /* Definition here */
int main() {
count = 0;
functAb();
functBa();
return 0;
}
From global.h, all your files inherit the declaration of variable count but the definition is missing from all files.
You must add the definition to one of the file as int count = some_value;
You have declared count, not defined it.
extern is a part of declaration, not a definition.
To be explicit, extern is a storage-class specifier and used at declaration.
You need to define int count somewhere in your source files.
You have to add int count; to your z.c file.
This because of declaring a variable in header file as extern tells to the compiler that the variable will be declared in another file, but the variable is not declared yet and will be resolved b linker.
Then you need to declare the variable somewhere.
Related
I'm using Cmocka to write a unit testing suite for a shared object written in C, but I'm having some issue. Since I cannot share the source code, I have written a minimum "not-working" example to show what is the issue:
my program is composed 5 files: foo.c, foo.h bar.c bar.h main.c.
bar.* files define a bar() function, which simply returns the argument multiplied by 2
foo.* files declare a foo() function that uses the bar() function defined by bar.h
main.c contains a simple cmocka test and a __wrap_bar()
function, returning the argument multiplied by 3.
I compile the program by producing a libfootest.so object (foo+bar) and then I link this object with main.o passing the -Wl,--wrap=bar flag to the compiler. In this configuration libfootest is the module under test and main is the tester program. I expect the __wrap__bar to be called (failing the test), but the standard bar() is called(test is passed). How can I solve this problem? Below you find all the code I'm using.
bar.c:
#include "bar.h"
int bar(int val) {
return val*2;
}
bar.h:
int bar(int val);
foo.h:
#include <stdio.h>
int foo(int val);
foo.c:
#include "foo.h"
#include "bar.h"
int foo(int val) {
int ret;
ret = bar(val);
printf("RET: %d", ret);
return ret;
}
main.c:
#include <stdio.h>
//required include for CMOCKA
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmocka.h>
//library under test
#include "foo.h"
int __wrap_bar(int val) {
return 3*val;
}
static void test_foo(void **state) {
int ret = foo(5);
assert_int_equal(ret, 10);
}
int main (int argc, char** argv) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_foo),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
Makefile:
CMOCKA_LIB_DIR=../../cmocka-1.1.5/build/src
CXXFLAGS+=-g -Og -fPIC
CFLAGS+=-g -Og -std=c99 -fPIC
CC=gcc
CXX=g++
all: main.o ./libfootest.so
gcc -o linux-test -g -L. -L$(CMOCKA_LIB_DIR) $(filter %.o, $^) -lcmocka -lfootest -Wl,-rpath=. -Wall -Wl,--wrap=bar -Wl,-rpath=$(CMOCKA_LIB_DIR)
./libfootest.so: foo.o bar.o
$(CC) -shared -o $# -g $^ -pedantic -Wall
clean:
rm -f *.o
rm -f *.so
The problem is your build of the library. You don't create a link library as commonly done, with separated modules. Instead you link all given modules and place the resulting single module in the target library.
That's why the linker resolved the call to bar() already, and it is no longer unresolved when linking the test program.
The option --wrap works only for unresolved references between modules.
The solution is to build the library from separated modules. Use the tool ar for this:
ar r libfootest.a foo.o bar.o
This question already has answers here:
How do I use extern to share variables between source files?
(19 answers)
Access a global static variable from another file in C
(4 answers)
Closed 7 years ago.
To set the scene here - I have 2 .c files, call it a.c and b.c.
I then have 2 header files for each .c file, a.h (which has all function prototypes, and all global variables declared as extern type name;), and b.h which has only function prototypes of b.c as b.c contains no global variables.
I want to access a.c's global variables in b.c, so I have added a statement #include "a.h" in b.c.
Only problem is, I still can't access a.c's global variables in b.c, for example if I want to print. I have a global variable int i; in a.c, and if I do:
i = 5;
printf("%d", i); in b.c, I get an error saying that variable i has not been declared. What am I doing wrong?
The code:
a.c:
#include "b.h"
int i;
int main() {
executeMethod();
return 0;
}
b.c:
#include "a.h"
void executeMethod() {
i = 10;
printf("%d", i);
a.h:
int main();
extern int i;
b.h:
void executeMethod();
makefile:
CFLAGS=-Wall -g
all: main
main: a.c b.c a.h b.h
gcc $(CFLAGS) -o main a.c b.c a.h b.h
clean:
rm -f main
Have also tried without the makefile:
gcc -o main a.c b.c a.h b.h
Thanks.
Edit: it works if I define extern int i; on top of my b.c file, but say I have 60 variables, I would rather have them in a header.h file and just #include "header.h" rather than writing 50 extern statements.
Go ahead and include the header with the ext in both files. Put the definition and initialization in the file that owns the variable. This is okay:
extern int aaa;
int aaa = 1;
I created a sample .h file in C and it didn't work, for some reason. The files are as follows:
header.c:
#include <stdio.h>
#include "header.h"
int add(int a, int b) {
int tmp=a;
int i;
for(i=0, i==tmp, i++) {
b++;
}
return(b);
}
header.h:
#ifndef HEADER_H
#define HEADER_H
int add(int a, int b);
#endif
main.c:
#include <stdio.h>
#include "header.h"
int main(void) {
int foo=add(1, 2);
printf("%i \n", foo);
return(0);
}
When I try to compile main.c with make and gcc it says that add is undefined. Help!
You need to compile both main.c and header.c into the same executable:
all: main
main: main.o header.o
gcc -o main main.o header.o
header.o: header.c header.h
gcc -c header.c
main.o: main.c header.h
gcc -c main.c
Or for a one-liner without a make file:
gcc -g -o main main.c header.c
Including the header file only includes the function prototype. You need to link the actual definition of add() by compiling separate object files or you can compile them together in a single command line:
gcc -Wall -Wextra header.c main.c -o main
Perhaps, you may want to consider Makefiles for larger projects.
Your add() function has issues:
1) Semi-colons ; are used in for loops, not commas.
2) The condition should be i!=tmp for addition.
This:
for(i=0, i==tmp, i++) { .. }
should be
for(i=0; i!=tmp; i++) { .. }
You need to add header.c to the compile call. You can't just compile main.c.
The code is as follows:
global.h
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdio.h>
int test;
void test_fun(void);
#endif
global.c
#include "global.h"
void test_fun()
{
printf("%d\n", test);
}
main.c
#include "global.h"
int main(void)
{
test_fun();
test = 1;
printf("%d\n", test);
}
Makefile using gcc compiler
main: main.o global.o
gcc -o main main.o global.o
main.o: main.c global.h
gcc -c main.c
global.o: global.c global.h
gcc -c global.c
clean:
rm -f global.o main.o main
This works well.
However, when I change my code to C++, as follows:
global.h
#ifndef GLOBAL_H
#define GLOBAL_H
#include <iostream>
int test;
void test_fun(void);
#endif
global.cpp
#include "global.h"
void test_fun()
{
cout << test
}
main.cpp
#include "global.h"
int main(void)
{
test_fun();
test = 1;
std::cout << test;
}
Makefile using g++ compiler
main: main.o global.o
g++ -o main main.o global.o
main.o: main.cpp global.h
g++ main.cpp
global.o: global.cpp global.h
g++ global.cpp
clean:
rm -f global.o main.o main
The code above throws the output:
global.o:(.bss+0x0): multiple definition of `test'
What makes the different here?
You've int test; in a header which is included in 2 TUs, hence the error. Both the translation units main.c (or .cpp depending upon the compiler used) and global.c have global.h included, which leads to two definitions of the same variable in two object files, thus the linker error.
Pass test as an arguement to test_fun, thereby avoiding the usage of a global.
If you absolutely have to share the variable between the TUs, then remove int test; from global.h and in main.cpp do
int test;
and in global.cpp do
extern int test;
As an aside, since it's a global variable, test would be initialized to 0 and hence in main when you test_fun();, it should print 0 and then after setting it to 1, it'll print 1.
It's illegal in both C and C++ from a language standpoint, but as for why it works with a C compilers (like GCC) is because they implement a common extension, a legacy cruft.
... You are using a different programming language
I'm wondering why is this code compiles and run. I thought that if a variable is declared as static (in global scope) it will be accessible only within the file it is declared.
functions.h
static int x = 10;
main.c
#include <stdio.h>
#include "functions.h"
extern int x;
int main()
{
printf("%d", x);
}
Technically, it is indeed declared within the main.c, as this includes the functions.h. If it was a sparate compilation module, you'd be right.
But I'd have suspected that within the same compilation unit extern and staticwould collide with each other. At least it would be worth a warning.
The preprocessor takes the text in functions.h and copies it as is into main.c
After preprocessing (and before compilation) your main.c looks as follows:
#include <stdio.h>
static int x = 10;
extern int x;
int main()
{
printf("%d", x);
}
You will have linker problems if functions.h is included into a second source file, and you try to link both object files into one executable.
when you are including functions.h in main.c , you are actually copy content of function.h in main.c so your final code become something like :
#include <stdio.h>
static int x = 10;
extern int x;
int main()
{
printf("%d", x);
}
So your extern line is redundant.
you can achieve what you want by this
remove #include "functions.h" from main.c
compile function.h using g++ -c function.h
compile main.c using g++ -c main.c
then build g++ function.o main.o -o out
third line would not compile because of static int .