Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read-only Pointer to Pointer

Tags:

c

pointers

In C, const char *p is sometimes called a "read-only" pointer: a pointer to a constant object (in this case, a char).

It would seem that either

  1. const char **p
  2. const char *const *p

would be the equivalent declarations for a read-only pointer to a pointer, depending on how many levels of indirection are immutable.

However, compilers (gcc, clang) generate a warning.

My Questions: How do you pass a pointer to a pointer (like char **p) to a function as a "read-only" pointer without generating a warning? If an explicit cast is required, why in the case of char **p and not char *p?

More Details

Here is a concrete example of what I'm trying to achieve.

Read-only Pointer

This code treats char *ptr as a read-only pointer.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void readonly(const char *ptr)
{
    // ... do something with ptr ...

    // but modifying the object it points to is forbidden
    // *ptr = 'j';  // error: read-only variable is not assignable
}

int main(void)
{
    char *ptr =  malloc(12*sizeof(char));
    strcpy(ptr, "hello world");

    printf("before: %s\n", ptr);
    readonly(ptr);
    printf("after: %s\n", ptr);

    free(ptr);
    return 0;
}

The qualifier const is added in the function call without any complaints.

Read-only Pointer to Pointer

I would expect that a similar function call should be possible with a pointer to a pointer.

void readonly(const char *const *ptr)
{
    //  ... do something with ptr ...

    // but modifying the object it points to is forbidden
    // **ptr = 'j';
}

int main(void)
{
    char **ptr;

    ptr = (char **) malloc(2*sizeof(char *));

    ptr[0] = malloc(14*sizeof(char));
    strcpy(ptr[0], "hello world 0");

    ptr[1] = malloc(14*sizeof(char));
    strcpy(ptr[1], "hello world 1");

    printf("before: %s %s\n", ptr[0], ptr[1]);
    readonly(ptr);
    printf("after: %s %s\n", ptr[0], ptr[1]);

    free(ptr[1]);
    free(ptr[0]);
    free(ptr);
    return 0;
}

The clang compiler (version 6.0.0) gives the most human-readable warning.

warning: passing 'char **' to parameter of type
    'const char *const *' discards qualifiers in nested pointer types
    [-Wincompatible-pointer-types-discards-qualifiers]
    readonly(ptr);
         ^~~
note: passing argument to parameter 'ptr' here
    void readonly(const char *const *ptr)

But gcc (8.1.1) also gives a warning.

Aside: It seems strange that clang says that passing char ** discards the qualifier, when I'm trying to add the qualifier?

The Questions

  1. How do you pass a pointer to a pointer (like char **p) to a function as a "read-only" pointer without generating a warning?

  2. If an explicit cast is required, why in the case of char **p and not char *p?

like image 569
Kris Avatar asked Jul 14 '18 17:07

Kris


People also ask

Is a pointer read only?

A pointer is a reference to data, so the only way to do so is to copy the data, there's nothing like a read-only memory permission (unless it's the part holding the executable itself).

What is constant to pointer?

A constant pointer is one that cannot change the address it contains. In other words, we can say that once a constant pointer points to a variable, it cannot point to any other variable. Note: However, these pointers can change the value of the variable they point to but cannot change the address they are holding.

Can we assign a constant to a pointer variable?

A pointer to constant is a pointer through which the value of the variable that the pointer points cannot be changed. The address of these pointers can be changed, but the value of the variable that the pointer points cannot be changed.

What is the difference between constant pointer and pointer to constant?

In the constant pointers to constants, the data pointed to by the pointer is constant and cannot be changed. The pointer itself is constant and cannot change and point somewhere else.


1 Answers

Your function readonly(const char *ptr) promises that it will not touch what is behind the pointer.
That's the reason why a caller may trustfully pass a pointer to a const memory area (passing a const char *ptr).
And of course it is no problem to pass a non-const char *ptr to this function as well, because your function promises more security than needed. That's why the compiler follows your wish without any warning or note whatsoever.

However this automatism does only work for the 1st indirection level.

The Standard claims in 6.5.16.1 for an assignment, that "both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right"
The last part of the sentence means that adding a qualifier to the pointed-to type is no problem.
And the first part claims "compatible" types. And (I think,) 6.7.3 (11) does describe this for qualified types: "For two qualified types to be compatible, both shall have the identically qualified version of a compatible type."

Reading this, your pointed-to types are not considered as compatible (even if it would be possible to assign one to the other).

Hence I would say that the clang warning about discarding qualifiers is a bit misleading, but it refers to the non-identically qualified pointed-to types.

like image 76
MattTT Avatar answered Sep 28 '22 05:09

MattTT