C 指向数组的指针与指向';常数';阵列?

C 指向数组的指针与指向';常数';阵列?,c,arrays,pointers,constants,language-lawyer,C,Arrays,Pointers,Constants,Language Lawyer,在C模块(又名编译单元)中,我希望有一些私有数据,但将其以只读方式公开给外部世界。我通过在.c文件中声明结构中的一个字段和在.h文件中声明的一个函数来实现这一点,该函数返回指向该字段的常量指针。例如,对于字符串,这可能类似于以下内容: 现在我的问题是,我有一个对象数组,它们本身就是数组。因此,在我的struct中,我有一个指向数组的指针,并且从我的函数中,我尝试返回一个指向相应const数组的指针。考虑下面的示例代码: #include <stdlib.h> #include <

在C模块(又名编译单元)中,我希望有一些私有数据,但将其以只读方式公开给外部世界。我通过在
.c
文件中声明
结构中的一个字段和在
.h
文件中声明的一个函数来实现这一点,该函数返回指向该字段的常量指针。例如,对于字符串,这可能类似于以下内容:

现在我的问题是,我有一个对象数组,它们本身就是数组。因此,在我的
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;
}
我知道双指针与指向const的双指针不兼容,也知道其原因,但我认为这两者没有关系,或者是吗?那么,为什么不允许将指向数组的指针隐式转换为指向
const
数组的指针呢?你知道我该怎么做吗

我现在做的是添加一个明确的演员阵容,如下所示:

const shape *fooshapes(const foo *f)
{
    return (const shape *) f->shapes;
}
这当然会使编译器静音,我几乎可以肯定它在实践中总是能正确工作的。在这种情况下,“const-hole”不可能存在,因为对于数组,没有非const内部指针。但这仍然给我留下了两个问题:

  • 我的假设是否正确,即这不会导致
    const
    正确性出现漏洞
  • 显式强制转换是否违反了此处的标准

我不完全确定这是否是一个好主意,但有时当我感到懒惰时,我会定义一个“const cast”宏,如:

#define CC(_VAR) ((const typeof(_VAR))(_VAR))
当然,这假设您有一个支持
typeof
扩展名的编译器。使用这种(可疑的)结构,您可以编写

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

否则,C通常不会隐式地强制转换为常量。您必须显式地将指针强制转换为常量指针。

这确实与您在问题中提到的双指针问题相同

您可以将pointer-to-T转换为pointer-to-const-T。但是应用于数组的
const
限定了元素类型,而不是数组类型(C11 6.7.3.9),因此这不是您在这里要做的。您没有尝试将指针到数组的T转换为指针到常量数组的T,而是尝试转换为指针到数组的常量T,这两种类型在C中不兼容

指向数组的指针与指向“const”数组的指针不兼容

是的,它们是不兼容的,您不能更改嵌套常量限定符,
int(*a)[42]
int const(*b)[42]
不兼容,因为
double**
double const**

我的假设正确吗?这不会导致常量正确性的漏洞

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

shape *fooshapes(const foo *f)
{
    return f->shapes;
}
显式强制转换是否违反了此处的标准

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

问题是,标准并不保证
double*a
double const*b
具有相同的大小或甚至相同的值,而是保证
a==b
将产生正值。您可以将任何
double*
升级为
double const*
,但正如我所说,您必须升级它。强制转换时,不会升级任何内容,因为数组/指针是嵌套的。所以这是未定义的行为

shape *fooshapes(const foo *f)
{
    return f->shapes;
}
这是一个“在C语言中你根本不能这么做”,你可以问“我该怎么做?”。我看不出你的结构的用途,也看不到你的指向结构中数组的指针。要解决数据结构的深层次问题,您应该问另一个问题,在这里您可以更多地讨论使用此代码的目的是什么。例如,您的函数可以执行以下操作:

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

问题是,通过键入def:ing一个数组,然后限定指向该类型的指针,实际上会得到一个
const uint8\u t(*)[64]
,它与
uint8\u t(*)[64]
1)不兼容。常量正确性和数组指针一起表现得很尴尬,例如同一个问题

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

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

typedef struct foo
{
  shape_t shapes;
} foo_t;
现在您可以返回一个
常量shape\u t*
很好

您现在可以选择将
shape\u t
设置为不透明类型,就像
foo\u t
一样。或者,您可以将
shape\t
的内部公开,例如在公共头shape.h中公开struct声明


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

C116.3.2.3/2

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

这在这里不适用。要使转换正常,它必须是从指针到数组类型到指针到限定数组类型的转换

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

C中兼容类型的规范性文本为第6.2.7章,仅进一步参考第6.7.3章。相关部分:

C116.7.3/9

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

和C116.7.3/10

为了