3

I wish to return a pointer to data from a function. The caller should not modify that data. I want the compiler to flag an error if the caller is assigning the return value to a non const pointer.

I know that it is possible to cast away const, but I want the caller to be forced to do that cast instead of just an implicit cast or warning. (or no warning!, as on the compiler I am using). I believe the answer to my question is that it is not possible. I think that there is no way to prevent caller code like this from compiling, and that if the user deferences the data (buf in my example) it will be undefined behaviour.

Please note I am asking if it is possible to do this in the c language itself, and if not, an explanation of why the standard prevents this. I am not looking for a way to make this specific example flag an error using some specific compiler command line.

#include "stdio.h"

const int* get_buf_2()
{
    static int arr[10] = {1};
    return arr;
}

void get_buf(const int ** buf)
{
    static int arr[10] = {1};
    *buf = arr;
}

int main(int argc, char const *argv[])
{
    int *buf;
    get_buf(&buf);
    printf("%d %d\n", buf[0], buf[1]);
    buf = get_buf_2();
    printf("%d %d\n", buf[0], buf[1]);
    const int cbuf[10];
    buf = cbuf;
    return 0;
}

This code produces 3 warnings with clang and gcc, each time the pointer buf is assigned from a pointer to const. The two different warnings:

warning: passing 'int **' to parameter of type 'const int **' discards
      qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers]
warning: assigning to 'int *' from 'const int *' discards qualifiers
  [-Wincompatible-pointer-types-discards-qualifiers]

Possibly related:

Why can't I convert 'char**' to a 'const char* const*' in C?

Return Pointer To Non-Modifiable Member Array C++ gcc warning

Evan Benn
  • 1,571
  • 2
  • 14
  • 20

3 Answers3

1

I'm not entirely clear on what you are looking for, but nothing prevents you from returning the address to a read-only location. Take for example the short snippet where the function number returns a const int* pointer to the address of a value within data:

#include <stdio.h>

const int data[] = { 1, 2, 3, 4 };
const int notfound = -1;

const int *number (unsigned n)
{
    if (n < sizeof data / sizeof *data)
        return &data[n];

    return &notfound;
}

int main (int argc, char **argv) {

    int v = argc > 1 ? *argv[1] - '0' : 2;
    const int *n = number (v);

#ifdef TESTERR
    (*n)++;  /* <== error: increment of read-only location ‘*n’ */
#endif

    printf ("v : %d\n", *n);

    return 0;
}

Example Without Attempt to Modify

If there is no further attempt to modify the returned pointer, the compiler has no problems at all:

$ gcc -Wall -Wextra -pedantic -std=gnu11 -o bin/fn_const_ptr fn_const_ptr.c
(no error, no warning)

Example Attempting to Modify Return

However, if you do attempt to modify the value at the location returned by number, the compiler will generate an error without any specific option supplied

$ gcc -Wall -Wextra -pedantic -std=gnu11 -o bin/fn_const_ptr fn_const_ptr.c -DTESTERR
fn_const_ptr.c: In function ‘main’:
fn_const_ptr.c:20:5: error: increment of read-only location ‘*n’
     (*n)++;  /* <== error: increment of read-only location ‘*n’ */
     ^

Of course the const can always be cast away, but that would take an affirmative act, which would not be avoidable. E.g.

int *p;
...
#ifdef TESTERR
    p = (int*)n;
    (*p)++;  /* SegFault */
#endif

Let me know if this is what you intended.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1

To answer your question, "I am asking if it is possible to do this [...] and if not, an explanation of why the standard prevents this."

For the first part: const is the only tool that C gives you for this purpose.

If your use-case permits it you could build an iterator-like struct and supporting functions to provide element-by-element access while protecting the actual storage. (But if that fits, you could also just change the API to a int get_len()/char get_char(int idx) interface)

For the second part: per ISO 9899:201x draft n1570:

6.5.16.1 Simple assignment

1 One of the following shall hold:

[...]

— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) 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;

This is the case that covers assignment to a pointer (when neither operand to the assignment is void *), and it clearly requires the type pointed to by the left side to have all of the qualifiers of the type pointed to by the right side. With const being one such qualifier, your compiler that doesn't even warn about an assignment that ignores const is just not implementing the spec.

(For clarity, the spec uses "qualified pointer" to mean <type> * <qualifier>. It uses "qualifier on the type pointed to" to mean <qualifier> <type> *.)

[Editorial comment: I would hazard a guess that the compiler that doesn't even warn is targeted toward embedded development. Embedded developers seem to put up with abysmal tools regularly.]

lockcmpxchg8b
  • 2,205
  • 10
  • 16
0

The problem is your declaration of buf in main:

int *buf;

Just change it to:

const int *buf;

That should take care of the warnings.

Tom Karzes
  • 22,815
  • 2
  • 22
  • 41
  • Hello, I realise now I was not clear enough, I want to get rid of the warnings by turning them into errors. I want the compiler to tell me I am not allowed to have a modifiable pointer to that data. I will clarify the question – Evan Benn Dec 14 '17 at 03:24
  • This response only considered the title, not the question – lockcmpxchg8b Dec 14 '17 at 04:43
  • @EvanBenn if you wish to turn warrings to error, use this compile flag `-Werror` – ntshetty Dec 14 '17 at 04:47
  • @EvanBenn Oh, I see now. I originally thought you were just trying to eliminate the warnings. As for turning it into an error, that's going to be compiler-specific. As Thiru suggests, you can use `-Werror` with `gcc`. Note that you can make it more specific by using `-Werror=x` where `x` is a specific class of warnings (see the `gcc` documentation for specific details). – Tom Karzes Dec 14 '17 at 05:29
  • @lockcmpxchg8b Yes, I see that now. I originally thought OP was just trying to eliminate the warning. – Tom Karzes Dec 14 '17 at 05:34
  • Thanks for the comments, I have clarified the question further. I am looking for an answer as to how (or why not possible) to do this in the C language, not using compiler flags. @TomKarzes I did not know about the specific -Werror=x, thanks for that. – Evan Benn Dec 14 '17 at 05:48