Ben
Ben

Reputation: 7802

C++: Const correctness and pointer arguments

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?

Upvotes: 24

Views: 40222

Answers (5)

havexz
havexz

Reputation: 9590

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.

Upvotes: 28

Grizzly
Grizzly

Reputation: 20211

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

Upvotes: 2

mattjgalloway
mattjgalloway

Reputation: 34912

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

Upvotes: 7

Mike Seymour
Mike Seymour

Reputation: 254751

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.

Upvotes: 40

Puppy
Puppy

Reputation: 147056

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.

Upvotes: 2

Related Questions