问题描述
在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中,“访问”被定义为“用于读取或修改对象值的“执行时动作””。如果x
是volatile 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不是这样的语言之一。