在C中,const char *p
有时被称为“只读”指针:指向常量对象(在这种情况下为char
)的指针。
似乎也是
const char **p
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 **
丢弃限定词似乎很奇怪资格赛?
如何在不生成警告的情况下将指针(例如char **p
)作为“只读”指针传递给函数?
如果需要显式强制转换,为什么是char **p
而不是char *p
?
答案 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