指向指针的只读指针

时间:2018-07-14 17:02:21

标签: c pointers

在C中,const char *p有时被称为“只读”指针:指向常量对象(在这种情况下为char)的指针。

似乎也是

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

将是指向指针的只读指针的等效声明,具体取决于how many levels of indirection are immutable

但是,编译器(gcc,clang)会生成警告。

我的问题:如何在不生成警告的情况下将指针(例如char **p)作为“只读”指针传递给函数?如果需要显式强制转换,为什么是char **p而不是char *p

更多详细信息

这是我要实现的目标的具体示例。

只读指针

此代码将char *ptr视为只读指针。

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

在函数调用中添加了限定词const

指向指针的只读指针

我希望可以使用指向指针的指针进行类似的函数调用。

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

clang编译器(版本6.0.0)给出了最容易理解的警告。

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)

但是gcc(8.1.1)也给出了警告。

在旁边:当我尝试添加时,c声说通过char ** 丢弃限定词似乎很奇怪资格赛?

问题

  1. 如何在不生成警告的情况下将指针(例如char **p)作为“只读”指针传递给函数?

  2. 如果需要显式强制转换,为什么是char **p而不是char *p

4 个答案:

答案 0 :(得分:1)

您的函数 readonly(const char *ptr) 承诺它不会触及指针后面的内容。
这就是调用者可以信任地将指针传递到常量内存区域(传递 const char *ptr)的原因。
当然,将非常量 char *ptr 传递给该函数也没有问题,因为您的函数承诺比需要的安全性更高。这就是为什么编译器在没有任何警告或注释的情况下遵循您的愿望。

然而,这种自动机制仅适用于第一间接层。

The Standard 在 6.5.16.1 中声明,“两个操作数都是指向兼容类型的限定或非限定版本的指针,左侧指向的类型具有所有 右侧所指类型的限定符"
句子的最后一部分意味着在指向的类型中添加限定符是没有问题的。
第一部分声称“兼容”类型。并且(我认为)6.7.3 (11) 确实针对限定类型描述了这一点:“要使两个限定类型兼容,两者都应具有兼容类型的相同限定版本。

读到这里,您的指向类型不被认为是兼容的(即使可以将一个分配给另一个)。

因此我会说关于丢弃限定符的clang警告有点误导,但它指的是非相同限定的指向类型。

答案 1 :(得分:0)

您可以将代码简化为:

char const ** var = (char**)0;

第二个事实。这不是一个错误。下面是测试用例: https://github.com/llvm/llvm-project/blob/62ec4ac90738a5f2d209ed28c822223e58aaaeb7/clang/test/Sema/pointer-conversion.c

再举一个例子:

char const*****  c1 = (char*****)0; // fail
char *const****  c2 = (char*****)0; // fail
char **const***  c3 = (char*****)0; // fail
char ***const**  c4 = (char*****)0; // fail
char ****const*  c5 = (char*****)0; // ok
char *****const  c6 = (char*****)0; // ok

好。我们看到了模式。

我猜 C/C++ 不能保证你可以修改深度大于 2 的任何东西,因为常量正确性存在一些问题,这在 here 之前讨论过。

答案 2 :(得分:0)

在 GCC 中,您可以使用以下命令禁用代码中的警告:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
readonly(ptr);
#pragma GCC diagnostic pop

我想 clang 有类似的东西。至于你的第二个问题,评论里好像有。

答案 3 :(得分:-1)

我知道您想声明指向const chat的const指针,类似于指向const char的指针。声明是:

void foo( const char ** const pointer )

由于恒星从相反方向开始计数,因此有点“诡异”。

让我们考虑一个非常简单的例子

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

void foo( const char ** const pointer )
{
    while(**pointer)
    {
        printf("%c", **pointer);
        (*pointer)++;
    }
    printf("\n");
}

void foo1( const char *const * pointer)
{
    while(*pointer)
    {
        printf("%s\n", *pointer++);
    }
}


int main(void) 
{
    char **pointer = malloc(sizeof(char *));
    char **pointer1 = malloc(sizeof(char *) * 4);

    char strings_in_RAM_0[] = "one";
    char strings_in_RAM_1[] = "two";
    char strings_in_RAM_2[] = "three";

    pointer1[0] = strings_in_RAM_0;
    pointer1[1] = strings_in_RAM_1;
    pointer1[2] = strings_in_RAM_2;
    pointer1[3] = NULL;

    *pointer = malloc(50);

    strcpy(*pointer,"Const pointer to pointer");

    foo(pointer);
    foo1(pointer1);

    free(*pointer);
    free(pointer);
    free(pointer1);

    return 0;
}

它会给您警告,但在免费之前它将按预期运行。然后(由于更改了指针),释放失败。 为了避免这种情况,您需要将原始指针保存为char例如:

void foo( const char ** const pointer )
{
    const char *ptr = *pointer;
    while(**pointer)
    {
        printf("%c", **pointer);
        (*pointer)++;
    }
    printf("\n");
    *pointer = ptr;
}

foo1函数参数的类型为const指针,而const指针为const char

https://ideone.com/JSewN4