C中的副作用是什么?

What is side effect in C?

维基百科说:

In computer science, an operation, function or expression is said to have a side effect if it modifies some state variable value(s) outside its local environment, that is to say has an observable effect besides returning a value (the main effect) to the invoker of the operation.

但是我们如何访问其局部环境之外的变量,谁能解释清楚这种情况,副作用,主要作用和序列点?

C语言里没有叫"main effect"的东西。

C语言(C17 5.1.2.3/2)中side effect的正式定义是:

Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

显然,副作用是:

  • 访问可变变量,或
  • 写入任何变量,
  • 或写入文件

(其中 stdout 可能被视为给定系统的文件。)

这与确定 if/how 允许编译器优化代码 (C17 5.1.2.3/4) 相关:

In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).

而且在确定表达式是否具有明确定义的行为时,这也是序列点的用武之地。然而,序列点本身就是一个大话题,例如在此处的几个答案中进行了解释: Why are these constructs using pre and post-increment undefined behavior?

函数是(应该是)一个黑盒子,其中 return 值或通过引用传递的变量的值应该是唯一可能根据输入参数而改变的东西。

函数在这些情况之外产生的任何其他可观察到的变化都是副作用。最著名的例子可能是 printf() 函数,它除了 return 写入字符的数量外,还更改标准输出的内容,这意味着更改与管道关联的一些内存缓冲区,一个文件或屏幕,例如,不属于函数的本地环境。

A State 是一个程序的属性,它讲述了 program/function/variable.

的行为

例如观察"i"(变量)的状态:

int i = 0; // here state of i is only dependent on the value its holding. lets denote the time when i is having a value 0 as State A.
i++; // i = 1, that means its no longer 0 i.e. state changed to state B
i++ // i = 2 . State C
i--; // i = 1, state B
i += 0; // state B

副作用: 通常,当有人谈论函数中的副作用时(无论使用何种语言),他们谈论的是除了函数参数和对象本身的变化之外的程序状态变化。

我喜欢想象副作用的方式:

----------------------
            \ <= side effect
           ----------------

在 C 中没有副作用的函数(我们忽略函数作为成员函数的可能性)可能如下所示:

int stateOfP(int a, char *p)
{
  *p = 0;
   return a+1;
}

在这种情况下,程序修改了 p 指向的内存位置,但由于 p 是参数中指向的内存位置,我们不认为这是副作用。

没有副作用的函数是一件好事,原因有几个。

首先,没有副作用使得编译器更容易优化函数的使用。

其次,副作用使得证明程序的正确性变得更加困难。

最后,当使用多线程时,尤其是在 C 程序中,副作用可能会产生不确定的结果。例如,如果两个线程在没有某种特殊锁定机制的情况下修改 C 程序中的一个普通全局变量,则该程序的结果是未定义的。

函数有副作用时会是什么样子?像这样:

int a = 0;
void stateChange(int p)
{
  a++; // here the function is having side effects as 'a' is not its attribute
  return;
}