我想知道注释"Is this legal C?"(在底部的函数dumpverts()中)前一行是否合法:

#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);
    }

我倾向于相信这是合法的,但我不能char *%确定严格的别名规则是否会允许从char *struct stmesh *进行转换,就像dumpverts()函数体中有趣的一行所做的那样.

基本上,该行从指向第二个成员的指针中获取指向struct stmesh的指针.我没有看到任何与对齐相关的潜在问题,因为整个struct stmesh的内存来自malloc(),所以 struct 的开头是"适当对齐的".但正如我所说,我不确定严格的别名规则.

如果它打破了严格的混叠,是否可以在不改变dumpverts()函数原型的情况下使其兼容?

如果你想知道我想要这个是为了什么,主要是为了了解offsetof()的极限在哪里.是的,我知道dumpverts()应该收到一个指向struct stmesh的指针.但我想知道,以编程方式获取struct stmesh指针是否可能以合法的方式实现.

推荐答案

是的,它是有效的.您可以将任何非函数指针转换为char *或从char *转换为char *:标准中有一个明确的部分允许:

C17第6.3.2.3节第7条:

当指向对象的指针转换为指向字符类型的指针时,结果将指向对象的最低地址字节.结果的连续增量,直到对象的大小,都会产生指向对象剩余字节的指针.

允许这样做的原因正是为了让你可以玩你正在展示的那种把戏.然而,请注意,这仅在指针首先来自struct stmesh的情况下有效(即使在执行此操作时,范围内没有该 struct ).

旁注:在你的例子中,你根本不需要offsetof(struct stmesh, nverts).保证为零.第6.7.2.1节第15条:

经过适当转换的 struct 对象指针指向其初始成员(如果该成员是位字段,则指向其所在的单元),反之亦然. struct 对象中可能有未命名的填充,但在其开始处没有.

C++相关问答推荐

Zig将std.os.argv转换为C类型argv

理解没有返回语句的递归C函数的行为

为什么下面的递归基本情况在C中不起作用?

当打印字符串时,为什么在c中没有使用常量限定符时我会收到警告?

在函数中使用复合文字来初始化C语言中的变量

如何在Visual Studio代码中关闭此函数名称显示功能?

从组播组地址了解收到的数据包长度

S的这种管道实施有什么问题吗?

ifdef __cplusplus中的整数文字单引号

在编写代码时,Clion比vscode有更多的问题指示器

无法访问共享目标文件内的共享指针

为什么realloc函数在此代码中修改变量?

Zlib:解压缩大文件导致";无效代码长度设置";错误

C程序向服务器发送TCPRST

将不同类型的指针传递给函数(C)

memcmp 是否保证按顺序比较字节?

保存有符号整数结果的变量是否会溢出(后增量的副作用),并且此后从未在任何表达式中使用过它,是否会导致 UB?

如何正确探测平台设备?

C simd _m128 晶圆厂

int 与 size_t 与 long