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.
Related
I've written the code for passing the values normally with pointers My question is how to pass these values i.e. a & b as constant pointers, if it is not already.
void swap(int *x, int *y)
{
int tmp;
tmp = *x;
*x = *y;
*y = tmp;
return;
}
int main () {
int a = 20;
int b = 12345;
printf("Before swap, value of a : %d\n", a );
printf("Before swap, value of b : %d\n", b );
swap(&a, &b);
printf("After swap, value of a : %d\n", a );
printf("After swap, value of b : %d\n", b );
return 0;
}
const in C generally goes after the type, and the * is part of the type, so the type declaration of an integer pointer constant is:
int * const
You may have tried int const *, and found that to not work. That's because, to repeat, generally const applies to the thing on its left.
Opinion: This is why I hate the pattern of writing const first, like const int. It only works because the language has a special case - if there is nothing to the left of the const, it applies to the thing on its right - and since most people learn by example, every occurrence of const at the beginning of a type declaration contributes to mis-teaching people trying to learn the language.
Tip: I like reading and saying const as "which is constant", because that phrase clearly refers to the thing before it, so it matches the left-to-right order of the parts of a type declaration: int const then reads as "an integer which is constant" and int * const reads as "an integer's address which is constant".
Putting that together with your swap function we get
void swap(int * const x, int * const y)
Opinion: This is also why I dislike * in a declaration "touching" the name. I do int * foo instead of int *foo because it's more consistent with other type declarations like int * const foo, which I feel is more important than consistency with the unary operator placement in dereferences like *foo.
But you should know that in C, or at least in all real and most conceivable implementations of C, it doesn't actually change code behavior to make function parameters const like that - the parameter itself is private to the function, so the function can't change it. The only way it could realistically make a difference for the actual code is if you had a C compiler which could better optimize a function if you tell it that it won't be modifying its own parameters, but most optimizing compilers can just notice if a parameter isn't being modified within a function. Of course, it can still be useful as a way of telling people reading your code what your intent with the parameters is, and if you have a habit of declaring things const any time you don't have specific intent to change them, you're more likely to have computers catch certain mistakes (like if you have a parameter named similarly to a variable in your function, and you mean to modify one but accidentally type the other - if the other was const, simple tooling can catch the mistake).
Arguments are passed by value, and values are not const or non-const; only objects can be qualified that way. You can qualify the function parameters (which are objects that are initialized to the argument values), and that just means the function will not change the values of the parameters:
void swap(int * const x, int * const y)
{
// x and y are not changed, although the objects they point to are changed.
int temporary = *x;
*x = *y;
*y = temporary;
}
If you want to make the parameter types pointers to const objects, that is also possible. As long as a pointer points to an object that was not defined with const, the behavior of removing the const through a conversion and using it to modify the object is defined:
void swap(const int *x, const int *y)
{
/* ncx and ncy are set to the original pointers to non-const
that became the pointers to const that are x and y.
*/
int *ncx = (int *) x, *ncy = (int *) y;
int temporary = *ncx;
*ncx = *ncy;
*ncy = temporary;
}
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 :)
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
GCC gives me folowing warning:
note: expected 'const void **' but argument is of type 'const struct auth **
Is there any case, where it could cause problems?
Bigger snippet is
struct auth *current;
gl_list_iterator_next(&it, ¤t, NULL);
Function just stores in current some void * pointer.
The error message is clear enough: you are passing a struct auth ** where a void ** was accepted. There is no implicit conversion between these types as a void* may not have the same size and alignment as other pointer types.
The solution is to use an intermediate void*:
void *current_void;
struct auth *current;
gl_list_iterator_next(&it, ¤t_void, NULL);
current = current_void;
EDIT: to address the comments below, here's an example of why this is necessary. Suppose you're on a platform where sizeof(struct auth*) == sizeof(short) == 2, while sizeof(void*) == sizeof(long) == 4; that's allowed by the C standard and platforms with varying pointer sizes actually exist. Then the OP's code would be similar to doing
short current;
long *p = (long *)(¤t); // cast added, similar to casting to void**
// now call a function that does writes to *p, as in
*p = 0xDEADBEEF; // undefined behavior!
However, this program too can be made to work by introducing an intermediate long (although the result may only be meaningful when the long's value is small enough to store in a short).
Hm... I think constructs like const void * doesn't makes much sense.
Because if user wants to access data under void * he needs casting from void, and this action bypasses compiler type checks and consequently - constantness.
Consider this example:
#include <stdio.h>
#include <stdlib.h>
int main () {
int i = 6;
int * pi = &i;
const void * pcv = pi;
const int * pci = pi;
// casting avoids type checker, so constantness is irrelevant here
*(int *)pcv = 7;
// here we don't need casting, and thus compiler is able to guard read-only data
*pci = 7;
return 0;
}
So conclusion is that we need either void pointer Or to ensure constantness of data, but not both.
In this code, why is sizeof(x) the size of a pointer, not the size of the type x?
typedef struct {
...
} x;
void foo() {
x *x = malloc(sizeof(x));
}
Because C says:
(C99, 6.2.1p7) "Any other identifier has scope that begins just after the completion of its declarator."
So in your example, the scope of the object x start right after the x *x:
x *x = /* scope of object x starts here */
malloc(sizeof(x));
To convince yourself, put another object declaration of type x right after the declaration of the object x: you will get a compilation error:
void foo(void)
{
x *x = malloc(sizeof(x)); // OK
x *a; // Error, x is now the name of an object
}
Otherwise, as Shahbaz notee in the comments of another answer, this is still not a correct use of malloc. You should call malloc like this:
T *a = malloc(sizeof *a);
and not
T *a = malloc(sizeof a);
This is because sizeof(x) uses the innermost definition of x, which is the pointer. To avoid this problem, don't use the same name for a type and a variable.
It is a bad idea to not give different things different names (not only in programming):
The academic reason for the behavior observer had already been mentioned by my dear fellow annotators.
To give clear advises name diffenet things differnet (here: variable types and variable instances):
typedef struct {
...
} X;
void foo() {
X *x = malloc(sizeof(X));
}
An even more flexible way to code this example would be (as also already mentioned by Shahbaz's comment):
typedef struct {
...
} X;
void foo() {
X *x = malloc(sizeof(*x));
}
The latter example allows you to change the type of x without changing the code doing the allocation.
The drawback of this approach is that you could switch from using references to arrays and verse vica (as type for x) without being notified by the compiler, and break your code doing so.