【问题标题】:Const type qualifier when doing pointer things做指针事情时的 const 类型限定符
【发布时间】:2018-01-14 20:57:01
【问题描述】:

假设我有一个局部变量int var;,用于通过调用另一个函数来检索一些值,例如:func(..., &var);

然后我检查某些事物的检索值并采取相应的行动。在变量var 接收到它的值之后,var 将从此不再改变。 那么将var 定义为const 是否有意义? (int const var;)。

我不确定这是否是使用 const 关键字的正确方法,因为 var 的初始值是从堆栈中遗留下来的,并且在调用 func 时会发生变化:@ 987654329@。因此,在某些方面与 const 类型限定符相矛盾。

我对此不确定的原因是因为将变量 var 定义为 const (int const var;) 的效果(或结果)是后面代码的期望行为在var 的范围内。 即,不应允许使用赋值运算符直接更改 var 的值,即使 var 的值刚刚被 func 函数中的某个指针更改。

示例

int foo(void)
{
    int const var;

    if (!func(..., &var))
        return 0;

    if (var == x)
        // do some stuff
    else if (var == y)
        // do some other stuff
    else
        // nope

    return var * x + y;
}

我想知道在正确性方面,用于定义varconst 类型限定符是否仍然适用于这种情况。


关于正确性,至少使用 gcc 你可以传递你的 const var 到修改功能,但它会产生警告。我也不会 推荐也不做。这无助于混淆读者。

我似乎因为误解了 MSVC 的结果行为而走错了路。在启用/Wall 时,MSVC 产生的唯一警告是const object should be initialized。我没想太多,因为只需初始化var,看看会发生什么,结果是一个干净的编译。

但我想我至少对正在发生的事情有所了解。如果你有一个 const 数据类型,编译器认为你不应该写入只读的东西,即使使用指针这样做也是如此。因此,编译器唯一能看到传递给函数的 const 变量的地址是在该变量包含垃圾值以外的任何内容时。因此出现初始化警告。

如果在您的示例中使用 var 进行相等比较,则使其 const 可以避免 if (var = ...) 与 if (var == ...) 错字,因为 编译器会捕捉到这一点。

关于在尝试捕获此错误时是否使用 const;我认为它更多地与确保编译器根据标准中定义的规则吐出一些东西有关,而不是与编译器在省略 const 时产生或不产生警告有关var的定义。

【问题讨论】:

  • 请注意,在 C 中,const只读 的误称。 const 限定对象允许更改(但您不能分配给它们)。
  • 但是,将(可能是使用指针)写入只读字段/变量仍然感觉非常违反直觉。
  • 我仍然不确定您要问的是什么。你不能用一句话来表达你的问题,比如:“下面的代码是否正常”?
  • 我添加了示例代码来说明我认为你想要什么,希望能澄清这个问题。如果不正确,请编辑它和/或您的问题。

标签: c c99


【解决方案1】:

你可以包装另一个函数来做。

int wrapfun() {
    int var;
    func(..., &var);
    return var;
}
int main()
{
    const int var = wrapfun();
}

【讨论】:

  • 你好像误会了。没有理由将 func 的函数调用包装在另一个函数中,因为这会更改 var 变量的范围。如果var 的范围发生变化,那么将var 定义为const 的适用性不再具有任何价值/意义。此外,如果您的答案包含说明为什么您更喜欢将函数调用包装在另一个函数中而不是从变量 @987654326 的定义中省略 const 类型限定符的解释,那么它可能会更有价值@.
  • 如果调用函数是int foo(void) 而不是int main(),这将按需要工作。您可以直接在foo() 中初始化int const var = wrapfun();,并且在部分恒定、部分非恒定的范围内不会有乱七八糟的编码。我认为这是目前最好的解决方案。
【解决方案2】:

扩展 Eric Postpischil 的答案,您还可以将 var 应该为 const 的块提取到一个新函数中,并将 var 作为 const 参数。这是否有意义取决于该块中实际发生的情况以及需要处理多少其他动态状态。我会选择更具可读性的解决方案。

如果您在代码的某些部分将 var 声明为 const,您可以确保以后不会有人无意中对代码进行更改以更改 var 的值。您可以明确说明您对该变量的意图,这可能有助于读者理解代码。如果在您的示例中使用 var 进行相等比较,则将其设为 const 可以避免 if (var = ...)if (var == ...) 错字,因为编译器会捕捉到这一点。

关于正确性,至少使用 gcc 你可以将你的 const var 传递给修改函数,但它会产生一个警告。我既不会推荐也不会这样做。这无助于混淆读者。

// const.c
#include <stdio.h>

void func(int *var)
{
    *var = 5;
}

void post(const int var)
{
    printf("%d\n", var);
}

int main(int argc, char *argv[])
{
    const int var;
    func(&var);
    post(var);
    return 0;
}

$ gcc const.c

const.c:在函数'main'中:

const.c:16:10: 警告:传递 'func' 的参数 1 会丢弃来自指针目标类型 [-Wdiscarded-qualifiers] 的 'const' 限定符

 func(&var);
      ^

const.c:3:6:注意:预期为“int *”,但参数的类型为“const int *”

 void func(int *var)
      ^~~~

所以在这种情况下,我会在 main 中不使用 const,但您可以将其保留在 post 中。

【讨论】:

  • 感谢您帮助我。我在 OP 中放置了一些 cmets。
【解决方案3】:

你可以这样做,但它很丑:

int var;
func(..., &var);
{
    const int sneaky = var, var = sneaky;

    // var is const here.
    ...
}

这会打开一个新块,定义sneaky 以暂时保存var 的值,然后定义一个新的var 隐藏旧的var。由于旧的var是隐藏的,我们无法用旧的初始化新的var,用我们隐藏在sneaky中的值来初始化新的var

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多