This question already has an answer here:
C++ forbidden pointer to pointer conversion
(1 answer)
Closed 8 years ago.
Is there way to create an const int** from an int**?
I'm currently using:
const int **pixel2=*pixel;
const char **header2=*header;
I keep getting the error: cscd240HW34.c:52:21: warning: initialization from incompatible pointer type [enabled by default]
const int **pixel2=*pixel;
If pixel is already of type int ** then:
const int **pixel2 = (const int **)pixel;
By way of explanation: the reason a cast is required is because this still doesn't give you as much type-safety as you might think. For example you could now write:
const int c = 'x';
*pixel2 = &c; // fine, both are const int *
**pixel = 'y'; // no compiler error, but UB as we modify a non-writable object
So, see if there is another way to do what you want to do. Note that this definition of pixel2 avoids that exploit
const int * const * pixel2;
although sadly C still requires a cast to assign pixel to pixel2.
This question is 11.10 in the c.l.c. FAQ.
Related
This question already has an answer here:
Confusion regarding modification of const variable using pointers
(1 answer)
Closed 6 years ago.
I have some code with const void* as below:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main()
{
const int p = 10;
char s[] ="I am newbie";
const void *vp;
vp = &p;
*(int*)vp = 11;
printf("%i\n", *(int*)vp);
vp= s;
printf("%s\n", (char*)vp);
return 0;
}
My question is why const void* vp still be updated?
As my understanding, const variable can not be updated directly, is that correct for all types?
You effectively removed the const when you got a pointer to your int, cast the pointer to something without the const, and then changed the value of what that points to.
*(int*)vp = 11;
You can cast a pointer type to any other pointer type.
Modifying a const variable like this is undefined behavior. For example, the compiler might have seen that the variable was const, realized that it could pre-compute what the printf functions would print, and do the substitution at compile time as an optimization. The compiler would be allowed to do this because it thinks the variable is const.
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 :)
I was looking at an example which showed that why typedef'ng a pointer is a bad practice. The part I didn't understand about the example is that why the compiler wasn't able to catch the problem. I elaborated the example into the following code:
#include <stdio.h>
typedef int *TYPE;
void set_type(TYPE t) {
*t = 12;
}
void foo(const TYPE mytype) {
set_type(mytype); // Error expected, but in fact compiles
}
int main() {
TYPE a;
int b = 10;
a = &b;
printf("A is %d\n",*a);
foo(a);
printf("A is %d\n",*a);
return 0;
}
So, I was able to change the value of a. Even though, the example explained that you would have to do, typedef const int *TYPE, to solve the problem, I dont understand why the compiler was not able to catch the error when I am setting TYPE to be const in the function argument itself. I am sure I am missing something very basic.
The issue here is confusion about what const is being applied to: is it applied to the pointer value itself, or the value being pointed to?
const TYPE x is saying that the pointer value should be constant. This is roughly equivalent to int * const x. Note that the following code does result in an error:
int b = 10;
const TYPE p = NULL;
p = &b; // error here (assign to const)
What you were expecting is const int * x, which makes the value pointed to by x a constant.
Since the typedef hides the fact that TYPE is a pointer type, there's no way to specify that the thing pointed to by TYPE should be const, aside from declaring another typedef:
typedef const int * CONST_TYPE;
However, at this point the typedef seems to be causing more trouble than it solves... Probably not the best idea.
The const modifier in C++ before star means that using this pointer the value pointed at cannot be changed, while the pointer itself can be made to point something else. In the below
void justloadme(const int **ptr)
{
*ptr = new int[5];
}
int main()
{
int *ptr = NULL;
justloadme(&ptr);
}
justloadme function should not be allowed to edit the integer values (if any) pointed by the passed param, while it can edit the int* value (since the const is not after the first star), but still why do I get a compiler error in both GCC and VC++?
GCC: error: invalid conversion from int** to const int**
VC++: error C2664: 'justloadme' : cannot convert parameter 1 from 'int **' to 'const int **'. Conversion loses qualifiers
Why does it say that the conversion loses qualifiers? Isn't it gaining the const qualifier? Moreover, isn't it similar to strlen(const char*) where we pass a non-const char*
As most times, the compiler is right and intuition wrong. The problem is that if that particular assignment was allowed you could break const-correctness in your program:
const int constant = 10;
int *modifier = 0;
const int ** const_breaker = &modifier; // [*] this is equivalent to your code
*const_breaker = & constant; // no problem, const_breaker points to
// pointer to a constant integer, but...
// we are actually doing: modifer = &constant!!!
*modifier = 5; // ouch!! we are modifying a constant!!!
The line marked with [*] is the culprit for that violation, and is disallowed for that particular reason. The language allows adding const to the last level but not the first:
int * const * correct = &modifier; // ok, this does not break correctness of the code
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;
}