I am trying to understand how the inline keyword works with pointers to variables.
Consider the following example:
struct S
{
float a;
float b;
};
inline void foo (struct S *s)
{
s->a = 5;
}
void main()
{
struct S ss;
foo(&ss);
}
When the compiler inlines the function foo, will it generate
void main()
{
struct S ss;
(&ss)->a = 5;
}
Or will it generate
void main()
{
struct S ss;
ss.a = 5;
}
In other words, will the code need to de-reference the pointer to the structure or will it understand that it needs to replace by just the structure?
In an embedded application, this would make a difference in runtime that could be significant.
The compiler is required only to generate code that achieves the result required by the semantics of the language. How a specific compiler achieves that is entirely implementation dependent. It is even possible that the code will not be in-lined at all.
To determine how your particular compiler will translate this code, you can either instruct it to output an assembly listing of the generated code or inspect the code disassembly in a debugger. The code generated may also be very different depending upon compiler options such as optimisation level.
Related
Given I'll return a large struct in a function like here:
#include <stdio.h>
// this is a large struct
struct my_struct {
int x[64];
int y[64];
int z[64];
};
struct my_struct get_my_struct_from_file(const char *filename) {
int tmp1, tmp2; // some tmp. variables
struct my_struct u;
// ... load values from filename ...
return u;
}
int main() {
struct my_struct res = get_my_struct_from_file("tmp.txt"); // <-- here
printf("x[0] = %d\n", res.x[0]);
// ... print all values ...
}
At the place marked by here, do I have to assume that this large struct is copied or is it likely that the compiler does something to avoid this?
Thank you
… do I have to assume that this large struct is copied…
No, of course you do not have to make that assumption. Nobody requires you to make that assumption, and it would be unwise to adopt the statement as an assumption rather than deriving it from known information, such as compiler documentation or inspection of the generated assembly code.
In the specific code you show, it is likely good compilers will optimize so that the structure is not copied. (Testing with Apple Clang 11 confirms it does this optimization.) But that is likely overly simplified code. If a call to get_my_struct_from_file appears in a translation unit separate from its definition, the compiler will not know what get_my_struct_from_file is accessing. If the destination object, res in this example, has had its address previously passed to some other routine in some other translation unit, then the compiler cannot know that other routine did not stash the address somewhere and that get_my_struct_from_file is not using it. So the compiler would have to treat the structure returned by get_my_struct_from_file and the structure the return value is being assigned to as separate; it could not coalesce them to avoid the copy.
To ensure the compiler does what you want, simply tell it what you want it to do. Write the code so that the function puts the results directly in the structure you want to put it in:
void get_my_struct_from_file(struct my_struct *result, const char *filename)
{
…
}
...
get_my_struct_from_file(&res, "tmp.txt");
At the place marked by here, do I have to assume that this large struct is copied or is it likely that the compiler does something to avoid this?
Semantically, the structure is copied from the function's local variable to the caller's variable. These are distinct objects, and just like objects of other types, setting one structure equal to another requires copying from the representation of one to the representation of the other.
The only way to avoid a copy would be for the compiler to treat the local variable as an alias for the caller's structure, but that would be wrong in the general case. Such aliasing can easily produce observably different behavior than would occur without.
It is possible that in some specific cases, the compiler can indeed avoid the copy, but if you want to ensure that no copying happens then you should set up the wanted aliasing explicitly:
void get_my_struct_from_file(const char *filename, struct my_struct *u) {
int tmp1, tmp2; // some tmp. variables
// ... load values from filename into *u
}
int main() {
struct my_struct res = { 0 };
get_my_struct_from_file("tmp.txt", &res);
printf("x[0] = %d\n", res.x[0]);
// ... print all values ...
}
Suppose I have these two structs coming from different header files:
header_1.h
struct main_node {
struct *sec_node
}
header_2.h
struct sec_node {
int var;
}
Now I am using this both header files in main.c and the code looks something like this:
#include <stdio.h>
#include "header_1.h"
#include "header_2.h"
struct main_node *node;
void main()
{
for (int i = 0; i < 1000; i++)
printf( "%d\n", node->sec_node->var) ;
}
Let's assume, I am not using a modern optimizing compiler. I'm looping over this struct many times, would it be faster/good practice to use a temp variable here?
Is there any difference performance-wise in C?
void main()
{
int temp = node->sec_node->var;
for (int i = 0; i < 1000; i++)
printf( "%d\n", temp);
}
It's not bad, but it can be a source of optimization bottleneck. Because the compiler cannot see the definitions of external functions (like printf here, although it might know about its properties as a builtin because it's a standard function), it must assume any external function could modify any non-const object whose address it could see. As such, in your example, node or node->sec_node may have a different value before and after the call to the external function.
One way to mitigate this is with temps like you're doing, but you can also make use of the restrict keyword as a promise to the compiler that, during the lifetime of the restrict-qualified pointer, the pointed-to object will not be accessed except via pointers "based on" the restrict-qualified one. How to do this is probably outside the scope of this question.
Example:
struct dummy
{
int var;
};
Why structures like this are used? Mostly I have seen them in some header files.
The atomic_t type is also defined like this. Can't it be defined simply using:
typedef int atomic_t;
It's more extensible.
Assume that in the future, you realize that struct dummy should contain a name field, then you can change the definition of it to:
struct dummy
{
int var;
char name[30];
};
without changing much of your application code.
Besides extensibility, this idiom also makes it syntactically impossible to do normal arithmetic on types whose meaning is such that that doesn’t make sense semantically.
E.g.:
typedef uint32_t myObject;
myObject x, y;
...
y = x + 3; // meaningless, but doesn’t produce an error.
// may later cause runtime failure.
v.s.
typedef struct { uint32_t var; } myObject;
myObject x, y;
...
y = x + 3; // syntax error.
This may seem contrived, but it is occasionally very useful.
One other use is to pass entire arrays to functions.
struct s {
int a[3];
};
void f1(int a[]) // this evaluates to a pointer, same as void f1(int *a)
{
printf("%d\n", sizeof(a));
}
void f2(struct s *obj)
{
printf("%d\n", sizeof(obj->a));
}
int main(int argc, char **argv)
{
int a[3] = {1, 2, 3};
struct s obj;
obj.a[0] = 1;
obj.a[1] = 2;
obj.a[2] = 3;
f1(a);
f2(&obj);
return 0;
}
// output
// 8
// 12
Not all things that are representable in 32 bits should be treated as numbers. Even things that have a numeric value may have semantics which would suggest that they need special treatment. Suppose, for example, that a processor has an "atomic increment" instruction but it's slower than a "normal" increment instruction. If one wants to atomically increment fnord in one place and decrement it in another, one could use:
volatile int fnord;
...
atomic_inc(&fnord);
...
atomic_dec(&fnord);
A problem with that, however, is that if one of the places that is supposed to increment fnord happens to use fnord++ rather than atomic_inc(&fnord);, the compiler will perfectly happily generate a "normal" increment instruction, and the code may work most of the time, but it could fail in hard-to-track down fashion.
Replacing the int with a structure (and defining atomic_inc inline functions to work with it) would prevent erroneous code like fnord++; from compiling. It wouldn't guard against fnord.var++; but would give a programmer a chance to examine the structure and see what the right way to increment it would be.
Mostly it is to keep compatibility, as maybe earlier the structure had additional elements.
Or as it could be intended to add other elements later.
(or even an itnernal version of the structure has more than just one member (what I realy could imagine for atomic_t-type.)
What is the best way to accomplish the following in C?
#include <stdio.h>
struct A
{
int x;
};
struct A createA(int x)
{
struct A a;
a.x = x;
return a;
}
struct A a = createA(42);
int main(int argc, char** argv)
{
printf("%d\n", a.x);
return 0;
}
When I try to compile the above code, the compiler reports the following error:
"initializer element is not constant"
The bad line is this one:
struct A a = createA(42);
Can someone explain what is wrong? I'm not very experienced in C. Thanks!
struct A a = { .x = 42 };
More members:
struct Y {
int r;
int s;
int t;
};
struct Y y = { .r = 1, .s = 2, .t = 3 };
You could also do
struct Y y = { 1, 2, 3 };
The same thing works for unions, and you don't have to include all of the members or even put them in the correct order.
Why not use static initialization?
struct A a = { 42 };
The problem here is that global / file static variables in C must have a value known at compile time. This means you can't use a user defined function to initialize the value. It must be a constant expression
For curious people who also use MSVC:
In C it is possible to run initialization functions before main just as it is possible in C++ (of course it is, how would C++ do it if it wasn't possible in C), however it may be somewhat confusing if you haven't read how does your runtime library work.
Long story short:
#pragma section(".CRT$XIU",long,read)
int
init_func ()
{
// initialization
return 0; // return 0 is mandatory
}
__declspec(allocate(".CRT$XIU"))
int (*global_initializer)() = init_func;
So it's not as compact source text as in C++, but it can be done. Also, before using I recommend to understand PE format first, then read crt\src\crt0.c and crt\src\crt0dat.c (search for _cinit in both files) in your MSVC installation directory so you know what is going on.
You cannot invoke functions in static initialization like that. In your example, you can simply use:
struct A a = {42};
If you have a more complicated setup, you will need to provide a library construction and library destruction function that you force users of your library to call (assuming you want to be portable), or you will have to use C++ and take advantage of constructors/destructors, or you will have to take advantage of the non-standard and non-portable __attribute__((constructor)) to create a function that is run on startup to initialize it.
If you have more complicated setup, I would strongly advocate that you use C++:
class A
{
A(){
// can do initialization in the constructor
}
// ...
};
A a;
However, if you need to stick with pure C, the portable thing to do is use something like:
typedef void* mylibrary_attr_t;
typedef void* mylibrary_t;
#ifdef __cplusplus
# define EXTERNC extern "C"
#else
# define EXTERNC
#endif
EXTERNC int mylibrary_attr_init(mylibrary_attr_t*);
EXTERNC int mylibrary_attr_setparam1(mylibrary_attr_t,int);
EXTERNC int mylibrary_attr_setparam2(mylibrary_attr_t,double);
// .. more functions for various attributes used by library
EXTERNC void mylibrary_attr_destroy(mylibrary_attr_t*);
EXTERNC int mylibrary_init(mylibrary_t*,mylibrary_attr_t);
EXTERNC void mylibrary_destroy(mylibrary_t*);
// functions that use mylibrary_t
// ...
Basically, in the above, you would initialize your library with mylibrary_init and teardown your library using mylibrary_destroy. The functions using your library would require an initialized instance of mylibrary_t, and so the person who created the main function would be responsible for invoking mylibrary_init. It is also good to make the initialization function dependent on an "attributes" parameter that can be replaced with 0 or NULL as a default. That way, if you extend your library and need to accept configuration options, it is available to you. That's more a design than technical approach, though.
I've seen the concept of 'opaque types' thrown around a bit but I really haven't found a succinct answer as to what defines an opaque type in C and more importantly what problems they allow us to solve with their existence. Thanks
It is the most generally used for library purpose. The main principe behind Opaque type in c is to use data though its pointer in order to hide data handling implementation. Since the implementation is hidden, you can modify the library without recompiling any program which depend on it (if the interface is respected)
eg:
version 1:
// header file
struct s;
int s_init(struct s **x);
int s_f(struct s *x);
int s_g(struct s *x);
// source file
struct s { int x; }
int s_init(struct s **x) { *x = malloc(...); }
int s_f(..) { ... }
int s_g(..) { ... }
version 2
// header file
struct s;
int s_init(struct s **x);
int s_f(struct s *x);
int s_g(struct s *x);
// source file
struct s { int y; int x; }
int s_init(struct s **x) { *x = malloc(...); }
int s_f(..) { ... }
int s_g(..) { ... }
From your program side, nothing changed! and as said previously, no need to recompile every single program which rely on it.
In my understanding, opaque types are those which allow you to hold a handle (i.e., a pointer) to an structure, but not modify or view its contents directly (if you are allowed to at all, you do so through helper functions which understand the internal structure).
Opaque types are, in part, a way to make C more object-oriented. They allow encapsulation, so that the internal details of a type can change--or be implemented differently in different platforms/situations--without the code that uses it having to change.
An opaque type is a type which is exposed in APIs via a pointer but never concretely defined.