Function return warning about const attribute - c

I heavily reduced my real code in order to make it as compact as possible for this posting.
My questions is about the const attribute. When I compile this ...
const char tmp[] = "anything";
int main(int argc, char *argv[]) {
/* my code */
return 0;
}
char *somefunction (char *c) {
extern const char tmp[];
/* my code */
return tmp;
}
... I get:
warning: return discards 'const' qualifier from pointer target type [enabled by default]
The code works fine.
The somefunction didn't touch the string tmp, so why does return generate this warning?
If I leave const away, the warning disappears.

When you mark something as const you're telling the compiler you're not going to modify it.
You're taking a const char * and returning it as a char *. That breaks the promise of const that you're giving to tmp. Code that calls this function is free to modify what it returns because it doesn't return a const pointer.
Change somefunction to return a const char *:
const char *somefunction (char *c) {
...

As you are returning a pointer you are giving the possibility to "edit" your data to the caller of the function. This is in contradiction with what you have done right before:
extern const char tmp[];
that's the declaration of a pointer to something which should remain constant, namely non editable.
The compiler is just warning you about this contradiction.

Related

return discards ‘const’ qualifier from pointer target type

I am sorry if this has been asked previously but i have tried all the solutions available but still getting this error. someone please explain the meaning of this error and also the solution to avoid this.
char *find_char( char const *source,char const *chars ){
char const *result=NULL;
for(int i=0;*(chars+i);i++){
for(int j=0;*(source+j);j++){
if(*(chars+i)==*(source+j)){
result=chars+i;
return result;
}
}
}
return result;
}
when compiling, the following error occurs:
6_1.c: In function ‘find_char’:
6_1.c:8:12: warning: return discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
return result;
^
6_1.c:12:9: warning: return discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
return result;
can constants cannot be returned?
Your title is a bit misleading, and probably you misinterpreted the warning, too:
warning: return discards ‘const’ qualifier from pointer target type
[-Wdiscarded-qualifiers] return result;
The function is declared as non-const, i.e. char *find_char(..., while result is declared as const, i.e. char const *result; Hence, statement return result would mean to return a pointer that is declared as const as being non-const, and thus the warning.
I'd suggest to define your function as const char *find_char(....
Since result is of type char const *, use const in the function signature to match it:
char const *find_char( char const *source,char const *chars )
The return type of the function find_char does not match the type of result. They are char * and char const * respectively.
Change the return type of the function find_char to char const *.
This relates to a well-known shortcoming in the C language, which I will discuss below. Presuming we want find_char to return a char * even though its inputs are const char *, you should use an explicit cast in the return statement:
return (char *) result;
With return result;, there is an implicit conversion from char * to the incompatible type const char *. The compiler warns because this could be a mistake. With the explicit cast, you show the compiler the conversion is intentional, and it is not likely to warn. (If it does, you can turn off that particular warning category.)
Regarding the shortcoming in C, consider the nature of find_char. It is similar to the C library routine strstr, which suffers from the same issue:
The caller may have a char * and want a char * as a result.
We would like the function parameters to be const char * to inform the compiler that the function does not alter the strings it is passed, because this may create some optimization opportunities.
The function returns a pointer into one of its input strings. So it internally has a const char * but must return a char *, so it must convert it.
In other words, we have a char *, and we want to tell the compiler “Ths function will accept a char *, and it will not change the content, but it will return a char * result.” To do this in C, it is necessary to declare the parameter to be const char * but to convert the result pointer to char * before returning it.

C - Function processing a const *

I have a function which search something in a const *. And returns such a const * to the found element. Therefore the function is not changing anything and I actually want to maintain the "const" for readability and reuseability.
But I have another function which calls the function with just a * and then works with the returned value. But in this use-case the value should be changed. But because the returned value is a const * I can not do that.
Would should I adjust in my programming pattern to evade that problem?
const void* search_min_vector_element(const void* vector, size_t length, size_t element_size, int(*cmp_fnc) (const void*, const void*));
This is the first function.
void* min_element = search_min_vector_element((unsigned char*) vector + (i * element_size), length - i, element_size, cmp_fnc);
And that is the call in the second one. vector is void* in this case. And because of that I want to modify it.
Thanks for your help.
You can pick which function to execute based on the type of the argument. Write a _Generic macro to select which types to allow, then call the appropriate function based on that. Example:
#include <stdio.h>
char* print_str (char* str)
{
printf("%s\n", str);
return str;
}
const char* print_str_const (const char* str)
{
printf("const %s\n", str);
return str;
}
#define print(str) \
_Generic((str), \
const char*: print_str_const, \
char*: print_str) (str)
int main(void)
{
const char* cstr = "hello";
char* str = "world";
cstr = print(cstr); // ok
str = print(str); // ok
cstr = print(str); // ok
//str = print(cstr); // compiler error, incorrect assignment
//str = print((void*)str); // compiler error, wrong type
}
In C++ you have a lot of options to deal with this. But since this is C, create another function is the best solution I can currently come up with:
const void* search_min_vector_element_const(const void* vector, size_t length, size_t element_size, int(*cmp_fnc) (const void*, const void*));
void* search_min_vector_element ( void* vector, size_t length, size_t element_size, int(*cmp_fnc) ( void*, void*));
You can also force-cast the result directly:
void* min_element = (void*)search_min_vector_element((unsigned char*) vector + (i * element_size), length - i, element_size, cmp_fnc);
I would really recommend to change the return type (if you can) to not be a constant value. It is really not the search_min_vector_element's business what another function shall do with the result.
If you cannot change the return type, then you will need to:
const void* value = search_min_vector_element(....)
void* second_no_const_value = *value; // copy value of the value pointed by constant pointer.
Since you know for a fact that you hold a non-const pointer to vector, you should simply cast the const pointer you receive:
void* min_element = (void*)search_min_vector_element(...);
Technically you may cast from const char* to char* through an explicit cast without yielding undefined behaviour per se; But if you alter a value that must not be altered (e.g. a string literal), you get undefined behaviour then. So the const-qualifier will not prohibit a change of the underlying data, but when used as qualifier in a function argument or return value, the function prototype pretends that the value is not meant to be altered.
Hence, if you - or a library - provides a function with return type const char*, without knowing the implementation, one shall not assume that the returned value points to memory that may be altered. See the following example illustrating this. Functions validImpl1 and validImpl2 have - apart from the name - the same signature but different implementations. Provided that you pass in a parameter which's content may be altered, casting the return value to char* for the first is OK, but for the second one it yields UB. So unless you know the internals of the function, you should never cast from const char* to char *:
const char* validImpl1(const char* t) {
return t;
}
const char* validImpl2(const char* t) {
return "Hello!";
}
int main() {
char x[100] = "Herbert";
char* t=(char*) validImpl1(x);
*t = 'a'; // OK
printf("%s\n",t);
char* t2=(char*) validImpl2(x);
*t2 = 'a'; // Undefined behaviour here.
printf("%s\n",t2);
}
In your case, if you are the one offering the function and knowing about the implementation details, you could offer two functions, one with const char* and one with char*, where the latter simply calls the former. So the ones who use your functions can rely on that what the function prototypes pretend.

Why does the compiler complain about the assignment?

While compiling the following code, the compiler produces the warning:
assignment discards ‘const’ qualifier from pointer target type
#include<stdio.h>
int main(void)
{
char * cp;
const char *ccp;
cp = ccp;
}
And this code is ok(no warning).Why?
#include<stdio.h>
int main(void)
{
char * cp;
const char *ccp;
ccp = cp;
}
Edit: Then why isn't this ok?
int foo(const char **p)
{
// blah blah blah ...
}
int main(int argc, char **argv)
{
foo(argv);
}
Because adding constness is a "safe" operation (you are restricting what you can do to the pointed object, which is no big deal), while removing constness is not (you promised not to touch the pointed object through that pointer, and now you are trying to take back your promise).
As for the additional question, it's explained in the C-Faq: http://c-faq.com/ansi/constmismatch.html. Simply told, allowing that conversion would allow another kind of "unsafe" behavior:
int give_me_a_string(const char **p)
{
const char *str="asd";
*p=str; // p is a pointer to a const pointer, thus writing
// a in *p is allowed
}
int main()
{
char *p;
give_me_a_string(&ptrs); //< not actually allowed in C
p[5]='a'; // wooops - I'm allowed to edit str, which I promised
// not to touch
}
In the first case, you're taking a pointer to data that must not be modified (const), and assigning it to a pointer that allows modification of it's data. Bad and dangerous.
In the second case, you're taking a non-const pointer and assigning it to a pointer that can cause less trouble than the original. You're not opening yourself up to any harmful, illegal or undefined actions.

C: Illegal conversion between pointer types: pointer to const unsigned char -> pointer to unsigned char

The following code is producing a warning:
const char * mystr = "\r\nHello";
void send_str(char * str);
void main(void){
send_str(mystr);
}
void send_str(char * str){
// send it
}
The error is:
Warning [359] C:\main.c; 5.15 illegal conversion between pointer types
pointer to const unsigned char -> pointer to unsigned char
How can I change the code to compile without warnings? The send_str() function also needs to be able to accept non-const strings.
(I am compiling for the PIC16F77 with the Hi-Tech-C compiler)
Thanks
You need to add a cast, since you're passing constant data to a function that says "I might change this":
send_str((char *) mystr); /* cast away the const */
Of course, if the function does decide to change the data that is in reality supposed to be constant (such as a string literal), you will get undefined behavior.
Perhaps I mis-understood you, though. If send_str() never needs to change its input, but might get called with data that is non-constant in the caller's context, then you should just make the argument const since that just say "I won't change this":
void send_str(const char *str);
This can safely be called with both constant and non-constant data:
char modifiable[32] = "hello";
const char *constant = "world";
send_str(modifiable); /* no warning */
send_str(constant); /* no warning */
change the following lines
void send_str(char * str){
// send it
}
TO
void send_str(const char * str){
// send it
}
your compiler is saying that the const char pointer your sending is being converted to char pointer. changing its value in the function send_str may lead to undefined behaviour.(Most of the cases calling and called function wont be written by the same person , someone else may use your code and call it looking at the prototype which is not right.)

idiomatic C for const double-pointers

I am aware that in C you can't implicitly convert, for instance, char** to const char** (c.f. C-Faq, SO question 1, SO Question 2).
On the other hand, if I see a function declared like so:
void foo(char** ppData);
I must assume the function may change the data passed in.
Therefore, if I am writing a function that will not change the data, it is better, in my opinion, to declare:
void foo(const char** ppData);
or even:
void foo(const char * const * ppData);
But that puts the users of the function in an awkward position.
They might have:
int main(int argc, char** argv)
{
foo(argv); // Oh no, compiler error (or warning)
...
}
And in order to cleanly call my function, they would need to insert a cast.
I come from a mostly C++ background, where this is less of an issue due to C++'s more in-depth const rules.
What is the idiomatic solution in C?
Declare foo as taking a char**, and just document the fact that it won't change its inputs? That seems a bit gross, esp. since it punishes users who might have a const char** that they want to pass it (now they have to cast away const-ness)
Force users to cast their input, adding const-ness.
Something else?
Although you already have accepted an answer, I'd like to go for 3) namely macros. You can write these in a way that the user of your function will just write a call foo(x); where x can be const-qualified or not. The idea would to have one macro CASTIT that does the cast and checks if the argument is of a valid type, and another that is the user interface:
void totoFunc(char const*const* x);
#define CASTIT(T, X) ( \
(void)sizeof((T const*){ (X)[0] }), \
(T const*const*)(X) \
)
#define toto(X) totoFunc(CASTIT(char, X))
int main(void) {
char * * a0 = 0;
char const* * b0 = 0;
char *const* c0 = 0;
char const*const* d0 = 0;
int * * a1 = 0;
int const* * b1 = 0;
int *const* c1 = 0;
int const*const* d1 = 0;
toto(a0);
toto(b0);
toto(c0);
toto(d0);
toto(a1); // warning: initialization from incompatible pointer type
toto(b1); // warning: initialization from incompatible pointer type
toto(c1); // warning: initialization from incompatible pointer type
toto(d1); // warning: initialization from incompatible pointer type
}
The CASTIT macro looks a bit complicated, but all it does is to first check if X[0] is assignment compatible with char const*. It uses a compound literal for that. This then is hidden inside a sizeof to ensure that actually the compound literal is never created and also that X is not evaluated by that test.
Then follows a plain cast, but which by itself would be too dangerous.
As you can see by the examples in the main this exactly detects the erroneous cases.
A lot of that stuff is possible with macros. I recently cooked up a complicated example with const-qualified arrays.
2 is better than 1. 1 is pretty common though, since huge volumes of C code don't use const at all. So if you're writing new code for a new system, use 2. If you're writing maintenance code for an existing system where const is a rarity, use 1.
Go with option 2. Option 1 has the disadvantage that you mentioned and is less type-safe.
If I saw a function that takes a char ** argument and I've got a char *const * or similar, I'd make a copy and pass that, just in case.
Modern (C11+) way using _Generic to preserve type-safety and function pointers:
// joins an array of words into a new string;
// mutates neither *words nor **words
char *join_words (const char *const words[])
{
// ...
}
#define join_words(words) join_words(_Generic((words),\
char ** : (const char *const *)(words),\
char *const * : (const char *const *)(words),\
default : (words)\
))
// usage :
int main (void)
{
const char *const words_1[] = {"foo", "bar", NULL};
char *const words_2[] = {"foo", "bar", NULL};
const char *words_3[] = {"foo", "bar", NULL};
char *words_4[] = {"foo", "bar", NULL};
// none of the calls generate warnings:
join_words(words_1);
join_words(words_2);
join_words(words_3);
join_words(words_4);
// type-checking is preserved:
const int *const numbers[] = { (int[]){1, 2}, (int[]){3, 4}, NULL };
join_words(numbers);
// warning: incompatible pointer types passing
// 'const int *const [2]' to parameter of type 'const char *const *'
// since the macro is defined after the function's declaration and has the same name,
// we can also get a pointer to the function
char *(*funcptr) (const char *const *) = join_words;
}

Resources