I have 3 files in my directory:
ex22.h, ex22.c. ex22_main.c
ex22.h:
#ifndef _ex22_h
#define _ex22_h
extern double* v;
...
#endif
ex22.c:
#include <stdio.h>
#include "ex22.h"
double* v;
/*a function that accepts a new_variable, changes a
static variable inside of it while keeping track of
what the static variable was before the change. The
purpose of the function is to update the static
variable and print what it was right before the
updating.*/
double update_variable(double new_variable)
{
static double variable = 1.0;
double old_variable = variable;
variable = new_variable;
v = &variable;
return old_variable;
}
...
ex22_main.c:
#include "ex22.h"
#include "dbg.h"
...
int main(void)
{
//test if it is possible
printf("Variable at first: %f", update_variable(2.0);
printf("Access 'variable' inside update_variable: %f", *v);
}
Compiller (ubuntu) gives me those error messages:
cc ex22_main.c -o ex22_main
/tmp/ccGLFiXP.o: In function `main':
...
ex22_main.c:(.text+0x1f4): undefined reference to `update_variable'
ex22_main.c:(.text+0x222): undefined reference to `r'
collect2: error: ld returned 1 exit status
<builtin>: recipe for target 'ex22_main' failed
make: *** [ex22_main] Error 1
Hope you understood what I'm trying to achieve. My goal is to access the static variable inside a function (which is not possible) by making a pointer to it. I'm just curious if it works that way?
EDIT:
There were some stupid bugs in my code but the idea of accessing a static variable inside a function by it's pointer is completely feasible.
Avoid my mistakes:
1) Make sure files are linked
2) Watch out for variable names
My goal is to access the static variable inside a function (which is not possible) by making a pointer to it. I'm just curious if it works that way?
Yes, it does. static has a different meaning for variables declared inside functions than for those declared at file scope, but but both varieties exist and have the same address for the entire lifetime of the program. A pointer to such a variable can be used to access its value anywhere in the program, without regard to whether the variable can be accessed via its declared identifier at the point of access-via-pointer.
Your compilation issue is unrelated. Your other answer explains the nature of that problem.
The compiler, or more precisely the linker, can't find the definitions of update_variable or r because you didn't compile and link ex22.c which contains those definitions.
You need to compile both files and link them together.
cc -o ex22_main ex22_main.c ex22.c
Related
I am using library that I shouldn't change it files, that including my h file.
the code of the library looks somthing like like:
#include "my_file"
extern void (*some_func)();
void foo()
{
(some_func)();
}
my problem is that I want that some_func will be extern function and not extern pointer to function (I am implementing and linking some_func). and that how main will call it.
that way I will save little run time and code space, and no one in mistake will change this global.
is it possible?
I thought about adding in my_file.h somthing as
#define *some_func some_func
but it won't compile because asterisk is not allowed in #define.
EDIT
The file is not compiled already, so changes at my_file.h will effect the compilation.
First of all, you say that you can't change the source of the library. Well, this is bad, and some "betrayal" is necessary.
My approach is to let the declaration of the pointer some_func as is, a non-constant writable variable, but to implement it as constant non-writable variable, which will be initialized once for all with the wanted address.
Here comes the minimal, reproducible example.
The library is implemented as you show us:
// lib.c
#include "my_file"
extern void (*some_func)();
void foo()
{
(some_func)();
}
Since you have this include file in the library's source, I provide one. But it is empty.
// my_file
I use a header file that declares the public API of the library. This file still has the writable declaration of the pointer, so that offenders believe they can change it.
// lib.h
extern void (*some_func)();
void foo();
I separated an offending module to try the impossible. It has a header file and an implementation file. In the source the erroneous assignment is marked, already revealing what will happen.
// offender.h
void offend(void);
// offender.c
#include <stdio.h>
#include "lib.h"
#include "offender.h"
static void other_func()
{
puts("other_func");
}
void offend(void)
{
some_func = other_func; // the assignment gives a run-time error
}
The test program consists of this little source. To avoid compiler errors, the declaration has to be attributed as const. Here, where we are including the declarating header file, we can use some preprocessor magic.
// main.c
#include <stdio.h>
#define some_func const some_func
#include "lib.h"
#undef some_func
#include "offender.h"
static void my_func()
{
puts("my_func");
}
void (* const some_func)() = my_func;
int main(void)
{
foo();
offend();
foo();
return 0;
}
The trick is, that the compiler places the pointer variable in the read-only section of the executable. The const attribute is just used by the compiler and is not stored in the intermediate object files, and the linker happily resolves all references. Any write access to the variable will generate a runtime error.
Now all of this is compiled in an executable, I used GCC on Windows. I did not bother to create a separated library, because it doesn't make a difference for the effect.
gcc -Wall -Wextra -g main.c offender.c lib.c -o test.exe
If I run the executable in "cmd", it just prints "my_func". Apparently the second call of foo() is never executed. The ERRORLEVEL is -1073741819, which is 0xC0000005. Looking up this code gives the meaning "STATUS_ACCESS_VIOLATION", on other systems known as "segmentation fault".
Because I deliberately compiled with the debugging flag -g, I can use the debugger to examine more deeply.
d:\tmp\StackOverflow\103> gdb -q test.exe
Reading symbols from test.exe...done.
(gdb) r
Starting program: d:\tmp\StackOverflow\103\test.exe
[New Thread 12696.0x1f00]
[New Thread 12696.0x15d8]
my_func
Thread 1 received signal SIGSEGV, Segmentation fault.
0x00000000004015c9 in offend () at offender.c:16
16 some_func = other_func;
Alright, as I intended, the assignment is blocked. However, the reaction of the system is quite harsh.
Unfortunately we cannot get a compile-time or link-time error. This is because of the design of the library, which is fixed, as you say.
You could look at the ifunc attribute if you are using GCC or related. It should patch a small trampoline at load time. So when calling the function, the trampoline is called with a known static address and then inside the trampoline there is a jump instruction that was patched with the real address. So when running, all jump locations are directly in the code, which should be efficient with the instruction cache. Note that it might even be more efficient than this, but at most as bad as calling the function pointer. Here is how you would implement it:
extern void (*some_func)(void); // defined in the header you do not have control about
void some_func_resolved(void) __attribute__((ifunc("resolve_some_func")));
static void (*resolve_some_func(void)) (void)
{
return some_func;
}
// call some_func_resolved instead now
This question already has answers here:
How do I use extern to share variables between source files?
(19 answers)
Closed 3 years ago.
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!
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.
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.
This question already has answers here:
What is the difference between a definition and a declaration?
(27 answers)
Closed 5 years ago.
I have two .c files, one of them has the definition of x, and the other file is using x, as follows:
file1.c:
int x;
//other code...
main.c:
int main(void)
{
printf("%d", x);
}
Now, when I compile this code, I get the following compilation error message:
In function 'main':
error: 'x' undeclared (first use in this function)
So, if a global variable (in this case x) is 'extern' by default, then why can't main.c file see x?
When I now go to main.c and define x, so that main.c now looks like:
int x=9; //This line was added.
int main(void)
{
printf("%d",x);
}
And also initialize x in file1.c, the program doesn't compile and I get the following error message:
error: ld returned 1 exit status
So, if main.c can't see x that is in file1.c, then this time what is the problem?
Is this a linking error?
Note that when I add
extern int x;
in main.c, the problem disappears.
Each compilation unit (in this case your individual .c files) is compiled separately. The compiler needs to know the storage class of x in order to handle it, so your first error (undeclared) comes from the compiler not knowing what x is. The compiler does not need to know where x lives.
When you then link your compiled objects together, the linker resolves any external names (including x in main.c if you've marked it extern) and the final executable will then have all its variables in known places. If it finds 2 extern symbols with the same name, then it will fail, giving you your second error (error: ld returned 1 exit status).
You must declare you variable in main.c, so the compiler knows about it: extern int x. The compiler said it to you: error: 'x' undeclared
You added the second definition of x in main.c, the first definition you did in file1.c. The linker informed you about ambiguity between two definitions. You could read the error above the line error: ld returned 1 exit status
I've been searching through Stackoverflow and the web for the answer and it seems like I'm doing this correctly, however I can't get it to work.
I made a test program for using extern, (it's on xCode using the projects feature if that makes a difference). I'm trying to declare a variable, that I can access anywhere in my program, whenever I change the value of that variable, I want to be able to access the changed value.
What I've done so far is I have a header file
externFile.h
and a
externFile.c
and of course my
main.c
In externFile.h I defined:
extern int ply;
In main.c I have:
int main()
{
int ply;
ply = 5;
printPly();
}
In externFile.c I have:
void printPly(){
printf("%d is ply.\n", ply);
}
However I'm getting this error:
_ply referenced from _printPly
ld symbols not found for architecture x86_64
Does anyone have any sort of ideas about this?
You haven't actually defined ply globally:
int main(void)
{
int ply;
ply = 5;
printPly();
}
Here, ply is a local variable, not a global, since it is defined inside of a function. The extern line in your header file does not define the global, but declares it. It says "this variable exists somewhere", but it doesn't create it. Because there is no global defined, you get an undefined reference error.
Global variables must be defined at file scope, i.e. outside of a function:
int ply = 5;
int main(void)
{
printPly();
}
Consider the following code:
a.c
#include <stdio.h>
#include "b.h"
static int a = 41;
static void test(void){
a++;
printf("a: %d\n", a);
}
int main(void){
set_callback(test);
call();
return 0;
}
b.c
static void (*callback)(void);
void set_callback(void (*func)(void)){
callback = func;
}
void call(void){
if (callback){
callback();
}
}
b.h
void set_callback(void (*func)(void));
void call(void);
This compiles without warnings with -Wall and prints out a: 42 as expected.
Now, this might not be the best practice, since the writer of a.c probably doesn't expect test() to be called from another file and the variable a modified, but is this legal C code? Will it work portably on different platforms and compilers?
Yes, this is perfectly fine code and even good code. There is no need for your test callback to be global.
The compiler is responsible to ensure that the function isn't called outside the translation unit, before doing any optimizations that would affect calling it from outside.
If it sees that a pointer to the function is passed to an external function, it has to refrain from doing incompatible optimizations to the function.
Thus, the only effect is that the object file won't export a test symbol (This is termed Internal Linkage).
You wrote,
Now, this might not be the best practice, since the writer of a.c probably doesn't expect test() to be called from another file and the variable a modified, but is this legal C code?
If the writer did not want his test() function to be called from another file, he should not have passed a pointer to it to an outside module!
When the writer called set_callback(test); he knew he was passing his static method to an outside module, and giving that outside module permission to call it.
The point is that the author is in charge of the test method and where it goes. He's not prevented from doing anything, but he can control where his data goes; and he chose to pass it to an outsider.