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

时间:2018-05-03 22:48:32

标签: c arrays pointers const language-lawyer

在C模块(又名编译单元)中,我希望拥有一些私有数据,但将它只读暴露给外界。我通过在我的struct文件中声明的.c中的字段和在我的.h文件中声明的函数来实现这一点,该函数返回指向const 的指针领域。例如,对于字符串,它可能如下所示:

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

现在我的问题是,我有一个对象数组,它们本身就是数组。所以在我的struct中,我有一个指向数组的指针,从我的函数中,我尝试返回指向相应const数组的指针。请考虑以下示例代码:

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

使用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;
            ~^~~~~~~~

我理解双指针指向const 的双指针不兼容,以及它的原因,但我不认为这是相关的,或者是吗?那么,为什么不允许将指向数组的指针隐式转换为指向const数组的指针?还有什么想法我应该做什么呢?

我现在所做的是添加一个这样的显式演员:

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

这当然使编译器沉默,我几乎可以肯定它在实践中总能正常工作。在这种情况下,“const hole”不存在,就像数组一样,没有非const内部指针。但它仍然留下了两个问题:

  • 我的假设是否正确,这不会导致const正确性漏洞?
  • 显式演员是否违反了标准?

4 个答案:

答案 0 :(得分:2)

这确实是你在问题中引用的双指针问题。

您可以将指针转换为指针指向const-T。但是应用于数组的const限定了元素类型,而不是数组类型(C11 6.7.3.9),因此这不是你在这里尝试做的事情。您并没有尝试将指针指向T的指针转换为T指针指向const-array,而是尝试将指针转换为指向const-T的指针,以及那些C中不兼容两种类型。

答案 1 :(得分:2)

问题是,通过typedef:一个数组然后NFA.java - 限定一个指向该类型的指针,你实际上得到一个const,它与const uint8_t(*)[64] 1)。 Const正确性和数组指针一起表现得很笨拙,see this表示相同问题。

无论如何,在这个特定情况下问题的根源是隐藏一个typedef后面的数组。这通常不是一个好主意。您可以通过将数组包装在结构中来解决此问题,这也可能提供更好的整体设计。例如:

uint8_t(*)[64]

现在你可以退回typedef struct shape { uint8_t shape[64]; } shape_t; typedef struct foo { shape_t shapes; } foo_t; 就好了。

现在,您可以选择将const shape_t*设为不透明类型,就像shape_t一样。或者你可以通过公共头文件shape.h中的struct声明来公开foo_t public的内部。

1)指向类型的指针和限定指针类型之间的隐式转换是唯一允许的隐式转换。

C11 6.3.2.3/2

  

对于任何限定符q,指向非q限定类型的指针可以转换为指向的指针   该类型的q限定版本;存储在原始和转换指针中的值   应比较平等。

这不适用于此处。为了使转换成为可能,它必须是从指针到数组类型到指向限定数组类型的转换。

但事实并非如此,它是从指针到数组类型到限定指针到数组类型的转换。

C中兼容类型的规范性文本是第6.2.7章,其仅涉及6.7.3。相关部分:

C11 6.7.3 / 9

  

如果数组类型的规范包含任何类型限定符,则元素类型是限定的,而不是数组类型。

和C11 6.7.3 / 10

  

要兼容两种合格类型,两者都应具有相同的合格版本   兼容类型

这就是gcc正确发出诊断消息的原因 - 指针不是相同的限定版本。

答案 2 :(得分:1)

我不完全确定这是否可以被认为是一个的想法,但有时当我感到懒惰时我会做的是定义一个“const cast”宏,如:

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

这当然假设您有一个支持typeof扩展名的编译器。使用这个(可疑的)构造,然后你可以写

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

否则,C通常不会隐式地转换为const。你必须显式地将指针转换为const指针。

答案 3 :(得分:1)

  

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

是的,它们不兼容,您无法更改嵌套的const限定符,int (*a)[42]int const (*b)[42] related不兼容,因为double **与{double const **不兼容1}}

  

我的假设是否正确,这不会导致const正确性漏洞?

嗯,你正在添加const限定符,所以没有。实际上,返回非const不会产生警告,也不会根据不允许添加const的相同规则来破坏const正确性。但是这样的代码非常混乱,但并不是未定义的行为。

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

显式演员是否违反了标准?

Strictly,是的,因为编译器说类型是不兼容的,但我不认为它会在任何实际的经典硬件中产生错误。

事情是标准,不保证double *adouble const *b具有相同的大小或甚至相同的值,但只保证a == b将产生正值。您可以将double *推广到double const *,但就像我说必须推广它一样。当你强制转换时,你不会提升任何东西,因为数组/指针是嵌套的。所以它是未定义的行为。

这是“你根本无法在C中做到”,你可以问,但“我想做什么?”。好吧,我没有看到你的结构的目的既不是你的结构中的数组指针。要解决数据结构的更深层次问题,您应该问另一个问题,在那里您可以更多地讨论使用此代码的目的是什么。例如,您的函数可以执行类似的操作:

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