So, I've got this code:
uint8_t* pbytes = pkt->iov[0].iov_base;
that creates a pointer to the start of an ethernet packet in a structure.
And I ask my friend to look at the code, and he says: "You're not modifying that, and it would be really confusing if you did, so make it const".
And this seems like a good idea, so:
const uint8_t* pbytes = pkt->iov[0].iov_base;
or even:
const uint8_t * const pbytes = pkt->iov[0].iov_base;
And now I am thinking, I bet there are loads of other places where I could have done that, and I bet the compiler is going to be better at finding them than me.
Any ideas how I ask it the question? (gcc preferred, but no problems using a different compiler or a linting tool as long as they will run on unix).
GCC has a flag to suggest beneficial attributes like const
-Wsuggest-attribute=[pure|const|noreturn|format] Warn for cases where adding an attribute may be beneficial. The attributes currently
supported are listed below.
-Wsuggest-attribute=pure
-Wsuggest-attribute=const
-Wsuggest-attribute=noreturn
Warn about functions that might be candidates for attributes pure, const or noreturn. The compiler only
warns for functions visible in other compilation units or (in the case
of pure and const) if it cannot prove that the function returns
normally. A function returns normally if it doesn’t contain an
infinite loop or return abnormally by throwing, calling abort or
trapping. This analysis requires option -fipa-pure-const, which is
enabled by default at -O and higher. Higher optimization levels
improve the accuracy of the analysis.
Src: http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
const propagates. In fact it often becomes a problem and is called "const poisoning". The issue is a function like strchr() which could be called with a const pointer or with a variable one. But if it returns a const *, the string cannot be modified through the pointer, which is often what you want to do.
But if you just make your constant data const at the point immediately after you read it in / initialise it, the compiler will star throwing errors at you every time you try to pass to a non-const qualified context.
Related
This question has a comment by #ideasman42 that says:
Not sure its worth another question, but interested to know why you
might write void func(int volatile arg);
I thought it was worth a question, but didn't see one, so here it is. What, if any, is the effect of doing that?
What sparked it for me, was writing a slightly different set of wrapper functions than what the Raspberry Pi Pico SDK provides. Specifically:
io_rw_32 _i2s_get_hw_clkdiv(struct _i2s* i2s)
{
return i2s->pio_ch->sm[i2s->pio_sm].clkdiv;
}
void _i2s_set_hw_clkdiv(struct _i2s* i2s, io_rw_32 clkdiv)
{
i2s->pio_ch->sm[i2s->pio_sm].clkdiv = clkdiv;
}
A header file buried deep in the SDK says typedef volatile uint32_t io_rw_32;.
Sure, I could do
uint32_t _i2s_get_hw_clkdiv(struct _i2s* i2s)
{
return i2s->pio_ch->sm[i2s->pio_sm].clkdiv;
}
void _i2s_set_hw_clkdiv(struct _i2s* i2s, uint32_t clkdiv)
{
i2s->pio_ch->sm[i2s->pio_sm].clkdiv = clkdiv;
}
instead, and (probably?) generate the exact same code, but if for some unforeseen reason that typedef changes, I'd rather have as few datatype conversions as possible.
What, if any, is the effect of doing that?
It tells the compiler the value of the parameter must be reloaded each time it is used (or, if the source code modifies the parameter, the new value must be written each time).
There is almost never a reason to do that. With const, it can be useful to declare a function parameter as const if you intend never to modify it. If you declare it const and modify it due to a typo where you intended some other name, the compiler will warn you. However, volatile is normally used for accessing special hardware, but function parameters are managed by the compiler and should not need volatile. One time when you might want to use volatile with a function parameter is when you are debugging and want to be able to change the parameter from the debugger. Then using volatile will ensure the generated code gets the new value each time it is used in the source code. Since this would only be for debugging, it should not be used in deployed code.
… generate the exact same code…
No, it does not. With a plain uint32_t parameter, Clang, with optimization enabled, does not reload the parameter from memory when it is used multiple times. When preparing for the second function call in this code:
void foo0(uint32_t clkdiv)
{
bar(clkdiv);
bar(clkdiv);
}
Clang copies the value of clkdiv from a register where it has cached it. In contrast, in this code:
void foo1(io_rw_32 clkdiv)
{
bar(clkdiv);
bar(clkdiv);
}
Clang reloads the parameter from memory. Interestingly, it is not memory where the parameter was passed to the function but rather is memory in the stack frame of the called function. The argument was passed to the function in a register, and the function stored it in memory to create the parameter. (A parameter is an object that is initialized with the value of an argument; it is not the argument itself.)
It is a mistake for the code to be using a volatile type as a parameter, as it results in inefficient code generation.
I have been confusing with const in the C language. I think if no const conception, all the programme existed in the world also could run smoothly.so Can anyone help me and give me some explanation.
Yes, if you removed const, most1 C code would continue to work as it does today.
However, const really exists to save the programmer from himself. The const modifier on a function parameter says: "this function promises to not modify the object this points to." So if you forget, and try to modify the object, the compiler will stop you.
1 - I say "most" because const can also have a runtime effect. Statically-allocated objects marked const will typically be placed in a read-only section in the program image, and this data is marked as read-only when the program is loaded. So if you were to cast away the const and try to write to this object, your program will segfault. The crash here is desired. Because it prevents your program from running with invalid assumptions about its data.
No, is not redundant at all. The qualifier const is usually applied to the declaration of any variable to specify that its value will not be changed during the program execution (Which depends upon where const variables are stored, we may change value of const variable by using pointer). If you have ever programmed in Java, is similar to an static variable.
You can read more about const here: http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html
const will make the assembly code into an absolute number value. It will not be a register or memory value and can't be modified by the code of course. It makes for fast program operation because it doesn't load the value from memory or a register.
However, most optimized coding already does this in assembly when it realizes the value isn't changed.
John
Before someone instantly marks this as a duplicate, let me say that I have spent a few hours searching for an answer to this (and read many similar S/O questions)
Here's the situation: I'm playing around with _Generic and trying to implement a dictionary structure which auto-casts upon retrieval. Let me explain (if you don't care, skip ahead to the bold header). From what I see, the go-to way to have a dictionary structure in which all values belong to the same field involves void pointers, which require the user to cast upon retrieval; here is an example:
struct C99_Dict *d = new_C99_Dict(); /* Creates a pointer to an empty dict
d: {} */
int i = 7;
put_in_c99_dict(d,"key",i); /* d: {"key": (void *)&i} */
...
// BAD: Will cast to i's address
int j = get_from_c99_dict(d,"key");
// BAD: Dereferencing void pointer
int k = *(get_from_c99_dict(d,"key"));
// GOOD: Will set l equal to 7
int l = *(int *)get_from_c99_dict(d,"key");
As one might imagine, after a while (especially once struct *s get thrown into the mix...although that's unchanged in my current project) your code ends up looking like something out of a Lisp textbook.
Using _Generic, however, I have managed to figure out a way to make an easier-to-use dictionary which auto-casts in such a fashion that the line
int j = get_from_c11_dict(d,"key");
becomes perfectly valid and works as one would expect (for builtins...structs still require manual casting). Changing the behavior of put_in_c11_dict based on the input type is easy enough, for the _Generic keyword does all of the heavy lifting. What is difficuly, however, is the notion of casting on the way out. This is because, in order for the dictionary struct to be well-defined, its value must be a consistent type (e.g. void*, as I have implemented). The problem with this, however, is that the type information is lost after the insertion function has processed the given input.
My initial (failed) attempt at a workaround for this was to make a dictionary struct of the following form:
typedef struct _dict_mem_struct{
union {
_Bool(*bool_get)(_dict_mem_struct*);
char(*char_get)(_dict_mem_struct*);
...
char *(*char_point_get)(_dict_mem_struct*);
void *(*void_point_get)(_dict_mem_struct*);
} get;
void *value;
} _dict_mem_t;
In hopes of (albeit perhaps foolishly so) being able to do the following in a _get helper macro definition:
#define _get(mem_struct) _Generic((mem_struct.get) ... )
Unfortunately, I then learned from gdb that mem_struct.get was of type union, so it was back to the drawing board. Eventually, I got something that worked. First, I added a char* field to the member structure which contained the original type. Then what I really needed was an inlined switch statement, since I had no prior indication of what the function signature would be. So, here's the hideous thing I did (is it technically invalid C? Maybe. Probably. I don't know. Nevertheless, GCC compiles it and it works, so I'm happy.)
#define IS_PFX(val,pfx) (!strcmp(val->pfx, pfx))
#define _get(valstruct) (IS_PFX(valstruct,"bool") ? boolval(valstruct) : IS_PFX(valstruct,"char") ? charval(valstruct) : ... : valstruct)
Yeah, I know; I'm probably going to hell for this. So, with that...
Here's my actual problem: When I compile this, it works, but gcc gets extremely upset with me. It gives me a bunch of errors such as
dict.c:203:75: warning: pointer type mismatch in conditional expression
#define PAIR(valstruct,str,fn,rst) (IS_PFX(valstruct,str) ? fn(valstruct) : rst)
From what I can gather, this means gcc is upset that these functions are all of different types. Nevertheless, as previously stated, the code works, so I would like to tell gcc to put a sock in it for specifically those warnings. The problem is that when I run gcc -fdiagnostics-show-option, those warning lines have no -W... flag after them. Furthermore, I have read through the gcc warning flag page, and nothing stands out as obvious to me which I could use to suppress these. Finally, the warnings still do not go away after adding the lines #pragma GCC diagnostic ignored "-Wall" and #pragma GCC diagnostic ignored "-Wextra". How can I get rid of these? Another solution I would be fine with is somehow turning off all warnings for specifically that file, since the reason I don't want them is so that I can integrate this into other projects without headache.
Thanks for any and all assistance. By the way, if there is some better way to do all of this, then please let me know. Regardless, I'm thinking I'll make a git repo for this once I've worked some of these kinks out, since I think it would be useful (if so, I'll update this post with a link).
gcc is probably right, you are mixing different pointer types in a ternary expression. So your design is most probably wrong. Have in mind that _Generic can't do miracles, the type system in C remains static, determined at compile time. It can only take care of type information that you pass to it in the first expression.
If you cast away type information to void*, store the pointer somewhere and try to retrieve it later, by definition _Generic can't help you. The context of the retrieval doesn't have the type information anymore, it might be called for such pointers that come from different places.
So in particular for a dictionary structure C can never know at the retrieval side, what type the original pointer had been. If you want to keep that information, you'd have to do that yourself and store that information along with the pointer.
BTW, already your question title is wrong: there is no such thing as a generic function in C. There are type generic function-like macros.
I know that using the const keyword on function arguments provides better performance, but I always forget to add it. Is the compiler (GCC in this case) smart enough to notice that the variabele never changes during the function, and compile it as if I would have added const explicitly?
You have a common misunderstanding about const. Only making an object const means that its value never changes, and then it's not just during a function, it never changes.
Making a parameter to a function const does not mean its value never changes, it just means that function cannot change the value through that const pointer. The value can change other ways.
For example, look at this function:
void f(const int* x, int* y)
{
cout << "x = " << *x << endl;
*y = 5;
cout << "x = " << *x << endl;
}
Notice that it takes a const pointer to x. However, what if you call it like this:
int x = 10;
f(&x, &x);
Now, f has a const pointer, but it's to a non-const object. So the value can change, and it does since y is a non-const pointer to the same object. All of this is perfectly legal code. There's no funny business here.
So there's really no answer to your question since it's based entirely on false premises.
Is the compiler (GCC in this case) smart enough to notice that the
variabele never changes during the function, and compile it as if I
would have added const explicitly?
Not necessarily. For example:
void some_function(int *ptr); // defined in another translation unit
int foo(int a) {
some_function(&a);
return a + 1;
}
The compiler can't see what some_function does, so it can't assume that it won't modify a.
Link-time optimization could perhaps see what some_function really does and act accordingly, but as far as this answer is concerned I'll consider only optimization for which the definition of some_function is unavailable.
int bar(const int a) {
some_function((int*)&a);
return a + 1;
}
The compiler can't see what some_function does, but it can assume that the value of a does not change anyway. Therefore it can make any optimizations that apply: maybe it can keep a in a callee-saves register across the call to some_function; maybe it computes the return value before making the call instead of after, and zaps a. The program has undefined behavior if some_function modifies a, and so from the compiler's POV once that happens it doesn't matter whether it uses the "right" or "wrong" value for a.
So, by in this example by marking a const you have told the compiler something that it cannot otherwise know -- that some_function will not modify *ptr. Or anyway that if it does modify it, then you don't care what your program's behavior is.
int baz(int a) {
some_function(NULL);
return a + 1;
}
Here the compiler can see all relevant code as far as the standard is concerned. It doesn't know what some_function does, but it does know that it doesn't have any standard means to access a. So it should make no difference whether a is marked const or not because the compiler knows it doesn't change anyway.
Debugger support can complicate this situation, of course -- I don't know how things stand with gcc and gdb, but in theory at least if the compiler wants to support you breaking in with the debugger and modifying a manually then it might not treat it as unmodifiable. The same applies to the possibility that some_function uses platform-specific functionality to walk up the stack and mess with a. Platforms don't have to provide such functionality, but if they do then it conflicts with optimization.
I've seen an old version of gcc (3.x, can't remember x) that failed to make certain optimizations where I failed to make a local int variable const, but in my case gcc 4 did make the optimization. Anyway, the case I'm thinking of wasn't a parameter, it was an automatic variable initialized with a constant value.
There's nothing special about a being a parameter in any of what I've said -- it could just as well be any automatic variable defined in the function. Mind you, the only way to for a parameter to get the effect of initialization with a constant value is to call the function with a constant value, and for the compiler to observe the value for that call. This tends to happen only when the function is inlined. So inlined calls to functions can have additional optimizations applied to them that the "out-of-line" function body isn't eligible for.
const, much like inline, is only a hint for a compiler and does not guarantee any performance gains. The more important task of const is to protect programmers from themselves so they do not unwilling modify variables where they shouldn’t be modified.
1) Really const is not affecting your performance directly. It may in some cases make simpler points-to analysis (so prefer const char* to char*), but const is more about semantics and readability of your code.
2) CV-qualified type forms different type in C and C++. So your compiler, even if it sees profit from making default const, will not do it, because it will change type and may lead to surprisingly odd things.
As part of the optimization the compiler is taking a deep look at when memory locations are read or written. So the compiler is quite good at detecting when a variable is not changed (const) and when it is changed. The optimizer does not need you to tell him when a variable is const.
Nevertheless you should always use const when appropriate. Why? Because it makes interfaces more clear and easier to understand. And it helps detect bugs when you are changing a variable that you did not want to change.
Example available at ideone.com:
int passByConstPointerConst(MyStruct const * const myStruct)
int passByValueConst (MyStruct const myStruct)
Would you expect a compiler to optimize the two functions above such that neither one would actually copy the contents of the passed MyStruct?
I do understand that many optimization questions are specific to individual compilers and optimization settings, but I can't be designing for a single compiler. Instead, I would like to have a general expectation as to whether or not I need to be passing pointers to avoid copying. It just seems like using const and allowing the compiler to handle the optimization (after I configure it) should be a better choice and would result in more legible and less error prone code.
In the case of the example at ideone.com, the compiler clearly is still copying the data to a new location.
In the first case (passing a const pointer to const) no copying occurs.
In the second case, copying does occur and I would not expect that to be optimized out if for no other reason because the address of the object is taken and then passed through an ellipsis into a function and from the point of view of the compiler, who knows what the function does with that pointer?
More generally speaking, I don't think changing call-by-value into call-by-reference is something compilers do. If you want copy by reference, implement it yourself.
Is it theoretically possible that a compiler could detect that it could just convert the function to be pass-by-reference? Yes; nothing in the C standard says it cannot..
Why are you worrying about this? If you are concerned about performance, has profiling shown copy-by-value to be a significant bottleneck in your software?
This topic is addressed by the comp.lang.c FAQ:
http://c-faq.com/struct/passret.html
When large structures are passed by value, this is commonly optimized by actually passing the address of the object rather than a copy. The callee then determines whether a copy needs to be made, or whether it can simply work with the original object.
The const qualifier on the parameter makes no difference. It is not part of the type; it is simply ignored. That is to say, these two function declarations are equivalent:
int foo(int);
int foo(const int);
It's possible for the declaration to omit the const, but for the definition to have it and vice versa. The optimization of the call cannot hinge on this const in the declaration. That const is not what creates the semantics that the object is passed by value and hence the original cannot be modified.
The optimization has to preserve the semantics; it has to look as if a copy of the object was really passed.
There are two ways you can tell that a copy was not passed: one is that a modification to the apparent copy affects the original. The other way is to compare addresses. For instance:
int compare(struct foo *ptr, struct foo copy);
Inside compare we can take the address of copy and see whether it is equal to ptr. If the optimization takes place even though we have done this, then it reveals itself to us.
The second declaration is actually a direct request by the user to receive a copy of the passed struct.
const modifier eliminates the possibility of any modifications made to the local copy, however, it is does not eliminate all the reasons for copying.
Firstly, the copy has to maintain its address identity, meaning that inside the second function the &myStruct expression should produce a value different from the address of any other MyStruct object. A smart compiler can, of course, detect the situations that depend on the address identity of the object.
Secondly, aliasing presents another problem. Imagine that the program has a global pointer MyStruct *global_struct and inside the second function someone modifies the *global_struct. There's a possibility that the *global_struct is the same struct object that was passed to the function as an argument. If no copy was made, the modifications made to *global_struct will be visible through the local parameter, which is a disaster. Aliasing issues are much more difficult (and in general case impossible) to resolve at compilation time, which is why compilers usually won't be able to optimize out the copying.
So, I would expect any compiler to perform the copying, as requested.