如何在c中返回指向不可修改数据的指针

时间:2017-12-14 02:54:44

标签: c pointers const

我希望返回一个指向函数数据的指针。调用者不应该修改该数据。如果调用者将返回值赋给非const指针,我希望编译器标记错误。

我知道可以抛弃const,但我希望调用者被强制执行该转换,而不仅仅是隐式转换或警告。 (或者没有警告!,就像我正在使用的编译器一样)。我相信我的问题的答案是,这是不可能的。我认为没有办法阻止这样的调用者代码进行编译,如果用户对数据进行了修改(在我的例子中为buf),那将是未定义的行为。

请注意我在询问是否可以用c语言本身进行此操作,如果没有,则说明为什么标准会阻止这种情况。我不是在寻找一种方法来使这个特定的示例使用一些特定的编译器命令行标记错误。

#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;
}

此代码使用clang和gcc生成3个警告,每次从指向const的指针指定buf。两个不同的警告:

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]

可能相关:

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

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

3 个答案:

答案 0 :(得分:1)

我并不完全清楚你在寻找什么,但没有什么能阻止你将地址返回read-only位置。例如,函数number返回指向const int*内值的地址的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;
}

不尝试修改的示例

如果没有进一步尝试修改返回的指针,编译器完全没有问题:

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

尝试修改退货的示例

但是,如果您尝试修改number返回的位置处的值,编译器将生成错误,而不提供任何特定选项

$ 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’ */
     ^

当然const总是可以被抛弃,但这将采取肯定行为,这是不可避免的。 E.g。

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

如果这是你想要的,请告诉我。

答案 1 :(得分:1)

要回答你的问题,“我问是否有可能这样做[...],如果没有,解释为什么标准会阻止这个。”

对于第一部分: const是C为此提供的唯一工具。

如果您的用例允许,您可以构建类似迭代器的结构和支持函数,以在保护实际存储的同时提供逐元素访问。 (但如果适合,您也可以将API更改为int get_len() / char get_char(int idx)界面)

对于第二部分:ISO 9899:201x draft n1570

  

6.5.16.1简单分配

     

1以下之一应持有:

     

[...]

     

- 左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,类型左边指向的是右边指向的所有类型的限定符;

这种情况涵盖指针的赋值(当赋值的操作数都不是void *时),并且它明显要求左侧指向的类型具有指向的类型的所有限定符在右边。由于const是一个这样的限定符,你的编译器甚至没有警告忽略const的赋值,只是没有实现规范。

(为清楚起见,规范使用“限定指针”来表示<type> * <qualifier>。它使用“指向类型的限定符”来表示<qualifier> <type> *。)

[编辑评论:我会猜测甚至没有发出警告的编译器都是针对嵌入式开发的。嵌入式开发人员似乎经常忍受糟糕的工具。]

答案 2 :(得分:0)

问题是您在buf中声明了main

int *buf;

只需将其更改为:

const int *buf;

应该注意警告。