简体   繁体   English

指向数组的指针与指向'const'数组的指针不兼容?

[英]pointer to array not compatible to a pointer to 'const' array?

In a C module (aka, compilation unit), I want to have some private data, but expose it read-only to the outside world. 在C模块(又名编译单元)中,我想要一些私有数据,但是向外界公开它。 I achieve that by having a field in a struct declared in my .c file and a function declared in my .h file that returns a pointer to const to that field. 我通过在我的.c文件中声明的struct中的字段和在我的.h文件中声明的函数来实现它,该函数返回指向该字段的const指针 For example, this could look like the following for a string : 例如,对于字符串 ,这可能如下所示:

// header:

typdef struct foo foo;

const char *foostr(const foo *f);

// implementation:

struct foo
{
    char *str;
};

const char *foostr(const foo *f)
{
    return foo->str;
}

Now my problem is, I have an array of objects that are themselves arrays. 现在我的问题是,我有一个对象数组本身就是数组。 So in my struct , I have a pointer to an array, and from my function, I try to return a pointer to the corresponding const array. 所以在我的struct ,我有一个指向数组的指针,从我的函数中,我尝试返回一个指向相应const数组的指针。 Consider the following example code: 请考虑以下示例代码:

#include <stdlib.h>
#include <stdint.h>

typedef uint8_t shape[64];

typedef struct foo
{
    shape *shapes;
} foo;


foo *createfoo(void)
{
    foo *f = malloc(sizeof *f);
    if (!f) return 0;

    // 10 empty shapes:
    f->shapes = calloc(10, sizeof *(f->shapes));

    if (!f->shapes)
    {
        free(f);
        return 0;
    }

    return f;
}

const shape *fooshapes(const foo *f)
{
    return f->shapes;
}

Compiling this with gcc -c -std=c11 -Wall -Wextra -pedantic , I get the following warning: gcc -c -std=c11 -Wall -Wextra -pedantic编译它,我收到以下警告:

constarr.c: In function ‘fooshapes’:
constarr.c:31:13: warning: pointers to arrays with different qualifiers are incompatible in ISO C [-Wpedantic]
     return f->shapes;
            ~^~~~~~~~

I understand a double pointer isn't compatible to a double pointer to const , and also the reason for it, but I don't think this is related, or is it? 我理解双指针与const双指针不兼容,也是它的原因,但我不认为这是相关的,或者是它? So, why isn't it allowed to implicitly convert a pointer to an array to a pointer to a const array? 那么,为什么不允许将指向数组的指针隐式转换为指向const数组的指针呢? And any ideas what I should do instead? 还有什么想法我应该做什么呢?

What I did now is adding an explicit cast like this: 我现在做的是添加一个这样的显式演员:

const shape *fooshapes(const foo *f)
{
    return (const shape *) f->shapes;
}

This of course silences the compiler and I am almost sure it will always work correctly in practice. 这当然使编译器沉默,我几乎可以肯定它在实践中总能正常工作。 The "const hole" can't exist in this case, as with an array, there is no non-const inner pointer. 在这种情况下,“const hole”不存在,就像数组一样,没有非const内部指针。 But it still leaves me with two further questions: 但它还有两个问题:

  • Is my assumption correct that this doesn't lead to a hole in const correctness? 我的假设是否正确,这不会导致const正确性漏洞?
  • Does the explicit cast violate the standard here? 明确的演员是否违反了标准?

It is indeed the same double pointer issue you refer to in the question. 它确实是你在问题中提到的双指针问题。

You can convert pointer-to-T to pointer-to-const-T. 您可以将指针指向T转换为指向const-T的指针。 But const applied to an array qualifies the element type, not the array type (C11 6.7.3.9), so that's not what you're trying to do here. 但应用于数组的const限定了元素类型,而不是数组类型(C11 6.7.3.9),所以这不是你想要在这里做的。 You're not trying to convert pointer-to-array-of-T to pointer-to-const-array-of T, but rather trying to convert to pointer-to-array-of-const-T, and those two types are not compatible in C. 你不是试图将指向数组的指针转换为T的指针到const数组,而是试图将指针转换为指向const-T的数组,以及这两种类型在C中不兼容

The issue is that by typedef:ing an array and then const -qualifying a pointer to that type, you actually get an const uint8_t(*)[64] , which is not compatible with uint8_t(*)[64] 1) . 问题是通过typedef:一个数组然后const -qualifying一个指向该类型的指针,实际上你得到一个const uint8_t(*)[64] ,它与uint8_t(*)[64] 1)不兼容。 Const correctness and array pointers behave awkwardly together, see this for an example of the same issue. const正确性和数组指针表现得很笨拙, 请参阅此示例以了解相同问题。

Anyway, the root of the problem in this specific case is hiding an array behind a typedef. 无论如何,在这种特定情况下问题的根源是将数组隐藏在typedef后面。 This is usually not a good idea. 这通常不是一个好主意。 You can fix this by wrapping the array inside a struct instead, which might also give a better design overall. 您可以通过将数组包装在结构中来解决此问题,这也可能提供更好的整体设计。 Example: 例:

typedef struct shape
{
  uint8_t shape[64];
} shape_t;

typedef struct foo
{
  shape_t shapes;
} foo_t;

Now you can return a const shape_t* just fine. 现在你可以返回一个const shape_t*就好了。

Optionally you can now either make shape_t an opaque type just like foo_t . 您可以选择将shape_t opaque类型,就像foo_t一样。 Or you can make the internals of shape_t public by for example exposing the struct declaration in a public header shape.h. 或者你可以通过例如在公共头shape.h中公开struct声明来使shape_t公开的内部结构。


1) Implicit conversion between a pointer-to-type and a qualified-pointer-to-type is the only allowed implicit conversion. 1)指向类型的指针和限定指针类型之间的隐式转换是唯一允许的隐式转换。

C11 6.3.2.3/2 C11 6.3.2.3/2

For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; 对于任何限定符q,指向非q限定类型的指针可以转换为指向该类型的q限定版本的指针; the values stored in the original and converted pointers shall compare equal. 存储在原始指针和转换指针中的值应相等。

This does not apply here. 这不适用于此。 For the conversion to be ok, it would have to be a conversion from pointer-to-array-type to pointer-to-qualified-array-type. 为了使转换成为可能,它必须是从指针到数组类型到指向限定数组类型的转换。

But it is not, it is a conversion from pointer to-array-type to qualified-pointer-to-array-type. 但事实并非如此,它是从指针到数组类型到限定指针到数组类型的转换。

Normative text for compatible types in C is chapter 6.2.7, which only references further to 6.7.3. C中兼容类型的规范性文本是第6.2.7章,仅对6.7.3进一步引用。 Relevant parts: 相关部分:

C11 6.7.3/9 C11 6.7.3 / 9

If the specification of an array type includes any type qualifiers, the element type is so-qualified, not the array type. 如果数组类型的规范包含任何类型限定符,则元素类型是限定的,而不是数组类型。

and C11 6.7.3/10 和C11 6.7.3 / 10

For two qualified types to be compatible, both shall have the identically qualified version of a compatible type 要使两种合格类型兼容,两者都应具有相同类型的兼容类型

This is why gcc correctly issues a diagnostic message - the pointers are not identically qualified versions. 这就是gcc正确发出诊断消息的原因 - 指针不是相同的限定版本。

I am not entirely sure if this could be considered a good idea, but something that I do sometimes when I'm feeling lazy is to define a "const cast" macro like: 我不完全确定这是否可以被认为是一个主意,但有时当我感到懒惰时我做的是定义一个“const cast”宏,如:

#define CC(_VAR) ((const typeof(_VAR))(_VAR))

This of course assumes that you have a compiler that supports the typeof extension. 当然,这假设你有一个支持的编译器typeof扩展。 Using this (dubious) construction you could then write 使用这个(可疑的)构造,然后你可以写

const shape *fooshapes(const foo *f)
{
    return CC(f->shapes);
}

Otherwise, C doesn't generally cast to const implicitly. 否则,C通常不会隐式地转换为const。 You do have to explicitly cast pointers to const pointers. 您必须显式地将指针转换为const指针。

pointer to array not compatible to a pointer to 'const' array? 指向数组的指针与指向'const'数组的指针不兼容?

Yes they are incompatible, you can't change a nested const qualifier, int (*a)[42] is not compatible with int const (*b)[42] related , as double ** is not compatible with double const ** 是的,他们是不兼容的,你不能改变嵌套const属性, int (*a)[42]是不兼容int const (*b)[42] 有关 ,如double **不兼容double const **

Is my assumption correct that this doesn't lead to a hole in const correctness? 我的假设是否正确,这不会导致const正确性漏洞?

Well, you are adding const qualifier so no. 好吧,你正在添加const限定符,所以没有。 In fact, return non const don't produce a warning and don't break const correctness according to the same rule that doesn't allow you to add const. 实际上,返回非const不会产生警告,也不会根据不允许添加const的相同规则来破坏const正确性。 But code like that are very confuse but are not undefined behavior. 但是这样的代码非常混乱,但不是未定义的行为。

shape *fooshapes(const foo *f)
{
    return f->shapes;
}

Does the explicit cast violate the standard here? 明确的演员是否违反了标准?

Strictly , Yes, as compiler said there types are incompatibles, however I don't think it would produce a bug in any actual classic hardware. 严格地说 ,是的,因为编译器说类型是不兼容的,但我不认为它会在任何实际的经典硬件中产生错误。

The thing is the standard don't guarantee that double *a and double const *b have the same size or even the same value but just guarantee that a == b will produce a positive value. 事情是标准不保证double *adouble const *b具有相同的大小或甚至相同的值,但只是保证a == b将产生正值。 You can promote any double * to double const * but like I said you must promote it. 你可以推广任何double *double const *但就像我说你必须推广它。 When you cast you don't promote anything because the array/pointer is nested. 当你强制转换时,你不会提升任何东西,因为数组/指针是嵌套的。 So it's undefined behavior. 所以它是未定义的行为。

It's a "you simply can't do that in C", you could ask but "what I'm suppose to do ?". 这是“你根本不能用C做的”,你可以问,但“我想做什么?”。 Well, I don't see the purpose of your structure neither of your pointer to array in the structure. 好吧,我没有看到你的结构的目的既不是你的结构中的数组指针。 To fix the deeper problem of your data structure, you should ask an other question where you talk more about what is your purpose with this code. 要解决数据结构的更深层次问题,您应该问另一个问题,在那里您可以更多地讨论使用此代码的目的是什么。 For example, your function could do something like that: 例如,您的函数可以执行以下操作:

uint8_t const *fooshapes(const foo *f, size_t i)
{
    return f->shapes[i];
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM