从指针到其第二会员的指针获得指针是合法的吗?
我想知道该行之前的评论“ 该法律c? ”(在函数dumperverts()
的底部)是否是法律c:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
struct stvertex
{
double x;
double y;
char tag;
};
struct stmesh
{
size_t nverts;
struct stvertex verts[]; /* flexible array member */
};
void dumpverts(struct stvertex *ptr);
int main(int argc, char **argv)
{
size_t f;
size_t usr_nverts=5; /* this would come from the GUI */
struct stmesh *m = malloc(sizeof(struct stmesh) + usr_nverts*sizeof(struct stvertex));
if(m==NULL) return EXIT_FAILURE;
m->nverts=usr_nverts;
for(f=0;f<m->nverts;f++)
{
m->verts[f].x = f*10.0; /* dumb values just for testing */
m->verts[f].y = f*7.0;
m->verts[f].tag = 'V';
}
dumpverts( &(m->verts[0]) );
return EXIT_SUCCESS;
}
void dumpverts(struct stvertex *ptr) /* Here is were the juice is */
{
size_t f;
/* Is this legal C? */
struct stmesh *themesh = (struct stmesh *)((char *)ptr - offsetof(struct stmesh, verts));
for(f=0;f<themesh->nverts;f++)
{
printf("v[%zu] = (%g,%g) '%c'\n", f, themesh->verts[f].x, themesh->verts[f].y, themesh->verts[f].tag);
}
fflush(stdout);
}
i倾向于认为这是合法的,但是我不能100%确定严格的混叠规则是否会允许演员从char *
到struct stment *
喜欢在dumpverts()
功能主体正在执行。
基本上,该行从指针到其第二个成员的指针获取struct stmesh
的指针。我看不到任何与对齐相关的潜在问题,因为整个struct stmesh
的内存来自malloc()
,因此结构的开始是“适当对齐”。但是,正如我所说,我不确定严格的别名规则。
如果它破坏了严格的混叠,是否可以在不更改dumpverts()
函数的原型的情况下进行符合符合条件?
如果您想知道我想要什么,它主要用于学习Offsetof()
的限制。是的,我知道dumpverts()
应该收到指向struct stmesh
的指针。但是我想知道是否可以通过法律方式以编程方式获得struct stmesh
指针。
I'm wondering if the line preceded by the comment "Is this legal C?" (in the function dumpverts()
at the bottom) is legal C or not:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
struct stvertex
{
double x;
double y;
char tag;
};
struct stmesh
{
size_t nverts;
struct stvertex verts[]; /* flexible array member */
};
void dumpverts(struct stvertex *ptr);
int main(int argc, char **argv)
{
size_t f;
size_t usr_nverts=5; /* this would come from the GUI */
struct stmesh *m = malloc(sizeof(struct stmesh) + usr_nverts*sizeof(struct stvertex));
if(m==NULL) return EXIT_FAILURE;
m->nverts=usr_nverts;
for(f=0;f<m->nverts;f++)
{
m->verts[f].x = f*10.0; /* dumb values just for testing */
m->verts[f].y = f*7.0;
m->verts[f].tag = 'V';
}
dumpverts( &(m->verts[0]) );
return EXIT_SUCCESS;
}
void dumpverts(struct stvertex *ptr) /* Here is were the juice is */
{
size_t f;
/* Is this legal C? */
struct stmesh *themesh = (struct stmesh *)((char *)ptr - offsetof(struct stmesh, verts));
for(f=0;f<themesh->nverts;f++)
{
printf("v[%zu] = (%g,%g) '%c'\n", f, themesh->verts[f].x, themesh->verts[f].y, themesh->verts[f].tag);
}
fflush(stdout);
}
I tend to believe it's legal, but I'm not 100% sure if the strict aliasing rule would permit the cast from char *
to struct stmesh *
like the interesting line in the dumpverts()
function body is doing.
Basically, that line is obtaining the pointer to the struct stmesh
from the pointer to its second member. I don't see any alignment-related potential issues, because the memory for the whole struct stmesh
came from malloc()
, so the beginning of the struct is "suitably aligned". But I'm not sure about the strict aliasing rule, as I said.
If it breaks strict aliasing, can it be made compliant without changing the prototype of the dumpverts()
function?
If you wonder what I want this for, it's mainly for learning where are the limits of offsetof()
. Yes, I know dumpverts()
should be receiving a pointer to struct stmesh
instead. But I'm wondering if obtaining the struct stmesh
pointer programmatically would be possible in a legal way.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
是的,这是有效的。您可以将任何非功能指针转换为
char *
:标准的明确部分允许:C17,第6.3.2.3节,第7条:
允许的原因是,您可以像所显示的那样做技巧。但是,请注意,仅当指针从
struct stmesh
中出现时,这才有效(即使您在执行此操作时您没有该结构的范围)。Sidenote:您不需要
Offsetof(struct Stmesh,nverts)
在您的示例中。保证为零。第6.7.2.1节,第15条:Yes, it's valid. You can convert any non-function pointer to and from
char *
: there's an explicit part of the standard allowing that:C17, section 6.3.2.3, clause 7:
The reason this is allowed is exactly so you can do tricks like the one you're showing. Note, however, that this is only valid if the pointer comes from a
struct stmesh
in the first place (even if you don't have that struct in scope when you're doing that).Sidenote: you don't need
offsetof(struct stmesh, nverts)
at all in your example. It's guaranteed to be zero. Section 6.7.2.1, clause 15:在脚步上,C标准中没有什么明确说明代码明确的。我会说这是在可疑行为和不确定的行为之间的位置。
严格的混杂问题:不是一个问题。在严格的混叠范围内,通过指向结构的指针去引用某些地址是可以的正确的有效类型(C176.5§6和§7)。
字符指针转换:可疑。可以通过使用字符指针来通过字节来检查C中的任何类型。这符合“严格的混叠” C176.5§7以及C17 6.3.2.3中的指针转换规则,重点是我的:
您的指针并不指向周围结构类型中最低的字节。您也不使用连续的增量。对齐是另一个问题,但我认为在您的情况下这不是一个问题。
指针算术:可疑。指针算术是由加性运算符C17 6.5.6定义的,严格来说,这仅允许对数组类型的指针算术。如果单个结构变量可以被视为1个这样的结构项目的数组。为了理解以前引用的6.3.2.3的指针算术,我认为必须将其解释为
sizeof(the_struct)
bytes的字符数组。指向指向结构中间的角色指针不受指针算术规则的涵盖 - 严格地说,在第8节中,“……否则,行为是不确定的”。初始结构成员/初始常见序列规则:不应用。有一个特殊规则,允许我们在结构指针和指针之间转换为 em> exterm element( C176.7.2.1§15),但这不适用于此处。在联合中,两个结构的“常见初始序列”也有一个特殊的规则,此处也不适用。
这可能是一个更明确的版本:
这是普通的整数算术。您唯一的关注点是对齐和严格的混叠,这应该是可以的。通过
uintptr_t
从指针转换到/从指针转换的整数是可以的(inmand.Defined),C176.3.2.3§5和§6。Pedantically, there is nothing in the C standard explicitly stating that the code is well-defined. I'd say that it's somewhere between questionable and undefined behavior.
Strict aliasing concerns: not a problem. To de-reference some address through a pointer to struct is fine as far as strict aliasing goes, as long as what's actually stored at that location is of the correct effective type (C17 6.5 §6 and §7).
Character pointer conversion: questionable. Any type in C may be inspected byte by byte through the use of a character pointer. This is in line with "Strict aliasing" C17 6.5 §7 and also the pointer conversion rules in C17 6.3.2.3, emphasis mine:
Your pointer does not point to the lowest addressed byte in the surrounding struct type. Nor do you use successive increments. Alignment is another issue but I don't think it will be a problem in your case.
Pointer arithmetic: questionable. Pointer arithmetic is defined by the additive operators C17 6.5.6, which strictly speaking only allow pointer arithmetic on array types. Where a single struct variable may be regarded as an array of 1 such struct item. To make sense of the previously quoted 6.3.2.3 in terms of pointer arithmetic, I think it must be interpreted as a character array of
sizeof(the_struct)
bytes. Decreasing a character pointer pointing into the middle of a struct is not covered by the rules of pointer arithmetic - strictly speaking it sorts under §8 "...otherwise, the behavior is undefined".Initial struct member/initial common sequence rules: do not apply. There's a special rule allowing us to convert between a struct pointer and a pointer to its first element (C17 6.7.2.1 §15) but that does not apply here. There is also a special rule for "common initial sequence" of two structs in a union, also does not apply here.
This might be a more well-defined version:
This is plain integer arithmetic. Your only concerns here are alignment and strict aliasing, which should be ok. Integer to/from pointer conversions with
uintptr_t
are otherwise fine (impl.defined), C17 6.3.2.3 §5 and §6.