为什么我不能转换“char**” 到一个“const char* const*” 在C语言中?

发布于 2024-07-04 19:26:47 字数 903 浏览 6 评论 0原文

以下代码片段(正确地)在 C 中给出警告,在 C++ 中给出错误(分别使用 gcc 和 g++,使用版本 3.4.5 和 4.2.1 进行测试;MSVC 似乎并不关心):

char **a;
const char** b = a;

我可以理解并接受这一点.
此问题的 C++ 解决方案是将 b 更改为 const char * const *,这不允许重新分配指针并阻止您规避 const 正确性(C++ 常见问题解答)。

char **a;
const char* const* b = a;

但是,在纯C,更正后的版本(使用const char * const *)仍然给出警告,我不明白为什么。 有没有办法在不使用演员的情况下解决这个问题?

澄清一下:

  1. 为什么这会在 C 中生成警告? 它应该是完全常量安全的,并且 C++ 编译器似乎也能识别它。

  2. 在说时接受此 char** 作为参数的正确方法是什么(并让编译器强制执行)我不会修改它指向的字符? 例如,如果我想编写一个函数:

     void f(const char* const* in) { 
         // 只从in读取数据,不写入 
       } 
      

并且我想在 char** 上调用它,参数的正确类型是什么?

The following code snippet (correctly) gives a warning in C and an error in C++ (using gcc & g++ respectively, tested with versions 3.4.5 and 4.2.1; MSVC does not seem to care):

char **a;
const char** b = a;

I can understand and accept this.
The C++ solution to this problem is to change b to be a const char * const *, which disallows reassignment of the pointers and prevents you from circumventing const-correctness (C++ FAQ).

char **a;
const char* const* b = a;

However, in pure C, the corrected version (using const char * const *) still gives a warning, and I don't understand why.
Is there a way to get around this without using a cast?

To clarify:

  1. Why does this generate a warning in C? It should be entirely const-safe, and the C++ compiler seems to recognize it as such.

  2. What is the correct way to go about accepting this char** as a parameter while saying (and having the compiler enforce) that I will not be modifying the characters it points to?
    For example, if I wanted to write a function:

     void f(const char* const* in) {
       // Only reads the data from in, does not write to it
     }
    

And I wanted to invoke it on a char**, what would be the correct type for the parameter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

世俗缘 2024-07-11 19:26:47

我很确定 const 关键字并不意味着数据不能更改/是恒定的,只是数据将被视为只读。 考虑一下:

const volatile int *const serial_port = SERIAL_PORT;

这是有效的代码。 易失性和常量如何共存? 简单的。 易失性告诉编译器在使用数据时始终读取内存,而const告诉编译器在尝试使用serial_port指针写入内存时创建错误。

const 对编译器的优化器有帮助吗? 一点都不。 因为常量可以通过转换添加到数据中或从数据中删除,所以编译器无法确定常量数据是否真的是常量(因为转换可以在不同的翻译单元中完成)。 在 C++ 中,您还可以使用 mutable 关键字来使问题进一步复杂化。

char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000

标准中可能根本没有定义当尝试写入真正只读的内存(例如 ROM)时会发生什么。

I'm pretty sure that the const keyword does not imply the data can't be changed/is constant, only that the data will be treated as read-only. Consider this:

const volatile int *const serial_port = SERIAL_PORT;

which is valid code. How can volatile and const co-exist? Simple. volatile tells the compiler to always read the memory when using the data and const tells the compiler to create an error when an attempt is made to write to the memory using the serial_port pointer.

Does const help the compiler's optimiser? No. Not at all. Because constness can be added to and removed from data through casting, the compiler cannot figure out if const data really is constant (since the cast could be done in a different translation unit). In C++ you also have the mutable keyword to complicate matters further.

char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000

What happens when an attempt is made to write to memory that really is read only (ROM, for example) probably isn't defined in the standard at all.

灰色世界里的红玫瑰 2024-07-11 19:26:47

将 char** 隐式转换为 const char * const * 时,至少在 MSVC 14 (VS2k5) 和 g++ 3.3.3 上,我无法收到错误。 GCC 3.3.3 发出警告,我不确定这样做是否正确。

test.c:

#include <stdlib.h> 
#include <stdio.h>
void foo(const char * const * bar)
{
    printf("bar %s null\n", bar ? "is not" : "is");
}

int main(int argc, char **argv) 
{
    char **x = NULL; 
    const char* const*y = x;
    foo(x);
    foo(y);
    return 0; 
}

编译为 C 代码时输出: cl /TC /W4 /Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

编译为 C++ 代码时输出: cl /TP /W4 /Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

使用 gcc 输出: gcc -Wall test.c

test2.c: In function `main':
test2.c:11: warning: initialization from incompatible pointer type
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type

输出为g++: g++ -Wall test.C

无输出

I'm not able to get an error when implicitly casting char** to const char * const *, at least on MSVC 14 (VS2k5) and g++ 3.3.3. GCC 3.3.3 issues a warning, which I'm not exactly sure if it is correct in doing.

test.c:

#include <stdlib.h> 
#include <stdio.h>
void foo(const char * const * bar)
{
    printf("bar %s null\n", bar ? "is not" : "is");
}

int main(int argc, char **argv) 
{
    char **x = NULL; 
    const char* const*y = x;
    foo(x);
    foo(y);
    return 0; 
}

Output with compile as C code: cl /TC /W4 /Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

Output with compile as C++ code: cl /TP /W4 /Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

Output with gcc: gcc -Wall test.c

test2.c: In function `main':
test2.c:11: warning: initialization from incompatible pointer type
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type

Output with g++: g++ -Wall test.C

no output

夜夜流光相皎洁 2024-07-11 19:26:47

这很烦人,但如果您愿意添加另一个级别的重定向,通常可以执行以下操作来下推到指针到指针:

char c = 'c';
char *p = &c;
char **a = &p;

const char *bi = *a;
const char * const * b = &bi;

它的含义略有不同,但通常是可行的,而且它并不适用。不要使用强制转换。

This is annoying, but if you're willing to add another level of redirection, you can often do the following to push down into the pointer-to-pointer:

char c = 'c';
char *p = &c;
char **a = &p;

const char *bi = *a;
const char * const * b = &bi;

It has a slightly different meaning, but it's usually workable, and it doesn't use a cast.

提赋 2024-07-11 19:26:47

为了被认为是兼容的,源指针在紧接前面的间接级别中应该是 const。 因此,这将在 GCC 中向您发出警告:

char **a;
const char* const* b = a;

但这不会: 或者

const char **a;
const char* const* b = a;

,您可以对其进行强制转换:

char **a;
const char* const* b = (const char **)a;

您将需要相同的强制转换来调用您提到的函数 f() 。 据我所知,在这种情况下没有办法进行隐式转换(C++ 除外)。

To be considered compatible, the source pointer should be const in the immediately anterior indirection level. So, this will give you the warning in GCC:

char **a;
const char* const* b = a;

But this won't:

const char **a;
const char* const* b = a;

Alternatively, you can cast it:

char **a;
const char* const* b = (const char **)a;

You would need the same cast to invoke the function f() as you mentioned. As far as I know, there's no way to make an implicit conversion in this case (except in C++).

茶底世界 2024-07-11 19:26:47

但是,在纯C中,这仍然会发出警告,我不明白为什么

你已经发现了问题——这段代码不是常量正确的。 “Const 正确”意味着,除了 const_cast 和删除 const 的 C 风格转换之外,您永远不能通过这些 const 指针修改 const 对象或参考文献。

const 的值 - 正确性 - const 的存在很大程度上是为了检测程序员的错误。 如果您将某些内容声明为 const,则表明您认为不应修改它——或者至少,那些有权访问 const 版本的人应该修改它无法修改它。 考虑一下:

void foo(const int*);

正如声明的那样,foo 没有权限修改其参数指向的整数。

如果您不确定为什么您发布的代码不是 const 正确的,请考虑以下代码,它与 HappyDude 的代码仅略有不同:

char *y;

char **a = &y; // a points to y
const char **b = a; // now b also points to y

// const protection has been violated, because:

const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it 
         //     with &x which is const char* ..
         //     ..  so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const 
         //     variable.  oops!  undefined behavior!
cout << x << endl;

const 类型只能以特定方式转换为 const 类型,以防止在没有显式强制转换的情况下在数据类型上规避 const。

最初声明为 const 的对象特别特殊——编译器可以假设它们永远不会改变。 但是,如果可以在不进行强制转换的情况下为 b 分配 a 的值,那么您可能会无意中尝试修改 const 变量。 这不仅会破坏您要求编译器进行的检查,禁止您更改该变量值 - 它还会让您破坏编译器优化!

在某些编译器上,这将打印 42,在某些 43 上,而在其他编译器上,程序将崩溃。

编辑-添加:

HappyDude:您的评论很正确。 C 语言或您使用的 C 编译器对待 const char * const * 的方式与 C++ 语言对待它的方式根本不同。 也许考虑仅对此源代码行静音编译器警告。

However, in pure C, this still gives a warning, and I don't understand why

You've already identified the problem -- this code is not const-correct. "Const correct" means that, except for const_cast and C-style casts removing const, you can never modify a const object through those const pointers or references.

The value of const-correctness -- const is there, in large part, to detect programmer errors. If you declare something as const, you're stating that you don't think it should be modified -- or at least, those with access to the const version only should not be able to modifying it. Consider:

void foo(const int*);

As declared, foo doesn't have permission to modify the integer pointed to by its argument.

If you're not sure why the code you posted isn't const-correct, consider the following code, only slightly different from HappyDude's code:

char *y;

char **a = &y; // a points to y
const char **b = a; // now b also points to y

// const protection has been violated, because:

const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it 
         //     with &x which is const char* ..
         //     ..  so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const 
         //     variable.  oops!  undefined behavior!
cout << x << endl;

Non-const types can only convert to const types in particular ways to prevent any circumvention of const on a data-type without an explicit cast.

Objects initially declared const are particularly special -- the compiler can assume they never change. However, if b can be assigned the value of a without a cast, then you could inadvertently attempt to modify a const variable. This would not only break the check you asked the compiler to make, to disallow you from changing that variables value -- it would also allow you break the compiler optimizations!

On some compilers, this will print 42, on some 43, and others, the program will crash.

Edit-add:

HappyDude: Your comment is spot on. Either the C langauge, or the C compiler you're using, treats const char * const * fundamentally differently than the C++ language treats it. Perhaps consider silencing the compiler warning for this source line only.

帅哥哥的热头脑 2024-07-11 19:26:47

几年前我也遇到过同样的问题,这让我一直烦恼不已。

C 中的规则表述更为简单(即,它们没有列出将 char** 转换为 const char*const* 等异常情况)。 结果就是,这是不允许的。 在 C++ 标准中,他们包含了更多规则来允许此类情况。

归根结底,这只是C标准的问题。 我希望下一个标准(或技术报告)能够解决这个问题。

I had this same problem a few years ago and it irked me to no end.

The rules in C are more simply stated (i.e. they don't list exceptions like converting char** to const char*const*). Consequenlty, it's just not allowed. With the C++ standard, they included more rules to allow cases like this.

In the end, it's just a problem in the C standard. I hope the next standard (or technical report) will address this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文