C的副作用是什么?

问题描述

在C99第5.1.2.3条第2款中,

访问易失性对象,修改对象,修改文件调用函数 这些操作中的任何一个都是副作用,它们是状态的变化 执行环境。

C标准定义了副作用,如上所述。但是C99似乎并没有解释什么是访问易失性对象,修改对象,修改文件(在第3章中定义了访问,修改,对象的定义。但是访问volatile如何?并修改文件内容?)。
当我用副作用进行搜索时,C99中有一些示例。但是我不确定在访问易失性对象,修改对象和修改文件中是否对每个示例进行了分类
我读过What is side effect in c?Are side effects a good thing?,但仍然感到困惑。
我的问题是C标准是否明确描述了副作用的含义?他们是什么意思?

解决方法

我的问题是C标准是否明确描述了副作用的含义?

您引用的C标准中的句子(C 1999 5.1.2.3 2,与C 2018中的句子相同)明确描述了副作用的含义。这些将在下面进一步解释。

修改对象

修改对象应理解为包括更新代表对象的存储字节的内容。我相信它们的完整列表是:

  • 简单分配(=)和复合分配(*=/= %=+=-=<<=>>=&=^=|=)。
  • 递增和递减运算符(++--),包括前缀和后缀。
  • 初始化包含在其定义中的对象。
  • 为更改对象而指定的库例程,例如memcpy

访问易失对象

在C 2018 3.1中,“访问”被定义为“用于读取或修改对象值的“执行时动作””。如果xvolatile int,则在表达式中使用x的值可以对其进行访问(在计算表达式时),因为它读取x的值。您可以在6.3.2.1 2中更具体地遵循此规则,它告诉我们在表达式中使用x会导致x的值被采用:

除非它是sizeof运算符的操作数,一元&运算符,++运算符,--运算符或{的左操作数{1}}运算符或赋值运算符,将不具有数组类型的左值转换为存储在指定对象中的值(不再是左值);这称为左值转换

因此,表达式本身中只是对象.的指定中的x被转换为存储在x中的值,这意味着存储的值是从内存中读取。这是对x的访问。

修改易失性对象与上述修改任何对象相同。

修改文件

通过第7.21节(“输入/输出x”)中定义的例程来修改文件。

,

访问易失性对象意味着通过易失性限定的左值读取volatile限定对象/一个对象的值-标准指出,这些值需要进行评估“ 严格按照抽象机的规则

修改对象意味着完全修改任何对象 -修改任何内容均被视为副作用。例如:赋值运算符具有修改变量的副作用,它将值分配给它!在下面的程序中。使用赋值运算符是有副作用的!

修改文件意味着写入文件,创建文件,删除文件等-构成更改的任何内容。

这些类别的副作用示例:

void increment(int *p) {
    (*p) ++; // side effect - assign a new value to the 
             // object pointed to by p
}


int a = 5;
volatile int b = 6;
if (b == 6) { // side-effect of accessing a volatile variable
    a += b;   // calculate a + b,and as a side effect assign a new 
              // value to a
}

increment(&a);     // side effect - call a function that does
                   // one of the aforementioned operations
printf("%d\n",a); // side effect - change the state of an output stream

FILE *fp = fopen("foo","w"); // side effect - create or truncate
fputc('!',fp); // side effect - modify file
fclose(fp);.    // side effect - close the file,flush 
remove("bar");. // side effect - remove file

有一小类编程语言,称为纯功能语言,例如Haskell,其计算没有副作用。 C不是这样的语言之一。