C语言中的副作用是什么?

3

维基百科表示:

在计算机科学中,如果一个操作、函数或表达式修改了其本地环境之外的某些状态变量值,则称其具有副作用,也就是说除了向操作调用者返回一个值(主要效果)之外,还具有可观察的影响。

但是我们如何访问本地环境之外的变量呢?有人能够简明扼要地解释一下这种情况、副作用、主要效果和序列点吗?


例如,一个函数在返回之前会在其代码中修改一个全局变量。 - undefined
2
如果你将一个变量通过引用从一个局部作用域传递到另一个作用域,那么这个变量是可以被改变的,除非参数是const类型。 - undefined
1
函数内的代码位于该函数的作用域内,而不是全局作用域中的全局变量。如果一个函数只返回一个值,并且不修改其作用域外的任何内容,则称之为纯函数。 - undefined
3个回答

7

一个函数应该是一个黑匣子,仅根据输入参数返回值或通过引用传递的变量的值可以改变。

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


5
C语言中没有所谓的“主要影响”(main effect)。在C语言中,副作用的正式定义(C17 5.1.2.3/2)是这样的: 访问易失性对象、修改对象、修改文件或调用执行这些操作的函数都属于副作用,这些操作会改变执行环境的状态。
简单来说,副作用包括:
- 访问易失性变量; - 写入任何变量; - 或者写入文件(对于给定系统,stdout可能被视为文件)。
这在确定编译器如何优化代码时非常重要(C17 5.1.2.3/4)。换句话说,如果实现可以推断出某个表达式的值未被使用且没有产生必需的副作用(包括通过调用函数或访问易失性对象产生的任何副作用),则不需要求值该表达式的一部分。
此外,在确定表达式是否具有明确定义的行为时也非常重要,这就涉及到序列点。然而,序列点本身就是一个大主题,例如在这里的几个答案中进行了解释。

1

状态是程序的属性,它告诉我们程序/函数/变量的行为。

例如,观察“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;
}

1
当你说对对象本身的更改时,你是指什么? - undefined
这里的对象状态指的是函数本身的状态。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接