Correctness - const

Non-Modifiable Memory (1)

Did you know the difference?

void f(void)
{
    char str[] = "blah";
    str[0] = 'x';
}
void f(void)
{
    char *str = "blah";
    str[0] = 'x';
}

Non-Modifiable Memory (2)

char str[] = "blah";
../../../../../../_images/91-05-00-sanity-const-memory-stack.svg
  • Array initialization

  • Allocated on the stack, at runtime

  • writable

char *str = "blah";
../../../../../../_images/91-05-00-sanity-const-memory-rodata.svg
  • Allocated in read-only memory, at compilation time

  • Pointer setup at runtime, to point there

  • not writable

The const Keyword (1)

So there is already the concept of read-only data …

  • Sadly compilers generally issue no warnings

  • (On Linux) Not an error, only on-demand duplication of a shared read-only memory page

  • expensive

  • Unintended in most cases

$ gcc -Wwrite-strings ...
warning: initialization discards ‘const’ qualifier from
   pointer target type

A-ha: “const” qualifier!

The const Keyword (2)

warning: initialization discards ‘const’ qualifier from
   pointer target type
  • char *str = "blah";

  • Obviously (no surprise) the compiler knows that "blah" is in read-only memory

  • ⟶ String literals are const char *

const char *str = "blah";

Consequences:

  • str cannot be written to

  • ⟶ Code has to be fixed until compiler is happy

  • Correctness with minimal effort

const Variables

Getting rid of the preprocessor (good idea) …

const int MAX_BUCKETS = 64;

… is the same, compiler-wise, as …

#define MAX_BUCKETS 64

Additional benefits …

  • MAX_BUCKETS has a type

  • Not a stupid string substitution, but a regular C identifier

  • “unused” warnings

const Parameters (1)

int sum(int *begin, int *end);

Reading this declaration, we assume the following:

  • It builds a sum

  • It returns the result

  • It operates on a range [begin, end)

  • It does not modify the input data

Ambiguity alert:

  • We can say nothing of the above for sure

  • … but we can help with the last item

const Parameters (2)

int sum(const int *begin, const int *end);

Now we can say one thing for sure:

  • It does not modify the input data

Consequences:

  • sum() has to modified

  • Not a big deal when only a few lines involved

  • Can be a problem when code is large and complex

  • ⟶ “const pollution”

Pointers, Pointers, Pointers … (1)

What’s known so far:

  • const can be applied to scalar types

  • const can be applied to struct types (we don’t know this, but it’s a logical consequence)

  • const, applied to pointers, keeps me from modifying what they point to

const int i;
int const j; /* same! */
const int *pi = &i;
int const *pj = &j;
../../../../../../_images/91-05-00-sanity-const--c-p-nc.svg

Pointers, Pointers, Pointers … (2)

Mixing …

int const i = 42;
int *pi = &i;
warning: initialization discards ‘const’ qualifier
  from pointer target type
  • pi does not promise to not modify the value it points to

  • Pointee is read-only

  • Sadly this can only be a warning for historical reasons

Pointers, Pointers, Pointers … (3)

So, given that …

int const i;

… is a read-only variable, …

int * const pi;

… is a read-only variable:

  • A pointer that cannot be modified

  • But can be used to modify what it points to (it’s an int, not an int const)

Pointers, Pointers, Pointers … (4)

int * const pi;
/* error: assignment of read-only variable ‘pi’ */
pi = NULL;
/* ok, compiles */
*pi = 42;
../../../../../../_images/91-05-00-sanity-const--nc-p-c.svg

But is this correct?

Pointers, Pointers, Pointers … (5)

So what’s this?

int i = 42
int const * const pi = &i;

/* error: assignment of read-only variable ‘pi’ */
pi = NULL;

/* error: assignment of read-only location ‘*pi’ */
*pi = 42;
../../../../../../_images/91-05-00-sanity-const--c-p-c.svg

Pointers, Pointers, Pointers … (6)

How about pointers that point to pointers?

int i = 42;
int *pi = &i;
int **ppi = π

**ppi = 7;
*ppi = NULL;
../../../../../../_images/91-05-00-sanity-const--nc-p-nc-p-nc.svg

Pointers, Pointers, Pointers … (7)

How about pointers that point to pointers that point to const}? (Gosh)

int const i = 42;
int const *pi = &i;
int const **ppi = π

ppi = NULL;
*ppi = NULL;
/* error: assignment of read-only location ‘**ppi’ */
**ppi = 7;
../../../../../../_images/91-05-00-sanity-const--pppppp.svg

Pointers, Pointers, Pointers … (8)

How about pointers that point to non-modifiable pointers that point to const ?

int const i = 42;
int const * const pi = &i;
int const * const *ppi = π

ppi = NULL;
/* error: assignment of read-only location ‘*ppi’ */
*ppi = NULL;
/* error: assignment of read-only location ‘**ppi’ */
**ppi = 7;
../../../../../../_images/91-05-00-sanity-const--ppppppccccppppp.svg

Pointers, Pointers, Pointers … (9)

How about …? (To be continued)