inline function definition in header vs source file - c

I am confused about how to inline functions in c (C99 onwards). In section 18.6 of "C programming, a modern approach" (K.N. King, 2nd edition), or for example 3 in this tutorial (under "Strategies for using inline functions"), the definition of an inline function is given in the header (.h) file, then the function is again listed as extern in a source (.c) file.
For example, what I am doing: in a header "stencil.h"
#ifindef _STENCIL_H
#define _STENCIL_H
inline double D1_center_2ndOrder(double vp1, double vm1, double dr)
{
return (vp1-vm1) / (2.0 * dr) ;
}
#endif
Then in a matching source file "stencil.c" one defines
#include "stencil.h"
extern double D1_center_2ndOrder(double vp1, double vm1, double dr) ;
I did this, and then in a file "main.c" I called average:
#include <stdio.h>
#include <stdlib.h>
#include "stencil.h"
int main(int argc, char *argv[])
{
double vp1 = 1 ;
double vp2 = 2 ;
dr = 0.1 ;
double der_v = D1_center_2ndOrder(vp1, vm1, dr) ;
printf("der_v\t%f\n", der_v) ;
return 0 ;
}
I compile everything with the following makefile
CC = gcc
CFLAGS = -Wall -lm -std=gnu11
OBJECTS = main.o stencil.o
DEPS_MAIN = stencil.h
test: $(OBJECTS)
$(CC) -o collapse $(OBJECTS) $(CFLAGS)
main.o: main.c $(DEPS_MAIN)
$(CC) -c main.c
stencil.o: stencil.c stencil.h
$(CC) -c stencil.c
And I then get the following compiler error:
gcc -c main.c
gcc -c stencil.c
gcc -o test main.o stencil.o -Wall -lm -std=gnu11
stencil.o: In function `D1_center_2ndOrder':
stencil.c:(.text+0x0): multiple definition of `D1_center_2ndOrder'
main.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [collapse] Error 1
When I define the function in the .c source file "stencil.c" and declare it in the header file I do not get the above error. The version of gcc I am using is
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28).
My questions are:
(1) Why then does "C programming, a modern approach" and the tutorials on inlining functions that I find online suggest defining the function in the header file and listing it again as extern in a source file? Doing so for the above gives me a compiler error.
(2) When I declare the inline function in a header file then define as extern in a source file, will the compiler still inline my function?

You are using a portable strategy based on standard C99 (and more recent), which is entirely correct.
It is failing because you invoke gcc with its default -std setting, which for GCC 4.8.5 effectively tells it to use its legacy semantics and not standard C11 semantics. The default value for -std was gnu90 in version 4.8.5. In version 5, it was changed to gnu11, which implements C99/C11 inline semantics.
You're using default settings because your Makefile does not include $(CFLAGS) in the compile recipes; only in the final link recipe. (You can see that in the commands printed out by make).
While -std=gnu11 should work, it would be better to use -std=c11 instead. And consider upgrading to a more recent GCC version.

You need to pass the standard-version flag while compiling. It's not doing any good when you pass it in the linking stage.
So your makefile should be something like:
CC = gcc
CFLAGS = -Wall -std=gnu11
LDFLAGS = -lm
OBJECTS = main.o stencil.o
DEPS_MAIN = stencil.h
test: $(OBJECTS)
$(CC) -o collapse $(OBJECTS) $(LDFLAGS)
main.o: main.c $(DEPS_MAIN)
$(CC) -c main.c $(CFLAGS)
stencil.o: stencil.c stencil.h
$(CC) -c stencil.c $(CFLAGS)
I've tested your example with this shellscript:
#!/bin/sh -eu
cat > stencil.h <<EOF
#ifndef _STENCIL_H
#define _STENCIL_H
inline double D1_center_2ndOrder(double vp1, double vm1, double dr)
{
return (vp1-vm1) / (2.0 * dr) ;
}
#endif
EOF
cat > stencil.c <<EOF
#include "stencil.h"
extern double D1_center_2ndOrder(double vp1, double vm1, double dr) ;
EOF
cat > main.c <<EOF
#include <stdio.h>
#include <stdlib.h>
#include "stencil.h"
int main(int argc, char *argv[])
{
double vp1 = 1 ;
double vp2 = 2 ;
double dr = 0.1 ;
double vm1 = 0;
double der_v = D1_center_2ndOrder(vp1, vm1, dr) ;
printf("der_v\t%f\n", der_v) ;
return 0 ;
}
EOF
: ${CC:=gcc}
set -x
gcc -c main.c -std=c99
gcc -c stencil.c -std=c99
gcc -o test main.o stencil.o -lm
and gcc 4.6.4 and it's working fine, as long as the compilations (rather than the linking command) get at least -std=c99 (It's not working with 4.6.4's defaults).
(Side note: the header guard shouldn't start with an underscore and an uppper-case letter.)

Related

Different behavior between clang and gcc-10 when linking to static library containing global variables

I have a statically linked library, containing a global variable barvar. I can compile the library with no problems with either gcc-10 or clang (this is on macOS Catalina). Interestingly, the behavior differs between the two when I try to link it into a program that uses the library. Here's the code:
In globvars.h, int barvar is declared:
#ifndef H_GLOBVARS_H
#define H_GLOBVARS_H
extern int barvar;
#endif
In globvars.c, int barvar is defined:
#include "globvars.h"
int barvar;
In foo.c, the function foo sets and prints barvar:
#include <stdio.h>
#include "globvars.h"
void foo()
{
barvar = 10;
printf("barvar is: %d\n", barvar);
return;
}
Here's test.c, the program that uses the library:
void foo();
int main(int argc, char **argv)
{
foo();
return 0;
}
When I compile and link with gcc-10, no problems:
gcc-10 -c foo.c -o foo.o
gcc-10 -c globvars.c -o globvars.o
gcc-10 -c test.c -o test.o
gcc-ar-10 rcs liblinktest.a foo.o globvars.o
gcc -o testlinkrun test2.o -L. -llinktest
When I compile and link with clang, I get an undefined symbol error at the last step:
cc -c foo.c -o foo.o
cc -c globvars.c -o globvars.o
cc -c test.c -o test.o
ar rcs liblinktest.a foo.o globvars.o
cc -o testlinkrun test2.o -L. -llinktest
with error:
Undefined symbols for architecture x86_64:
"_barvar", referenced from:
_foo in liblinktest.a(foo.o)
Any ideas? Interestingly, it appears the only step that has to be done with gcc-10 is compiling globvars.c. I can use clang and the clang linker for all other steps, and everything is fine. Is it possible that clang is optimizing away all the variables in globvars.c? How can I prevent this?
As #EricPostpischil observed in this comment, the issue is that clang defaults to treating barvar as a common symbol. Either changing int barvar; to int barvar = 0;, or compiling with -fno-common, fix the issue.
Beginning with gcc-10, gcc's default behavior is -fno-common instead of -fcommon.

Hexagon compiler having trouble with `typedef struct mystruct *mystruct` [duplicate]

This question already has answers here:
Is it a good idea to typedef pointers?
(15 answers)
Closed 6 years ago.
I have this line in a header file:
typedef struct mystruct *mystruct;
And the corresponding struct definition in a .c file. Pretty standard practices.
I am getting this compilation error:
fatal error: typedef redefinition with different types ('struct mystruct *' vs mystruct')
This is using the Hexagon Tools Compiler (7.2.12) from Hexagon 3.0 SDK. It is officially QuIC LLVM Hexagon Clang version 7.2.12. Building for Snapdragon Flight. This should work as far as I know. It works with Ubuntu clang version 3.5.0-4ubuntu2~trusty2 (based on LLVM 3.5.0) for x86_64-pc-linux-gnu.
What is wrong here? Is this type of typedef a newer feature of C that is not implemented in the compiler, or rather are compiler differences like these common?
Edit: Actually struct is defined in a .c, not .cpp, file. Added the Makefile and make output showing compilation with Ubuntu clang, as well as the top of the header file with the troublesome typedef statment. A test is run at the end, and all 105 tests pass.
Edit2: See Jonathan Leffler's answer for cases where this works vs doesn't work.
ringbuf.h:
#include <stddef.h>
#include <sys/types.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
typedef struct ringbuf_t *ringbuf_t;
Makefile:
CC=clang
CFLAGS=-O0 -g -Wall -Wpointer-arith -ftrapv -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error
# or, for gcc...
#CC=gcc
#CFLAGS=-O0 -g -Wall
LD=$(CC)
LDFLAGS=-g
test: ringbuf-test
./ringbuf-test
coverage: ringbuf-test-gcov
./ringbuf-test-gcov
gcov -o ringbuf-gcov.o ringbuf.c
valgrind: ringbuf-test
valgrind ./ringbuf-test
help:
#echo "Targets:"
#echo
#echo "test - build and run ringbuf unit tests."
#echo "coverage - use gcov to check test coverage of ringbuf.c."
#echo "valgrind - use valgrind to check for memory leaks."
#echo "clean - remove all targets."
#echo "help - this message."
ringbuf-test-gcov: ringbuf-test-gcov.o ringbuf-gcov.o
gcc -o ringbuf-test-gcov --coverage $^
ringbuf-test-gcov.o: ringbuf-test.c ringbuf.h
gcc -c $< -o $#
ringbuf-gcov.o: ringbuf.c ringbuf.h
gcc --coverage -c $< -o $#
ringbuf-test: ringbuf-test.o libringbuf.so
$(LD) -o ringbuf-test $(LDFLAGS) $^ -L$(MY_LIBS_PATH) -lringbuf
ringbuf-test.o: ringbuf-test.c ringbuf.h
$(CC) $(CFLAGS) -c $< -o $#
libringbuf.so: ringbuf.o
$(CC) -shared -o libringbuf.so ringbuf.o
cp ./libringbuf.so $(MY_LIBS_PATH)/
ringbuf.o: ringbuf.c ringbuf.h
$(CC) $(CFLAGS) -fPIC -c $< -o $#
cp ./ringbuf.h $(MY_INCLUDES_PATH)/
clean:
rm -f ringbuf-test ringbuf-test-gcov *.o *.so *.gcov *.gcda *.gcno
.PHONY: clean
make output:
clang -O0 -g -Wall -Wpointer-arith -ftrapv -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -c ringbuf-test.c -o ringbuf-test.o
clang -O0 -g -Wall -Wpointer-arith -ftrapv -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -fPIC -c ringbuf.c -o ringbuf.o
cp ./ringbuf.h /home/eric/Includes/
clang -shared -o libringbuf.so ringbuf.o
cp ./libringbuf.so /home/eric/Libs/
clang -o ringbuf-test -g ringbuf-test.o libringbuf.so -L/home/eric/Libs -lringbuf
./ringbuf-test
Edit3: This actually works fine with just the Hexagon-clang compiler. It is the compilation process of the larger program that this module exists in that is being problematic. I think that it is attempting to compile this code as C++.
Your code would be fine, in C and C++, if you did not try to Use typedef for a pointer type.
The header (hdr.h for instance) could/should contain:
typedef struct mystruct mystruct;
The source (hdr.cpp for instance) could contain:
#include "hdr.h"
struct mystruct
{
const char *a;
int b;
int c;
};
#include <iostream>
int main()
{
mystruct *ap = new mystruct;
ap->a = "collywobbles";
ap->b = 1;
ap->c = 2;
std::cout << "a: " << ap->a << ", b = " << ap->b << ", c = " << ap->c << "\n";
return 0;
}
This will compile in C++, even under stringent warnings. An equivalent C main() using <stdio.h> would work in C.

C - multiple definition with makefile

I am trying to compile my C program using make and I've come across this problem that I can't quite understand. I have 3 files in the 'calc' folder of my project: add.c sub.c and main.c. I have my Makefile located in the root folder of my project, which has the calc folder that I mentioned in it. This is what my Makefile looks like:
CC=gcc
OBJECTS=obj/main.o obj/add.o obj/sub.o
elf/new: ${OBJECTS}
${CC} -o elf/new ${OBJECTS}
obj/main.o: calc/main.c
${CC} -c -g calc/main.c -o obj/main.o
obj/add.o: calc/add.c
${CC} -c -g calc/add.c -o obj/add.o
obj/sub.o: calc/sub.c
${CC} -c -g calc/sub.c -o obj/sub.o
clean:
rm obj/${OBJECTS} elf/new
When I type 'make' into the terminal to compile, I get an error like this:
gcc -c -g calc/add.c -o obj/add.o
gcc -c -g calc/sub.c -o obj/sub.o
gcc -o elf/new obj/main.o obj/add.o obj/sub.o
obj/add.o: In function `add':
/home/bigger/workspace/test/calc/add.c:1: multiple definition of `add'
obj/main.o:/home/bigger/workspace/test/calc/add.c:1: first defined here
obj/sub.o: In function `sub':
/home/bigger/workspace/test/calc/sub.c:1: multiple definition of `sub'
obj/main.o:/home/bigger/workspace/test/calc/sub.c:1: first defined here
collect2: error: ld returned 1 exit status
makefile:5: recipe for target 'elf/new' failed
make: *** [elf/new] Error 1
And my code are there:
bigger#linux:~/workspace/test> cat calc/add.c
int add(int a, int b){
return a+b;
}
bigger#linux:~/workspace/test> cat calc/sub.c
int sub(int a, int b) {
return a-b;
}
bigger#linux:~/workspace/test> cat calc/main.c
#include <stdio.h>
#include "add.c"
#include "sub.c"
int main(int argc, char* argv[])
{
int a = 10;
int b = 5;
printf("add: %d\nsub:%d\n", a+b, a-b);
return 0;
}
When you include it is making the functions add and sub part of your main.c, then when you make you are linking main (which already has the functions by include) to the add and sub objects which have the same function symbols. You need to include header files with function declarations rather than include function definitions. See http://www.cprogramming.com/declare_vs_define.html for a longer discussion.

Hierarchical Linking in C

I want to link three files but in hierarchical way.
// a.c
int fun1(){...}
int fun2(){...}
// b.c
extern int parameter;
int fun3(){...//using parameter here}
// main.c
int parameter = 1;
int main(){...// use fun1 fun2 fun3}
So, I first compile three files separately into object file a.o, b.o and main.o. And then I want to combine a.o and b.o into another object file tools.o. And eventually use tools.o and main.o to generate executable file.
But, when I try to combine a.o and b.o like ld -o tools.o a.o b.o, the linker says undefined reference to 'parameter'. How could I link those object files into an intermediate object file?
You want the -r option to produce a relocatable object file (think 'reusable'):
ld -o tools.o -r a.o b.o
Working code
abmain.h
extern void fun1(void);
extern void fun2(void);
extern void fun3(void);
extern int parameter;
a.c
#include <stdio.h>
#include "abmain.h"
void fun1(void){printf("%s\n", __func__);}
void fun2(void){printf("%s\n", __func__);}
b.c
#include <stdio.h>
#include "abmain.h"
void fun3(void){printf("%s (%d)\n", __func__, ++parameter);}
main.c
#include <stdio.h>
#include "abmain.h"
int parameter = 1;
int main(void){fun1();fun3();fun2();fun3();return 0;}
Compilation and execution
$ gcc -Wall -Wextra -c a.c
$ gcc -Wall -Wextra -c b.c
$ gcc -Wall -Wextra -c main.c
$ ld -r -o tools.o a.o b.o
$ gcc -o abmain main.o tools.o
$ ./abmain
fun1
fun3 (2)
fun2
fun3 (3)
$
Proved on Mac OS X 10.11.6 with GCC 6.1.0 (and the XCode 7.3.0 loader, etc). However, the -r option has been in the ld command on mainstream Unix since at least the 7th Edition Unix (circa 1978), so it is likely to be available with most Unix-based compilation systems, even if it is one of the more widely unused options.

How to include helper functions in C?

There are 4 files:
helper.h //contains the signatures of functions in helper.c
helper.c //implements the signatures in helper.h
file.h //has all the includes needed to run file.h
file.c //this file includes file.h and helper.h
In file.c, I need to use the function that is defined in helper.c in my main function. However, file.c is saying that there is an undefined reference to 'func_found_in_helper.c'
Is this structure correct?
Yes, provided file.c contains
#include "helper.h"
and when building your program you link together helper.o and file.o.
You also need to ensure you compile each of the files with -c so that the compiler only compiles (and not links); do the link later with all the object files.
Here's a working example (I don't actually need a main.h but if you have one of those, #include it from main.c):
main.c
#include <stdio.h>
#include <stdlib.h>
#include "helper.h"
int
main (int argc, char **argv)
{
test ();
exit (0);
}
helper.c
#include <stdio.h>
void
test ()
{
printf ("Hello world\n");
}
helper.h
void test ();
To compile
gcc -Wall -Werror -c -o main.o main.c
gcc -Wall -Werror -c -o helper.o helper.c
To link
gcc -Wall -Werror -o test main.o helper.o
In a Makefile
test: main.o helper.o
gcc -Wall -Werror -o test main.o helper.o
%.o: %.c
gcc -c -Wall -Werror -o $# $<
clean:
rm -f *.o test
To run
$ ./test
Hello world
It's a bit difficult to tell what else might be wrong without the program; my guess is you simply forgot the -c flag to gcc, or forgot to link in helper.o.
undefined reference to 'func_found_in_helper.c'
That's a little odd, as it suggests you have tried to call the function using the '.c' extension, rather than just the function name. Maybe the '.' is just a typo in the question ?
Also a linker will flag an undefined symbol, so it may also be that you have not told the linker where to find helper.o ( the helper.c file compiled to the an object file ). The compiler will start the linker automatically. Did you compile helper.c first ?

Resources