What does const in void foo(const int a) do? [duplicate] - c

This question already has answers here:
Const correctness for value parameters
(6 answers)
Closed 6 years ago.
I do understand the meaning of const for pointers or structures that have to be passed by reference to a function. However in the example:
void foo(const int a);
the variable a is passed on the stack. There is no harm to the caller to modify the content of the stack so const looks pretty useless in this situation.
Furthermore, if I cannot modify a, I can still make a copy of a and change this copy:
void foo(const int a)
{
int b = a;
b++;
}
In which situation does the const keyword will be useful when applied to a scalar function argument (not a pointer)?

Code like void foo(const int a) is not really meaningful, nor is it "const correctness" as such.
Sometimes overly pedantic programmers get the weird idea that they need to declare common parameters as const just because a function doesn't modify the actual parameter. But most often, functions do not.
The point is, the variable is a copy of the original, so what your function does with it doesn't matter the slightest!
So this is not even a way of writing self-documenting code, because all it does is to tell the caller what's going on internally inside your function. The caller doesn't care and shouldn't care.

It's unnecessary to use const for a value parameter, but it has its value if what you want is to guarantee the code does what you intend.
Here there's an answer with some example of use cases

It's the same thing as declaring any constant variable
const int size = some_other_variable + 5;
It is a good programming practice to declare variables as const if you know their values will not change. That way from declaration you can avoid that someone accidentally changes their value further down the function.

It means that an implementer of the function cannot change the value of the input parameter a that the function receives by value.
It can lead to increased program stability.
(Personally I dislike the style as I find it too verbose).
If you do adopt this style, then note well that you only need to put the const in the parameter list of the function definition, you don't need it in the declaration.

It's useful mostly for purposes of self-documenting your code.
For future people looking through your source code, the const serves to emphasize your intent (that none of the code in foo() should attempt to modify a).

Related

When is a constant pointer (not a pointer to a constant) in a function prototype required for the code to be correct? [duplicate]

This question already has answers here:
What's the point of const pointers?
(17 answers)
Closed 3 years ago.
While reviewing code, I occasionally see function prototypes with constant pointers like:
void Foo(int * const bar);
Often a programmer using such a function does not understand what he actually sees and the function is later used like this:
const int bla = 0;
Foo((int* const) &bla);
which, as far as I understand, results in undefined behavior. In this case, the constant pointer caused confusion.
My question is: When is a constant pointer (not a pointer to a constant) in a function prototype required for the code to be correct?
The const keyword is just used to avoid mistakes generating compilation errors if the variable is touched and to optimize code making the assumption that the variable does not change. It is never required. It also is of no interest at all to the caller, since C passes all parameters by value.
Both the usages of const, either const int * or int * const, are just a contract between the programmer and the compiler. They are never required.

Why do we not use const more often?

It is common to see the keyword const with pointers :
void function(const char *string)
{
const char *other_string = "some string";
}
But why do not we use const every time we declare a variable that we will not change ? It is rare to see :
void function(const int i)
{
const double j = 1.2;
// i and j will not be changed in this function
}
I am trying to understand why we do not use const more often, is it always a good thing to use it when we can, or not ?
This post seems to answer YES, but in reality this doest not seem to be the case ...
In most cases it no longer makes a difference.
Often enough the compiler will be able to deduce that the variable is actually read only, and treat it as a constant during optimization.
Either way, you usually use const consequently on things like const char *string in order to avoid accidentally modifying the parameter.
Since all parameters are passed by value, this isn't something which can't happen for an integer. Only for pointers the pointer itself is the value.
Actually, const char *string isn't fully constant either yet. Only the chars the pointer is pointing to is, but the pointer as a variable isn't. It would only become when specifying it as const char * const string. Now the string variable is completely constant for the scope of the function.
In the case you give, I would always say
void function(const int i)
in a situation where the role of i is to parameterise the behaviour of the function -- which is almost always. If I change i in the body of the function, I have either made an error, or written something which another person will find hard to understand later.
Intentionally modifying the value of i in the function isn't as "dangerous" as modifying a entity through its pointer -- it won't create side-effects outside the function. I guess that's why most developers don't feel the pressure to define primitive paramters as const. I maintain, however, that it is a sensible thing to do, if one cares about long-term maintenance.
In your second example, the parameter is passed by value, making it const basicly has no meaning. const is most handy when passing parameters by reference.
There is another problem with const known as "const poisoning". Consider the following:
int foo(char * str, int n)
{
...
bar(str);
...
}
If we make str to be const, we must then ensure that bar also takes a const etc. (which can affect multiple files and modules). This is especially painful if you're editing old and inconsistent code (not always the case, but should be considered).

Is it true that (const T v) is never necessary in C?

For example:
void func(const int i);
Here,the const is unnecessary since all parameters are passed by value(including pointers).
Is that true?
All parameters in C are indeed passed by value, which means that the actual argument will not change regardless of whether you include that const or not.
However, that does not mean that const here is "never necessary". Whether it is necessary or unnecessary depends on what you want to achieve.
If you want to prevent any attempts to modify the parameter inside the function, then the const is necessary, of course.
There is a fairly popular (and pretty reasonable) coding guideline that says that function parameters should never be modified inside the function, i.e. that at any point of function's execution all parameters must retain their original values. Under this guideline it would actually make perfect sense to always include that const in all parameter declarations.
const is just used by the precompiler, to notice about errors...
Note that this is perfectly valid:
void foo( const char * bar )
{
char * baz = ( char * )bar;
baz++;
}
So it's never necessary, but it just makes the code more readable, and informs you the pointer should never change...

Why should I declare a C array parameter's size in a function header?

Can anyone enlighten me as to why I should bother to specify the size of a C array argument in a function header? For example:
void foo (int iz[6]) { iz[42] = 43; }
With:
int is[2] = {1,2,3};
we get a useful error. Perhaps it helps with commenting/documentation?
Can anyone enlighten me as to why I should bother to specify the size of a C array argument in a function header? For example:
void foo (const char sz[6]) { sz[42] = 43; }
IMO, you shouldn't. When you try to pass an array to a function, what's really passed is a pointer to the beginning of the array. Since what the function receives will be a pointer, it's better to write it to make that explicit:
void foo(char const *sz)
Then, since it's now clear that the function has been given no clue of the size, add that as a separate parameter:
void foo(char const *sz, size_t size)
The only meaningful reason to do that is for documentation purposes - to tell the future users that functions expect to receive an array of at least that many elements. But even that is a matter of convention - something that you have to agree upon with other users in advance. The language (the compiler) ignores that size anyway. Your function declaration is equivalent to void foo(int iz[]) and to void foo(int *iz).
The only way to make it somewhat meaningful for the compiler is to declare it as
void foo (int iz[static 6])
which acts as a promise to the compiler that the array will have at least 6 elements, meaning that the compiler will be able to optimize that code using that assumption. Moreover, if you really want to adopt the convention mentioned above, it makes more sense to declare array parameter sizes with static specifically, since the language explicitly defines the semantics of this construct.
What you mean by "we get a useful error" is not clear to me. The code
int is[2] = {1,2,3};
is[42] = 42;
does not contain any constraint violations. It produces undefined behavior, but it is not required to produce a diagnostic message during compilation. In other words, no, we don't get any "useful error" from this.
It's a comment. Arrays are demoted to pointers in function parameters. Comments can still be useful however, even if the compiler doesn't read them.
It is a useful comment when you want to tell to client code that it must pass an array of defined size, i.e:
void foo(const char bar[5]);
/* It is expected that foo function receives an array of size 5 */
Yet, documentation doesn't replace in code checks:
void foo(const char bar[5])
{
if (!bar) error();
if (strlen(bar) != 4) error();
/* ... */
}

Should useless type qualifiers on return types be used, for clarity?

Our static analysis tool complains about a "useless type qualifier on return type" when we have prototypes in header files such as:
const int foo();
We defined it this way because the function is returning a constant that will never change, thinking that the API seemed clearer with const in place.
I feel like this is similar to explicitly initializing global variables to zero for clarity, even though the C standard already states that all globals will be initialized to zero if not explicitly initialized. At the end of the day, it really doesn't matter. (But the static analysis tool doesn't complain about that.)
My question is, is there any reason that this could cause a problem? Should we ignore the errors generated by the tool, or should we placate the tool at the possible cost of a less clear and consistent API? (It returns other const char* constants that the tool doesn't have a problem with.)
It's usually better for your code to describe as accurately as possible what's going on. You're getting this warning because the const in const int foo(); is basically meaningless. The API only seems clearer if you don't know what the const keyword means. Don't overload meaning like that; static is bad enough as it is, and there's no reason to add the potential for more confusion.
const char * means something different than const int does, which is why your tool doesn't complain about it. The former is a pointer to a constant string, meaning any code calling the function returning that type shouldn't try to modify the contents of the string (it might be in ROM for example). In the latter case, the system has no way to enforce that you not make changes to the returned int, so the qualifier is meaningless. A closer parallel to the return types would be:
const int foo();
char * const foo2();
which will both cause your static analysis to give the warning - adding a const qualifier to a return value is a meaningless operation. It only makes sense when you have a a reference parameter (or return type), like your const char * example.
In fact, I just made a little test program, and GCC even explicitly warns about this problem:
test.c:6: warning: type qualifiers ignored on function return type
So it's not just your static analysis program that's complaining.
You can use a different technique to illustrate your intent without making the tools unhappy.
#define CONST_RETURN
CONST_RETURN int foo();
You don't have a problem with const char * because that's declaring a pointer to constant chars, not a constant pointer.
Ignoring the const for now, foo() returns a value. You can do
int x = foo();
and assign the value returned by foo() to the variable x, in much the same way you can do
int x = 42;
to assign the value 42 to variable x.
But you cannot change the 42 ... or the value returned by foo(). Saying that the value returned from foo() cannot be changed, by applying the const keyword to the type of foo() accomplishes nothing.
Values cannot be const (or restrict, or volatile). Only objects can have type qualifiers.
Contrast with
const char *foo();
In this case, foo() returns a pointer to an object. The object pointed to by the value returned can be qualified const.
The int is returned by copy. It may be a copy of a const, but when it is assigned to something else, that something by virtue of the fact that it was assignable, cannot by definition be a const.
The keyword const has specific semantics within the language, whereas here you are misusing it as essentially a comment. Rather than adding clarity, it rather suggests a misunderstanding of the language semantics.
const int foo() is very different from const char* foo(). const char* foo() returns an array (usually a string) whose content is not allowed to change. Think about the difference between:
const char* a = "Hello World";
and
const int b = 1;
a is still a variable and can be assigned to other strings that can't change whereas b is not a variable. So
const char* foo();
const char* a = "Hello World\n";
a = foo();
is allowed but
const int bar();
const int b = 0;
b = bar();
is not allowed, even with the const declaration of bar().
Yes. I would advise writing code "explicitly", because it makes it clear to anyone (including yourself) when reading the code what you meant. You are writing code for other programmers to read, not to please the whims of the compiler and static analysis tools!
(However, you do have to be careful that any such "unnecessary code" does not cause different code to be generated!)
Some examples of explicit coding improving readability/maintainability:
I place brackets around portions of arithmetic expressions to explicitly specify what I want to happen. This makes it clear to any reader what I meant, and saves me having to worry about (or make ay mistakes with) precedence rules:
int a = b + c * d / e + f; // Hard to read- need to know precedence
int a = b + ((c * d) / e) + f; // Easy to read- clear explicit calculations
In C++, if you override a virtual function, then in the derived class you can declare it without mentioning "virtual" at all. Anyone reading the code can't tell that it's a virtual function, which can be disastrously misleading! However you can safely use the virtual keyword: virtual int MyFunc() and this makes it clear to anyone reading your class header that this method is virtual. (This "C++ syntax bug" is fixed in C# by requiring the use of the "override" keyword in this case - more proof if anyone needed it that missing out the "unnecessary virtual" is a really bad idea)
These are both clear examples where adding "unnecessary" code will make the code more readable and less prone to bugs.

Resources