idiomatic C for const double-pointers - c

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;
}

Related

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.

Assigning a pointer to the const function arguement pointer

Say I have a const pointer to an array as a function argument like so:
int* test(const int* inputArg)
And within the function I want to assign inputArg as one of the members of a struct, like so:
typedef struct {
int * intArray;
} structSample;
structSample abc;
abc.intArray = inputArg;
How should I cast inputArg to achieve this? Right now if I compile it, error will be shown saying that
error: assigning to 'int *' from 'const int *'
Thank you
First of all, you do not have
a const pointer to an array
What you have is a pointer to constant integer. If you really wanted a constant pointer to integer as an argument, you would have to have the prototype declared as follows:
int* test(int* const inputArg)
Unless, of course, something else was in mind.
Update from comment:
So basically if you want to have a pointer to constant int stored in your function as a struct member, you can declare it just like that:
struct SampleStruct
{
const int* a;
/* whatever follows */
};
int* test(const int* inputArg)
{
struct SampleStruct ss;
ss.a = inputArg;
/* other code */
}
You must be aware, that in doing so, you must be const correct. That means, since both (argument and field) are pointers to constant integers, you must not change the values at that address(es).
abc.intArray = (int*)inputArg;
This is the C-style cast. On a side not the compiler didn't allow the const conversion by default because it's dangerous to do so. You are removing the const at your own risk.
For eg if your test is called like
const int max = 100;
//centuries later
test(&max);
and you do go ahead with the cast:
abc.intArray = (int*)inputArg;
// after another century later
*(abc.intArray) = 10; // kaboom. Undefined behavior. Debugging is a nightmare at this point
The best solution here would be changing the function to
int* test(int* inputArg)
{
/* do whatever you wish to do with inputArg itself
* why bother to create a structure and all?
* The whole purpose you wanted the const int* inputArg
* was to prevent any accidental change of data pointed to by it
* Wasn't it?
*/
}
It looks like that you are using Werror flag (this shouldn't be an error but a warning)
There is a way to lie to the compiler (using unions) without warnings:
#include <stdio.h>
int *test(const int *inputArg)
{
union {int *nonconstant; const int *constant;} fake = {.constant = inputArg};
int *p = fake.nonconstant;
return p;
}
int main(void)
{
const int x = 500;
int *p = test(&x);
*p = 100; /* Boom */
return 0;
}
As pointed out by others, don't do it :)

const char * vs. const char ** function argument [duplicate]

This question already has answers here:
Why does passing char** as const char** generate a warning?
(4 answers)
Closed 7 years ago.
I've read the C FAQ on const, but I'm still confused.
I was under the (apparently mistaken) impression that const in a function declaration was essentially a promise that the function won't modify what you have marked as const. Thus passing in a const or not-const parameter is fine. But this:
#include <stdio.h>
extern void f1 ( const char * cc );
extern void f2 ( const char ** ccc );
int main ( void ) {
char c1[] = "hello";
const char * c2 = "hello";
char * c3 = NULL;
char ** v1 = NULL;
const char ** v2 = NULL;
f1( c1 ); /* ok */
f1( c2 ); /* ok */
f1( c3 ); /* ok */
f1( "hello, world" ); /* ok */
f2( v1 ); /* compiler warning - why? */
f2( v2 ); /* ok */
return 0;
}
warns thusly:
$ cc -c -o sample.o sample.c sample.c: In function 'main': sample.c:17:
warning: passing argument 1 of 'f2' from incompatible pointer type
sample.c:4: note: expected 'const char **' but argument is of type 'char **'
Standard forbids this, because that would allow to violate constness of an object. Consider:
const char ch = 'X';
char* ch_ptr;
const char** ch_pptr = &ch_ptr; // This is not allowed, because...
*ch_pptr = &ch;
*ch_ptr = 'Q'; // ...this will modify ch, which should be immutable!
After this line:
const char** ch_pptr = &ch_ptr;
*ch_pptr and ch_ptr yield the same value (address), but they are of different types (const char* and char*). Then, you use ch_pptr to point const object, which automatically cause ch_ptr to point to the same memory location. By doing this, you allowed for any modification (using ch_ptr) of an object, that was initially declared an constant.
This error is very subtle, but after a while, you should be able to understand why it could be dangerous. That's why it is not allowed.
Yes, but why is not putting the const in the function declaration the "promise" that the function does not do that sort of evil?
Because it works the other way around - function, that does perfectly legal things may introduce invalid behavior. Look at this code:
static const char* sample_string = "String";
void f2 (const char ** ccc)
{
*ccc = sample_string; //Perfectly valid - function does not do any "evil" things
}
int main ( void )
{
char** v1 = NULL;
f2(v1); // After this call, *v1 will point to sample_string
(*v1)[0] = 'Q'; // Access violation error
return 0;
}
f2( v1 ); /* compiler warning - why? */
compiler warning - why?
Because char** and const char** are different. They are not treated as same.
char** v1 // pointer to pointer to char
Here you can change value of **v1 .
Whereas
const char** v2 //pointer to pointer to constant char
Here you can't change value of **v2 and if you try to compiler issues a error.

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 issues with pointers

I'm learning about the pointers in C. I don't understand why this code fails during the compilation process.
#include <stdio.h>
void set_error(int *err);
int main(int argc, const char * argv[])
{
const char *err;
set_error(&err);
return 0;
}
void set_error(int *err) {
*err = "Error message";
}
You declare the function to expect a pointer-to-int (int *). But you give it a pointer-to-pointer-to-char and set_error treats it as such. Change the declaration thusly:
void set_error(const char ** err)
If you had compiled with warnings enabled (-Wall for GCC) it would give the following warnings:
In function 'main':
warning: passing argument 1 of 'set_error' from incompatible pointer type [enabled by default]
set_error(&err);
^
note: expected 'int *' but argument is of type 'const char **'
void set_error(int *err);
^
In function 'set_error':
warning: assignment makes integer from pointer without a cast [enabled by default]
*err = "Error message";
^
Your function expects int * type argument but you are passing to it const char ** type argument.
Change your function declaration to
void set_error(const char **err);
The issue you have unearths an important facts about strings in C.
It also raises an interesting fact about scoping.
1. There is no such thing as a string in C; only a pointer to an array of characters.
Therefore, your statement *err = "Error message"; is wrong because by derefencing err you're not getting to the value of the string, but it's first character. (You can't quantify the 'value of a string' in C because there's no such thing as a string in C)
*err is actually undefined because nothing is yet assigned.
Note that the usual definition of a string is const char * or char * so I've changed this from what you had for the note below:
#include <stdio.h>
int main(void){
char * a = "hello";
if (*a == 'h'){
printf("it's an 'H'\n");
}
else{
printf("no it isn't\n");
}
}
You'll see that *err actually returns the value of the first character because a[0] == *a
2. You cannot return pointers to locally scoped data in C
set_error() has the correct intentions, but is doomed to fail. Although "Error message"looks like a value, it is actually already a pointer (because strings in C are pointers to character arrays, as mentioned above).
Therefore, taking (1) into account you might expect to be able to do this:
void set_int(int *myint) {
*myint = 1; //works just fine because 1 is a value, not a reference
}
void set_error(char *err) {
// doesn't work because you're trying to assign a pointer to a char
*err = "Error message";
void set_error_new(char *err) {
//doesn't work because when the function returns, "Error Message" is no longer available on the stack" (assignment works, but when you later try to get at that data, you'll segfault
err = "Error message";
}
You need to take a different approach to how you play with so-called 'strings' in C. Think of them as a pointer to a character array and you'll get better at understanding these issues. Also see C: differences between char pointer and array
One problem is that set_error expects an int * parameter, but you're passing the address of a char *, which makes it a char **. In addition, as noted by #Kninnug there's a buffer overwrite problem here which needs to be dealt with. Try rewriting your code as:
#include <stdio.h>
#include <string.h>
void set_error(char *err, size_t errbuf_size);
int main(int argc, const char * argv[])
{
char err_buf[1000];
set_error(err_buf, sizeof(err_buf));
printf("err_buf = '%s'\n", err_buf);
return 0;
}
void set_error(char *err, size_t errbuf_size) {
strncpy(err, "Error message", errbuf_size-1);
}
As you'll notice in the rewritten version of set_error, another problem is that you can't just assign a value to a pointer and have the target buffer changed - you need to use the string functions from the standard library (here I'm use strncpy to copy the constant "Error message" to the buffer pointed to by the char * variable err). You may want to get familiar with these.
Share and enjoy.
Firstly you have to change your function's declaration to
void set_error(char **err);
The body of the function is the same. Also you declared err variable as const char *err and tried change it. It generates a warning.
Let's start by talking about types. In your main function, you declare err as
const char *err;
and when you call the set_error function, you pass the expression &err, which will have type "pointer to const char *", or const char **.
However, in your function declaration and definition, you declare the parameter err as
int *err;
The types const char ** and int * aren't compatible, which is why the compiler is yakking. C doesn't allow you to assign pointer values of one type to pointer variables of a different type (unless one is a void *, which is a "generic" pointer type). Different pointer types are not guaranteed to have the same size or representation on a particular platform.
So that's where the compiler issue is coming from; what's the solution?
In C, string literals like "Error message" have type char *1 (const char * in C++), so whatever I assign it to needs to have a type of either char * or const char *. Since we're dealing with a string literal, the latter is preferable (attempting to modify the contents of a string literal invokes undefined behavior; some platforms put string literals in read-only memory, some don't). So you need to make the following changes to your code2:
void set_error( const char **err )
{
*err = "Error message";
}
int main( void ) // you're not dealing with command line arguments, so for this
{ // exercise you can use `void` for your parameter list
const char *err;
set_error( &err );
return 0;
}
Remember that C passes all function arguments by value; this means that the formal parameter err in set_error is a different object in memory than the actual parameter err in main; if the code had been
void set_error( const char *err )
{
err = "Error message";
}
int main( void )
{
const char *err;
set_error( err );
return 0;
}
then the change to err in set_error would not be reflected in the variable err in main. If we want set_error to modify the value of err in main, we need to pass set_error a pointer to err and dereference it in the function. Since the parameter err has type const char **, the expression *err has type const char *, which is the type we need for this assignment to succeed.
1. Actually, that's not true; string literals have type "N-element array of char", where N is the number of characters in the string plus the 0 terminator. However, for reasons that aren't really worth going into here, the compiler will convert expressions of array type to expressions of pointer type in most circumstances. In this case, the string literal "Error message" is converted from an expression of type "14-element array of char" to "pointer to char".
2. A function definition also serves as a declaration; I typically put the called function before the caller so I don't have to mess with separate declarations. It means my code reads "backwards" or from the bottom up, but it saves some maintenance headaches.
1st error--> You are noticing is due to the fact that your function expects a pointer to int and you are passing a pointer to const char
2nd error--> You dereferenced the pointer and inserted the value "Error Message" which is a string and you pointer was pointer to char.
3rd error--> set_error(&err); --> This statement is wrong as err itself stores an address so there is no need to put & putting & means you are passing the address of the pointer *err and not the address which it is holding. So try this.
include <stdio.h>
void set_error(const char* err[]); //Function Declaration
int main()
{
const char* err[1000];
set_error(err);
printf("%s",*err);
return 0;
}
void set_error(const char* err[])
{
*err = "Error Message";
}

Resources