使用const成员将结构转换为结构

时间:2013-12-18 13:58:21

标签: c pointers struct const c99

我有一个结构定义,只在声明它的.c文件中可见。

struct private
{
    int n ;
    void* data ;

    int field ;
}

访问成员的唯一方法是在同一个文件中定义并在标题中声明的函数。

我在一个随处可见的标题中声明了一个结构

struct public
{
    int n ;
    void* data ;
}

然后我有一个函数返回实际上是私有struct casted的公共结构

struct public* this = GetPrivateStruct() ;    //function returns pointer to struct private malloced internally, casted to public struct 
this->n = 123 ;

到目前为止,代码是正确的,没有未定义的行为。

但是我可以使用const成员来创建公共结构吗?

struct public
{
    const int n ;
    const void* data ;
}

所以只允许阅读:

void* private_struct = GetPrivateStruct() ;
struct public* this = ( struct public* )private_struct ;
this->n = 123 ;   //<-- this will now give an error which is fine as it is not allowed
int n = this->n ; //we can only read the value

4 个答案:

答案 0 :(得分:5)

intconst int不兼容类型(6.7.3p10),因此您无法将公共初始序列规则(6.5.2.3p6)应用于{{1 }};也就是说,这不是你正在做的事情,所以你必须依赖左值转换规则(6.3.2.1p2),它允许剥离一层类型限定,但前提是左值引用了适当类型的对象。

请注意,从6.2.5p26和6.7.2.1p15我们可以推断出两个union具有相同的布局(对于公共元素),但它并不表示您所做的是合法的。正如在"Private" struct members in C with const中所讨论的那样,关键是优化器(在对用户代码进行操作时)会注意到struct的成员是struct public并推断他们无法在任何地方更改 包括实现的(成员)函数。

但是,如果您很乐意相信用户不构造const,那么可以信任他们不构造this->n,那么为什么不给他们一个指向this - 合格的对象?您甚至可以const public typedef:

const

此外,以一些额外字符为代价重用typedef const struct initial { int n; void *data; } public; 布局是有意义的:

initial

您现在可以向用户struct private { struct initial i; int field; }; 提供&this->iinitial是兼容类型的合格版本,您甚至不需要投射任何内容(尽管可以通过6.7.2.1) P15)。

答案 1 :(得分:0)

你应该可以,但这很奇怪。您确实应该避免必须维护相同数据结构的2个定义的情况。

此外,如果您执行任何类似结构成员的操作,您将获得“未定义的行为”。听起来你更喜欢使用C ++ !!

在我使用指向私人信息的指针之前在C中做类似的事情时,如下所示:

struct _my_public_struct
{
int stuff
void *pMyPrivateData;
};

创建结构时,我将另一个结构分配给私有数据

struct _my_public_struct ss = _malloc(sizeof(struct _my_public_struct));
ss->pMyPrivateData = _malloc(sizeof(struct _my_private_struct));

此处_my_private_struct只会包含您的私人数据,而不是公共内容,您可以根据自己的内容进行更改。

答案 2 :(得分:0)

我已经尝试使用符合C99标准的Pelles C,并以您期望的方式工作:尝试编译此文件:

struct public
{
    const int n;
    const void* data;
    const int field;
};

void *GetPrivateStruct (int n, int field);
void PrintPrivateStruct (void *p);

int main()
{
    struct public *p;

    p = (struct public *)GetPrivateStruct (1,2);
    PrintPrivateStruct (p);
    p->n = 3;
    PrintPrivateStruct (p);
}

p->n = 3;

的行中抛出此错误
main.c(17): error #2033: Assignment to const location.

请注意,因为绕过const修饰符很容易:

void swap (int *a, int *b)
{
    int t;

    t = *a;
    *a = *b;
    *b = t;
}

int main()
{
    struct public *p;

    p = (struct public *)GetPrivateStruct (1,2);
    PrintPrivateStruct (p);
    swap ((int *)&p->field, (int *)&p->n);
    PrintPrivateStruct (p);
}

所以:如果你显示结构的内部字段,那么总有一个人可以绕过const强加的只读行为并执行对这些字段的实际写入。唯一受const关键字保护的数据是那些在声明期间已初始化的全局变量,并且已存储在只读存储器区域中,受操作系统保护,或者在嵌入式系统,受存储器设备无法接受,以接受来自处理器的写入。

我从Borland C ++学到的一个功能已被移植到C#中,如果它被实现,这将在这里证明非常有用:accessorsmutators。基本上,您的字段是私有的(用户不知道),并且这些字段的公共别名是用户已知的,因此当您对公共别名进行分配时,实际上是在调用私有函数{{1} },负责更新私人领域,作为其自由裁量权。当您在表达式中使用公共字段时,实际上是在调用另一个函数mutator,该函数检索私有字段值并将其传递给表达式。如果你为一个字段而不是一个mutator编写一个访问器,那么你实际上只是为所有意图和目的读取该字段。

我们可以模仿这个功能,如果没有相同的sintax,至少,足够清楚,不要乱码。我的提议是这样的:

不要记录结构的任何公共版本。所有字段都将保密。

记录您的结构将具有的公共字段。从私有版本(别名)为它们分配不同的名称。

对于每个字段,编写一个这样的函数(例如,对于你要写的别名字段accessorn):

field

编写一个将作为访问者的宏:

int inline __get_field_n (void *p)
{
  return ((struct private *)(p))->__private_n;
}

int inline __get_field_field (void *p)
{
  return ((struct private *)(p))->__private_field;
}

此宏将用于访问指向结构的有效指针中的任何字段。它是写的,所以你可以做这样的事情:

#define GET(pstr,fld) (__get_field_##fld (pstr))

答案 3 :(得分:0)

考虑这种结构:

struct element1 {
    int a;
    int b;
    char c;
};

声明此结构仅定义程序存储结构数据的方式以及如何操作它。因此,声明该结构类型的变量允许以这种方式存储元素:

------------------------------
| 4 bytes | 4 bytes | 1 byte |
------------------------------
|    a    |    b    |    c   |
------------------------------

但是,您无法使用常量字段定义公共结构,因为它们不是兼容类型。相反,您可以将整个结构定义为常量。

const struct element2 {
    int a;
    int b;
};

通过将public structure声明为constantpointer到该结构,然后向casted pointer添加private structure只是first一个指向adresse指向的second

数据所指向的方式将取决于公共结构的排除方式。

结论:从公共结构的定义我们可以假设:

1)原始结构的最后一个字段(char c)不能由公共结构处理,因为它没有在其中定义。

2)由于公共结构被定义为constante,因此无法修改结构的字段。

NB :定义公共结构时,请确保匹配字段的类型和顺序(无论您要忽略哪些字段,但不能忽略中间的字段)因为铸件与它们不匹配。如果字段未正确匹配http://ideone.com/A41vn7,则此示例显示冲突。