我有一个结构定义,只在声明它的.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
答案 0 :(得分:5)
int
和const 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->i
和initial
是兼容类型的合格版本,您甚至不需要投射任何内容(尽管可以通过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#中,如果它被实现,这将在这里证明非常有用:accessors
和mutators
。基本上,您的字段是私有的(用户不知道),并且这些字段的公共别名是用户已知的,因此当您对公共别名进行分配时,实际上是在调用私有函数{{1} },负责更新私人领域,作为其自由裁量权。当您在表达式中使用公共字段时,实际上是在调用另一个函数mutator
,该函数检索私有字段值并将其传递给表达式。如果你为一个字段而不是一个mutator编写一个访问器,那么你实际上只是为所有意图和目的读取该字段。
我们可以模仿这个功能,如果没有相同的sintax,至少,足够清楚,不要乱码。我的提议是这样的:
不要记录结构的任何公共版本。所有字段都将保密。
记录您的结构将具有的公共字段。从私有版本(别名)为它们分配不同的名称。
对于每个字段,编写一个这样的函数(例如,对于你要写的别名字段accessor
和n
):
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
声明为constant
和pointer
到该结构,然后向casted pointer
添加private structure
只是first
一个指向adresse
指向的second
。
数据所指向的方式将取决于公共结构的排除方式。
结论:从公共结构的定义我们可以假设:
1)原始结构的最后一个字段(char c)不能由公共结构处理,因为它没有在其中定义。
2)由于公共结构被定义为constante,因此无法修改结构的字段。
NB :定义公共结构时,请确保匹配字段的类型和顺序(无论您要忽略哪些字段,但不能忽略中间的字段)因为铸件与它们不匹配。如果字段未正确匹配http://ideone.com/A41vn7,则此示例显示冲突。