【问题标题】:A struct with a member that sometimes needs to be`const` and sometimes not一个结构,其成员有时需要是`const`,有时不需要
【发布时间】:2013-08-05 21:49:05
【问题描述】:

我正在开发一个程序,该程序在许多地方使用通用 struct 来穿梭相关值。这个结构包含一个字段char* s

很多函数修改s;但是,有时该结构用于将信息传递给只会读取它的函数。在这些情况下,用于初始化s 的字符串通常是const char*。但是,将其分配给 s 会导致编译器警告。

虽然技术上是正确的,但这个警告感觉不对,因为该函数没有修改s。 除了丢弃 const 之外,有没有办法绕过这个警告?函数是否有某种方式可以保证它将结构成员视为const

例子:

#include <stdio.h>

struct mystruct{
  int i;
  char* s;
};

void i_only_read(const struct mystruct *m){
  printf("mystruct: i=%d, s=%s\n", m->i, m->s);
}

int main(int argc, char **argv){
  const char* cstr = "Hello";
  struct mystruct m;
  m.i=99;
  /* gcc warning: assignment discards ‘const’ qualifier
   * from pointer target type
   */
  m.s=cstr;
  i_only_read(&m);
}

备注

  1. 我无法将结构的声明更改为const char* s,因为大多数采用指向结构的指针的函数都会修改s
  2. 我想我可以有两个结构,一个是char* s,一个是const char* s,但这看起来很丑(造成冗余,需要两个结构之间的转换函数)。
  3. 如果有人感兴趣,程序是Navit,结构是struct attr。我为这个问题创建了一个简单的示例。

【问题讨论】:

  • +1 我正在实现一个通用字符串对象,它可以在复制或不复制的情况下进行初始化,我也遇到了这个问题。我很高兴你问这个。
  • @H2CO3:呃,没关系:-S
  • @KerrekSB 没问题 - 如果从 const char *(char *) 的 C 风格转换也是安全的,我会很乐意使用它 :)

标签: c warnings constants


【解决方案1】:

有趣的是,您似乎可以在union 中做到这一点:

struct mystruct {
    int i;

    union {
        char *s;
        const char *cs;
    };
};

现在,union 的规则适用:仅使用分配给的联合成员。如果函数“承诺”正常运行,您可以将字符串分配给 cs 而不会发出警告。

具体来说,你应该做的是分配给cs,然后将结构作为非常量参数传递。

【讨论】:

  • AFAIK,然而,1. 通过联合的类型双关语是安全的,2. 相同类型的合格和不合格版本保证具有相同的表示和对齐要求,因此阅读@987654326 是安全的@ 当cs 被初始化时,反之亦然。
  • 谢谢@H2CO3。此外,在实践中,工会往往比标准描述的更灵活 - 但很高兴知道这是合法的。
  • 不过,您永远不应该依赖您的“实践”经验。如果某些东西不符合标准,那么它就不会“工作”,它只是看起来在工作。
  • C11(而不是 C99 或 C89)中允许使用匿名联合元素。第 §6.7.2.1 ¶8 节适用于 C11:如果 struct-declaration-list 不包含任何命名成员,无论是直接还是通过匿名结构或匿名联合,则行为未定义。 相应的C99 中的措辞是:§6.7.2.1 ¶7:如果 struct-declaration-list 不包含命名成员,则行为未定义。在 C11 中,§6.7.2.1 ¶2 和 §6.7.2.1 ¶13 进一步描述了匿名结构和联合。
  • 虽然这避免了强制转换来移除 const-ness,但这似乎有点像作弊,因为仍然没有编译时检查。还不如说实话,只是投...
【解决方案2】:

基于paddy和jxh的想法,我想出了一个看起来很实用的解决方案:

  • 使用const char* 成员创建结构的“const 版本”
  • 使用联合将其与结构的“非常量”版本结合

代码:

typedef struct const_mystruct {
    int i;
    const char * s;
} const_mystruct;

typedef union {
    struct {
        int i;
        char *s;
    };
    const const_mystruct cms;
} mystruct;

这实现了以下目标(类似于const 限定符对简单指针的作用):

  • 如果函数想要承诺不修改 char 数组,则可以接受 const_mystruct*,编译器将强制执行此操作。
  • mystruct 适用于需要修改 char 数组的情况/free()d。
  • 最后,mystruct 可以通过读取mystruct.cms 转换为const_mystruct。 不可能通过写mystruct.cms 转换const_mystruct -> mystruct 的(潜在危险),因为mystruct.cmsconst,因此不可写。

说明使用的代码:

#include <stdio.h>
#include <malloc.h>
/* [structs omitted] */

void i_only_read(const const_mystruct *m){
  printf("mystruct: i=%d, s=%s\n", m->i, m->s);
}

void i_might_modify(mystruct *m){
  printf("noconst: mystruct: i=%d, s=%s\n", m->i, m->s);
}

int main(void){
  const char* cstr = "Hello";
  const_mystruct cm;
  cm.i=99;
  cm.s=cstr;
  // Method promises not to change the structure: OK.
  i_only_read(&cm);
  // Pass a "constant" structure into a method that might modify it:
  // diagnosed by compiler (warning or error).
  i_might_modify(&cm);
  // Trying to remove "const" from the pointer: compiler will not allow this...
  // m.cms.s=cstr; 

  mystruct m;
  m.i=99;
  m.s=malloc(10);
  // Struct is not "const", so modification is OK.
  i_might_modify(&m);
  // Convert to "const" struct, without cast.
  i_only_read(&(m.cms));
  return 0;
}

这提供了与使用 const 指针相同的保证,这是我的目标(有点像“递归 const”)。

潜在问题:

  • 设置似乎相当复杂,其他开发人员可能很难理解(我当然知道)。
  • 拥有相同struct的两个版本感觉很笨拙。

我看看能不能用这个……

【讨论】:

    【解决方案3】:

    这个解决方案有点复杂。它结合了@paddy 对union 的建议,以允许const char * 分配与另一个unionconst 化的数据结构版本上提供严格的执行。

    typedef struct const_mystruct {
        const int i;
        const char * const s;
    } const_mystruct;
    
    typedef union {
        struct {
            int i;
            union {
                char *s;
                const char *cs;
            };
        };
        const_mystruct cms;
    } mystruct;
    
    void i_only_read(const_mystruct *m){
      printf("mystruct: i=%d, s=%s\n", m->i, m->s);
    }
    
    int main(int argc, char **argv){
      const char* cstr = "Hello";
      mystruct m;
      m.i=99;
      m.cs=cstr;
      i_only_read(&m.cms);
    }
    

    【讨论】:

    • 除了老套的union 之外,稍微不同的方法是只让const_mystruct 的公共版本mystruct 对API 的用户可见。使union 版本不透明。这意味着您必须添加更多可以代表调用者修改 mystruct 的 API。 API 实现会将const_mystruct 转换为union 版本。
    【解决方案4】:

    我意识到这个答案是您的问题试图避免的。但是,如果不使用 gcc 以外的编译器或将您的代码扭曲成不可读的状态,那么有时采用一种有纪律的方法和代码中的少量 cmets 可以解决问题!

    下面代码片段中const的用途是什么?

    void i_only_read(const struct mystruct *m){
      printf("mystruct: i=%d, s=%s\n", m->i, m->s);
    }
    

    从 C 的角度来看,这意味着该函数不会修改 mystruct。如果此定义以某种方式使 gcc 适合,则删除 const 措辞并在代码中强制执行“不更新”透视图。

    // the programmer can/must only read mystruct
    void i_only_read(struct mystruct *m){
      printf("mystruct: i=%d, s=%s\n", m->i, m->s);
    }
    

    【讨论】:

    • const 只是试图说明我想将整个结构视为“常量”。是的,它在这里实际上没有意义(尽管它也没有伤害)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多