从指针到其第二会员的指针获得指针是合法的吗?

发布于 2025-01-23 16:34:44 字数 1973 浏览 1 评论 0原文

我想知道该行之前的评论“ 该法律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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吃不饱 2025-01-30 16:34:44

是的,这是有效的。您可以将任何非功能指针转换为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:

When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

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:

A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

清风夜微凉 2025-01-30 16:34:44

在脚步上,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),但这不适用于此处。在联合中,两个结构的“常见初始序列”也有一个特殊的规则,此处也不适用。



这可能是一个更明确的版本:

dumpverts( (uintptr_t) &(m->verts[0]) );
...
void dumpverts (uintptr_t ptr) 
{
  struct stmesh* themesh = (struct stmesh *)(ptr - offsetof(struct stmesh, verts));

这是普通的整数算术。您唯一的关注点是对齐和严格的混叠,这应该是可以的。通过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:

    A pointer to an object type may be converted to a pointer to a different object type. If the
    resulting pointer is not correctly aligned for the referenced type, the behavior is
    undefined. Otherwise, when converted back again, the result shall compare equal to the
    original pointer. When a pointer to an object is converted to a pointer to a character type,
    the result points to the lowest addressed byte of the object.
    Successive increments of the
    result, up to the size of the object, yield pointers to the remaining bytes of the object.

    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:

dumpverts( (uintptr_t) &(m->verts[0]) );
...
void dumpverts (uintptr_t ptr) 
{
  struct stmesh* themesh = (struct stmesh *)(ptr - offsetof(struct stmesh, verts));

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文