【问题标题】:Are basic data types in C mutable or immutable by default?默认情况下,C 中的基本数据类型是可变的还是不可变的?
【发布时间】:2013-05-26 20:12:43
【问题描述】:

我找到了很多关于 C#、java 中数据类型可变性的信息……但是纯 C 呢?

例如,int 在 C 中是否默认不可变?

例如看下面的代码示例:

#include <stdio.h>

int main()
{
  int a,b,c;
  b=0;
  c=0;
  a=b+c;
  b=1;
  c=2;

  printf("1st time %d\n",a);//gives out 0
  c=3;
  printf("2nd time %d",a);//gives out 0
  return 0;
}

上面的代码表明一个普通的int是不可变的,对吗?

【问题讨论】:

  • 只有声明为 const 的数据是不可变的
  • Roger 在某种意义上是正确的,但它甚至可以覆盖const int 的内存位置。
  • @Shredderroy 不一定,这取决于const int 的范围和系统。在具有真正只读存储器的嵌入式系统中,const 变量将存储在闪存/EEPROM 中。那么无论你做了多少丑陋的演员,你都不能覆盖它们。但另一方面,只有文件范围(全局/静态)常量变量被放置在真正的只读内存中,因为在嵌入式系统上,您可能也需要不可变特性。因此,总是可以覆盖局部 const 变量,因为它们总是存储在 RAM 中。
  • @Shredderroy 我相当有信心尝试覆盖 const 任何东西都是 C 中的 UB(未定义行为)。我需要查找它 - 已经晚了。写可能/可能不工作。示例:const 数据存储器在程序加载后变为只读。您可以尝试覆盖,但没有任何变化。
  • @chux 是的,它始终是未定义的行为。 C11 6.7.3 "If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined."

标签: c variables types


【解决方案1】:

[I]s int mutable in C?

简短的回答:是的!

如果你问是否可以改变 C 中的任何内存位置,答案是肯定的。 (有时会带来危险的后果!)虽然可以设计一个无法通过您为其创建的 API 直接改变的数据结构,但您可以几乎覆盖纯 C 中的任何内存位置。

难怪有人喜欢,有人不喜欢。

编辑

参考上面罗杰的话:

#include<stdio.h>

int main (int argc, char *argv[]) {
    const int n = 1;
    int *m = &n;
    *m = 10;
    printf ("%d\n", n); /* prints 10 */
    return 0;
}

所以,是的,甚至可以覆盖const ints 的内存位置。

【讨论】:

  • const 更像是对程序员的提示,它与路标一样有效,您不需要按照箭头 :-)
  • 不,没有你介绍的那么糟糕。您的代码具有误导性。你这样做的方式是违反约束的,你的编译器应该已经发出了诊断。要“正确”地做到这一点,您必须将constness 扔掉,然后 C 假设您知道自己在做什么。
  • 我同意。 gcc 确实会发出警告,但程序还是会编译并运行。但是,如果可以将constness 抛弃,那么您必须同意该结构毕竟不是那么一成不变的。
  • @Shredderroy,不,这并不是说如果你施放它是不可变的。它说你告诉编译器你假设你知道你在做什么。如果您执行它,该程序很可能会在您身上崩溃。你强迫了未定义的行为,所以所有的赌注都被取消了。
  • 我认为可能会或可能不会导致段错误的未定义行为足以使其既不可变也不可变。它没有定义,因此可能两者都没有,因为没有任何保证。在大多数情况下,就标准而言, const 应该是不可变的,这才是最重要的。谈论非标准行为,无论在任何特定情况下是否有效,都是没有意义的。
【解决方案2】:

普通的int 是可变的。 const 的实体(不仅声称在某些时候是,而且“确实是”)在理论上是不可变的,如果硬件和软件合作,实际上通常也是不可变的。

使用const 限定符定义变量使其“真正”为常量:

const int c3 = 3;
void f(void) {
    const int c4 = 4;
    ...
}

使用“棘手”的代码(抛弃 const-ness),您可以说服系统将新值写入或尝试写入 c3 和/或 c4

void f(void) {
    const int c4 = 4;
    int *p;

    p = (int *)&c3;
    *p = 99;
    printf("c3 is now %d\n", c3);
}

如果您调用f(),您可能会在某些系统上发现 c3 更改并变为 99。在其他系统上,您可能会收到“分段错误”或其他运行时错误。

将引用更改为使用c4,同样的事情也会发生——尽管在实践中,很少有系统会产生运行时错误。但是,您可能会完全观察到其他情况:您可能会得到c4 is now 4,而不是打印c4 is now 99

这最后可能会发生,因为允许编译器假设,将c4 设为const int 的值为4,你,程序员,永远不会改变它。 *p = 99 一定没有改变它(即使它改变了),所以对 printf 的调用可以替换为只打印 4 的不同调用。

c3 的引用通常也是如此:编译器可以假设,因为你承诺永远不会改变它,你实际上从来没有改变它。这可能会产生令人惊讶的结果,例如:p == &amp;c3 为真,c3 为 3,*p 为 99。但运行时错误很可能发生,因为大多数现代编译器和操作系统合作坚持c3进入只读区域。

当字符串字面量产生数组时(大多数情况下),C 有一个怪癖。这些数组不是const 限定的,但组成数组的字符无论如何都是只读的(至少在原则上,与const int c3 = 3; 一样)。与c3 一样,大多数现代系统都设法将它们设为“真正的只读”。

(字符串文字在用作array of char 类型对象的初始化器时不会生成数组。比较:

char *ronly = "this string is read only";
char rwarray[] = "this string is read/write";

这里ronly 是一个指针,而不是一个数组,所以字符串字面量生成一个数组,ronly 指向该数组的第一个字符。底层数组只读的,即使它的类型是char [25]。但是rwarray 是一个数组,而不是一个指针,所以字符串字面量填充它——并将它的大小设置为 26——它是可变的。您必须编写 const char roarray[] = ... 以使其不可变。 [有些人喜欢拼写类型char const [],而 const 出现在数据类型之后。这些意思是一样的。])

【讨论】:

    【解决方案3】:

    虽然这不是一个答案,但我没有足够的“声誉”来发表评论或投票。

    1. 上面@torek 写的关于C constness 的精彩文章(也是在线测试人员在选择题中不包含此类原始问题的原因)。

    2. 作为 javascript 的初学者,“可变”一词意味着(我认为)您可以向它添加属性(使用 javascript 中的“原型”对象)。

    无论如何,在您在问题中编写的代码段中,语句 a=b+c;仅将 'a' 的 value 更改为代码块中该点的 'b' 和 'c' 的 value (均为 0),全部为 3变量保留其原始地址。因此,您在语句之后对分配变量所做的任何更改都不会影响“a”的值。而在 C 中,这一事实并不能使它成为“不可变的”,这意味着值无法更改。

    【讨论】:

      【解决方案4】:

      我认为您从根本上误解了可变性的含义,或者至少误解了过程语言的工作原理。这是您的代码,其中注释了 a、b 和 c 的值:

      #include <stdio.h>
      
      int main(void) // Good practice to declare main with `(void)` instead of ()
      {
        int a,b,c;                 // a,b, and c are garbage values (whatever was left in memory)
        b=0;                       // a == garbage, b == 0, c == garbage
        c=0;                       // a == garbage, b == 0, c == 0
        a=b+c;                     // a == 0, b == 0, c == 0
        b=1;                       // a == 0, b == 1, c == 0
        c=2;                       // a == 0, b == 1, c == 2
      
        printf("1st time %d\n",a);
        c=3;                       // a == 0, b == 0, c == 3 
        printf("2nd time %d",a);
        return 0;
      }
      

      一旦分配了a,它就不会神奇地更新,因为cb 会发生变化。这与可变性与不变性无关。事实上,通过将 b 和 c 分配给 0,然后再将它们分别重新分配给 1 和 2,您证明了 ints(以及 C 中实际上未声明为 const 的所有数据类型)实际上是可变的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-08
        • 1970-01-01
        • 2022-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-02
        • 2010-11-20
        相关资源
        最近更新 更多