I understand that a const pointer can be declared a couple ways:
const int * intPtr1; // Declares a pointer that cannot be changed.
int * const intPtr2; // Declares a pointer whose contents cannot be changed.
// EDIT: THE ABOVE CLAIMS ARE INCORRECT, PLEASE READ THE ANSWERS.
But what about the same principles within the context of function arguments?
I would assume that the following is redundant:
void someFunc1(const int * arg);
void someFunc2(int * arg);
Since someFunc 1 and 2 do a pass-by-value for the pointer itself, its impossible for someFunc1 to change the value of the original pointer, in a given call to the function. To illustrate:
int i = 5;
int * iPtr = &i;
someFunc1(iPtr); // The value of iPtr is copied in and thus cannot be changed by someFunc1.
If these are true, then there is no point in ever declaring a function with a 'const int * ptr' type arg, correct?
You have it backwards:
const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.
The following const is indeed unnecessary, and there's no reason to put it in a function declaration:
void someFunc1(int * const arg);
However, you might want to put it in the function implementation, for the same reason that you might want to declare a local variable (or anything else) const - the implementation may be easier to follow when you know that certain things won't change. You can do that whether or not it's declared const in any other declarations of the function.
Well it is not meant for the caller but for the code inside the someFunc1. So that any code inside someFunc1 wont accidentally change it. like
void someFunc1(int *arg) {
int i = 9;
arg = &i; // here is the issue
int j = *arg;
}
Lets do some case study:
1) Just making the pointed value const
void someFunc1(const int * arg) {
int i = 9;
*arg = i; // <- compiler error as pointed value is const
}
2) Just making the pointer const
void someFunc1(int * const arg) {
int i = 9;
arg = &i; // <- compiler error as pointer is const
}
3) Right way to use const if variables involved can be const:
void someFunc1(const int * const arg) {
int i = 9;
*arg = i; // <- compiler error as pointed value is const
arg = &i; // <- compiler error as pointer is const
}
This should clear all doubts. So I already mentioned it is meant for the function code and not for the caller and you should use the most restrictive of the 3 cases i mentioned above.
EDIT:
Even in declarations of functions its a good practice to declare const. This will not only increase readability but also the caller will be aware of the contract and has more confidence regarding immutability of arguments. (This is required bcoz you generally share your header files so caller might not have your implementation c/cpp file)
Even compiler can point out better if both declaration and definitions are in sync.
You've got your logic the wrong way round. You should read the type backwards, so const int * is a pointer to a const int and int * const is a const pointer to an int.
Example:
void foo() {
int a = 0;
int b = 0;
int * const ptrA = &a;
*ptrA = 1;
ptrA = &b; ///< Error
const int * ptrB = &a;
*ptrB = 1; ///< Error
ptrB = &b;
const int * const ptrC = &a;
*ptrC = 1; ///< Error
ptrC = &a; ///< Error
}
To elaborate and show why you would want your function parameter to be a const int * you might want to indicate to the caller that they must pass in an int because you as a function want to change the value. Consider this code for instance:
void someFunc1(const int * arg) {
// Can't change *arg in here
}
void someFunc2(int * arg) {
*arg = 5;
}
void foo() {
int a = 0;
someFunc1(&a);
someFunc2(&a);
const int b = 0;
someFunc1(&b);
someFunc2(&b); ///< *** Error here. Must pass in an int not a const int.
}
Yes, you are correct (ignoring the fact that you got them the wrong way around)- there is no sense in taking non-reference const parameters. In addition, there is no sense in returning non-reference const values.
You have it the wrong way:
const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.
Generally speaking its easier to reason about constness when writting that expression slightly different: const int* is the same type as int const *. In that notation the rules are much clearer, const always applies to the type preceding it, therefore:
int const * intPtr1; // Declares a pointer to const int.
int * const intPtr2; // Declares a const pointer to int.
int const * * const * complexPtr; // A pointer to const pointer to pointer to const int
When the type is written with a leading const, the const is handled as if it was written after the first type, so const T* becomes T const *.
void someFunc2(int * arg);
Is therefore not redundant, since someFunc2 may change the contents of arg, while someFunc1 may not. void someFunc3(int * const arg); would be redundant (and ambigous) though
Related
I'm currently working on a project where I often have to build linked lists of various C structs. Since I don't want to keep repeating myself setting next pointers, I wrote some helper templates, but soon found out that it falls apart if one of the next fields is a pointer-to-const.
My linked list elements look something like this:
struct WorkingElementType {
void *pNext;
/* stuff */
};
struct TroublesomeElementType {
const void *pNext;
/* stuff */
};
In reality, there are of course a lot more of these structs. My helper functions have to keep a pointer to the last element's pNext field in order to write to it when the linked list gets extended, so I went for a void **ppNext = &last->pNext. Unfortunately, that of course breaks down with TroublesomeElementType and its const void *pNext.
In the end, what I'd like to achieve is this:
void **m_ppNext;
/* In one function */
m_ppNext = &last->pNext;
/* In a different function, extending the list */
T *elementToAppend = ...;
*m_ppNext = elementToAppend;
I solved this by using a std::variant<void **, const void **> ppNext instead, but using a std::variant and std::visit just for a difference in constness that doesn't even affect the code's function feels like a bit of a waste.
That's why I'm wondering: Is it legal to use const_cast here to cast away const and stuff the const void ** into a void ** only for updating the pointed-to pointer? No const object actually gets modified, after all.
In other words: I'm not sure whether it's legal to alias const void* and void *. (My gut feeling says no, it's not legal because these are incompatible types, but I don't know for sure.)
The C++ standard in question is C++20.
Here's some simple example code:
#include <variant>
int g_i = 42;
/* This is legal */
void setIntPtr1(std::variant<int **, const int **> v) {
std::visit([](auto& p) { *p = &g_i; }, v);
}
int testNonConst1() {
int *i;
setIntPtr1(&i);
return *i;
}
int testConst1() {
const int *i;
setIntPtr1(&i);
return *i;
}
/* But I'm not sure about this */
void setIntPtr2(int **p) {
*p = &g_i;
}
int testNonConst2() {
int *i;
setIntPtr2(&i);
return *i;
}
int testConst2() {
const int *i;
setIntPtr2(const_cast<int **>(&i)); // Is this legal?
return *i;
}
On Godbolt, all of the various test... functions compile to the exact same assembly, but I don't know if testConst2 is legal C++.
I've found the following two existing questions:
Is it legal to modify any data pointer through a void **
Why isn't it legal to convert "pointer to pointer to non-const" to a "pointer to pointer to const"
However, both of them don't seem to quite answer my question. The first one deals with casting any T** to a void **, which is not what I'm doing; I'm just casting away constness. The second one asks why it's a compile error to convert a void ** to a const void **, but not whether interpreting the memory of a void * as a const void * and vice-versa (without actually overwriting a const object) would be a violation of the aliasing rules.
Yes, it is legal.
[basic.lval]/11:
If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:
the dynamic type of the object [...]
T* and const T* are similar:
Two types T1 and T2 are similar if they have cv-decompositions with the same n such that corresponding Pi components are either the same or one is "array of Ni" and the other is "array of unknown bound of", and the types denoted by U are the same.
So I'm looking for clarification on something that works. I'm pretty sure I understand what is happening but wanted to be sure before proceeding with my work.
I have a function defined as follows:
name* createName(char* firstName, char* lastName)
{
name* newName = (name*)malloc(sizeof(name));
initStringValue(&newName->firstName, firstName);
initStringValue(&newName->lastName, lastName);
newName->firstNameSize = strlen(newName->firstName);
newName->lastNameSize = strlen(newName->lastName);
return newName;
}
The structure "name" is defined like so:
struct name
{
char* firstName;
char* lastName;
int firstNameSize;
int lastNameSize;
};
Another function responsible for the copy of the name strings is written like the following:
void initStringValue(char** destination, char* source)
{
int length = strlen(source) + 1;
int size = length * sizeof(char);
*destination = (char*)malloc(size);
memset(*destination, 0, size);
strcpy(*destination, source);
}
If I'm understanding what I've done here, by using the & operator I've signified that I wish to send not a value but its associated memory address. In a statement such as
&newName->firstName
where the struct member firstName is a char* I've indicated that I would like to send the memory address of this pointer and not the pointers value (which happens to be a memory address in and of itself). The -> operator dereferences this pointer to the member of the pointer but then the & operator essentially returns us to the firstName memory reference instead, allowing me to manipulate information at that memory reference.
Now things get wild (for me anyway). To actually work with that memory reference, I end up using double indirection (so very passive aggressive). As it follows a memory reference (like that of &newName->firstName) sent to a char** like that of char** destination in the initStringValue function, would be a pointer of a pointer where the latter is assigned the memory reference returned by &newName->firstName. By then using *destination I'm working with a pointer pointed to the memory reference of &newName->firstName. Or stated differently, a pointer whose first and only member is the memory reference of newName->firstName.
Am I actually understanding this correctly?
Am I actually understanding this correctly?
After reading your description, I'll say yes
I'll try to explain it with some examples.
If you do this:
void foo(int a)
{
a = 5;
}
int main()
{
int a = 10;
foo(a);
printf("%d\n", a);
return 0;
}
You'll get the output: 10
That's because the function parameters are local variables to the function. In other words - any change made to a function parameter is lost when the function returns, i.e. the variable in main will not be changed.
If you want a function to change the value of a variable in main (aka in the caller), you'll have to pass a pointer. Like:
void foo(int* a) // notice int*
{
*a = 5; // notice *a
}
int main()
{
int a = 10;
foo(&a); // notice &a
printf("%d\n", a);
return 0;
}
This will output: 5
This is a general rule regardless of the type. I used int in the example but it applies to any type - pointers as well.
So let's take an example with a pointer:
void foo(char** a, int size) // notice char**
{
*a = malloc(32); // malloc memory
strcpy(*a, "Hello world"); // copy some data into the memory
}
int main()
{
char* a = NULL; // a is not pointing to anything yet
foo(&a);
// Now a points to the malloc'ed memory
printf("%s\n", a);
return 0;
}
This will output: Hello world
I have a function (func1) that gets as a parameter a const pointer.
The value of this pointer is used for a second function (func2).
Depending on a boolean I want to modify this value before handing it to func2 or not. If I have to modify it I allocate new memory to store the modified version of it. Because in reality the const int pointer is a big array, I don't want to copy it.
I tried to solve it that way:
void func1(const int* value, bool change)
{
int* valueToUse;
if(change)
{
int changedValue = (*value)++;
valueToUse = &changedValue;
}
else
{
valueToUse = value; // <= here the Error occurs
}
func2(valueToUse);
}
void func2(const int* foo)
{
// ...
}
But if I do it this way, I get an error because I assign a const pointer to a simple pointer:
error: invalid conversion from 'const int* to int*'
Is there any easy way to solve this?
I can imagine a solution with two extra functions for each case or a version that calls func2 at two points. But because this presented code is only a very simplified version of my real code, I'm wandering if there is an easier solution to that.
Best would be a solution that works for C and C++.
Thanks guys in advance!
This works?
void func1(const int* value, bool change)
{
const int* valueToUse;
int changedValue;
if(change)
{
changedValue = (*value) + 1;
valueToUse = &changedValue;
}
else
{
valueToUse = value;
}
func2(valueToUse);
}
A const int* is a pointer to const int, which is not a const pointer to int. Therefore the pointer itself is free to change.
Also, incrementing (*value) is an error because *value is a const int.
In func1, you can make 2 calls to func2, this way there is no copy when not needed and the original value is never changed:
void func1(const int* value, bool change)
{
if(change)
{
int changedValue = *value+1;
func2(&changedValue);
}
else
{
func2(value);
}
}
void func1(const int* value, int change)
{
int* valueToUse;
if(change)
{
(*(int*)value)++;
valueToUse = (int*)value;
}
else
{
valueToUse = (int*)value; // <= here the Error occurs
}
func2(valueToUse);
}
But you don't really need this valueToUse, it's what i think, so.
void func1(const int* value, int change)
{
if(change)
(*(int*)value)++;
func2(value);
}
const int* value
This means pointer to a constant int value. It is not permitted to change the value of a constant int.
There are two ways of solving this.
Change signature of first function void func1(int* value, bool change)
Pass arguments to func1 after constant casting using const_cast and change
int changedValue = (*value)++; to int changedValue = *value + 1;
Well, running this code i receive just a warning, though i think that type conversion will help you.
valueToUse = (int*)value;
void process( int boat ) { ; }
const void(*sequence_A[])( int ) = { process, process }; //ERROR
const void(**func_sequence)( int ) = sequence_A;
(*func_sequence++)( 7 );
Why won't this compile? I want const to refer to the array, not the contents of the array.
Error 1 error C2440: 'initializing' : cannot convert from 'void (__cdecl *)(int)' to 'const void (__cdecl *)(int)'
EDIT: So you guys are saying it doesn't exist. Fair enough. Just to be clear, I'm posting this analogy of the functionality I wanted but this is with int instead of function ptrs
//Compiles without error
int number1 = 7;
int number2 = 3;
const int* sequence_B[] = { &number1, &number2 };
const int** numbers = sequence_B;
int check = **numbers++; //value is 7
int chec2 = **numbers++; //value is 3
Okay, let's analyze the meaning of your statement
const void(*sequence_A[])( int ) = { process, process }; //ERROR
The way I remember the parsing of *x[] is that the second argument of main is char* argv[], so, it's an array of pointers. In other words, sequence_A is to be indexed, and then the result is to be dereferenced.
Then, to that you can apply a function call argument parenthesis with an int value, and as a result you should get a …
const void ?
Well that's not entirely meaningful. You can have a pointer to const void, but you can't dereference that pointer: you can't "get at" the const void directly. Yet here is some pointer to a function that produces as its expression value a const void.
To match that you would need
const void process( int boat ) { ; }
and although I haven't tried it, I doubt that any compiler will accept it. [Update: as it turns out, at least g++ accepts it, so it is one solution. But it's a very unconventional function signature. And not at all what you're after!]
In short, remove that const.
On a related note, as mentioned already in a comment you can't have a const raw array, except in the sense of a raw array of const items.
It's a corner case of the language, a problematic type system aberration inherited from C.
Along with the array type decay to pointer, also problematic.
Addendum: example of how to make the array items const:
void process( int boat ) { ; }
int main()
{
void(* const sequence_A[])( int ) = { process, process }; // OK
//sequence_A[0] = process; //ERROR
}
Suppose I have a class:
class test {
public:
void print();
private:
int x;
};
void test::print()
{
cout<< this->x;
}
and I have these variable definitions:
test object1;
test object2;
When I call object1.print() this happens to store address of object1 and so I get x from object1 printed and when I call object2.print() this happens to store address of object2 and I get x from object2 printed. How does it happen?
Each non-static member function has an implicit hidden "current object" parameter that is exposed to you as this pointer.
So you can think that for
test::print();
there's some
test_print( test* this );
global function and so when you write
objectX.print();
in your code the compiler inserts a call to
test_print(&objectX);
and this way the member function knows the address of "the current" object.
You can think of the this pointer being an implicit argument to the functions. Imagine a little class like
class C {
public:
C( int x ) : m_x( x ) { }
void increment( int value ) {
m_x += value; // same as 'this->m_x += value'
}
int multiply( int times ) const {
return m_x * times; // same as 'return this->m_x * times;'
}
private:
int m_x;
};
which allows you to write code like
C two( 2 );
two.increment( 2 );
int result = two.multiply( 3 );
Now, what's actually happening is that the member functions increment and multiply are called with an extra pointer argument, pointing to the object on which the function is invoked. This pointer is known as this inside the method. The type of the this pointer is different, depending on whether the method is const (as multiply is) or not (as is the case with increment).
You can do something like it yourself as well, consider:
class C {
public:
C( int x ) : m_x( x ) { }
void increment( C * const that, int value ) {
that->m_x += value;
}
int multiply( C const * const that, int times ) const {
return that->m_x * times;
}
private:
int m_x;
};
you could write code like
C two( 2 );
two.increment( &two, 2 );
int result = two.multiply( &two, 3 );
Notice that the type of the this pointer is C const * const for the multiply function, so both the pointer itself is const but also the object being pointed to! This is why you cannot change member variables inside a const method - the this pointer has a type which forbids it. This could be resolved using the mutable keyword (I don't want to get side-tracked too far, so I'll rather not explain how that works) but even using a const_cast:
int C::multiply( int times ) const {
C * const that = const_cast<C * const>( this );
that->m_x = 0; // evil! Can modify member variable because const'ness was casted away
// ..
}
I'm mentioning this since it demonstrates that this isn't as special a pointer as it may seem, and this particular hack is often a better solution than making a member variable mutable since this hack is local to one function whereas mutable makes the variable mutable for all const methods of the class.
The way to think about it is that this is simply a pointer to the memory for whichever object you're currently working with. So if you do obj1.print(), then this = &obj1;. If you do obj2.print(), then this = &obj2;.
this has different values for different objects
Each instance of class test gets it's own copy of member variable x. Since x is unique for each instance, the value can be anything you want it to be.
The variable this, refers to the instance to which it is associated. You don't have to use the variable 'this'. You could just write:
void test::print()
{
cout << x;
}