정적코드분석/코딩표준

프로그래밍에서 side effect 란?

오늘은 프로그래밍에서의 ‘side effect’에 대해서 이야기하고자 합니다. 우리가 접하는 side effect라는 단어는 직역하면 ‘부작용’입니다. ‘부작용’이라는 단어를 들었을 때 대부분의 사람들은 약의 ‘부작용’을 떠올릴 것입니다. ‘부작용’이란 용어는 왠지 탈이 난것 같고, 일어나서는 안되는 꺼름직한 일이겠지만, 프로그래밍에서의 ‘side effect’는 꼭 부정적인 말은 아닙니다.

이런거 아님

Side Effect라는 단어는 보통 프로그래밍 언어의 스펙 문서나 코딩 표준 문서에 많이 나옵니다. 예를 들어 JSF Air Vehicle C++ Coding Standards의 187번째 규칙에 바로 이 단어가 나오는데요, 이 규칙은 아래와 같습니다.

빈 구문이 아니라면 반드시 하나의 side effect를 가져야합니다. (All non null statements shall potentially have a side effect.)

왜냐하면 빈 구문이 아닌데 side effect가 없는 구문은 일반적으로 프로그래밍 에러를 가리키는 것이죠.?그런데 side effect가 무엇이냐구요?

Side Effect 란?

ISO/IEC 14882는 side effect라는 용어를 다음과 같이 정의합니다.

Accessing an object designated by a volatile lvalue, modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

쉽게 말해서 실행 중에 어떤 객체를 접근해서 변화가 일어나는 행위(라이브러리 I/O, 객체 변경 등)입니다.

예제를 한 번 보겠습니다.

x = 3 + 4;
위의 표현식은 1개의 side effect가 있습니다. x의 값이 변경되었기 때문입니다. .

y = x++;
위의 표현식은 총 2개의 side effect가 있습니다. x++에서 x가 한 번 변하고, x 값 대입으로 y가 한 번 변합니다.

3 + 4;
위 표현식은 side effect가 없습니다.

if(flag){
foo();
}

위 표현식은 flag가 true일 경우에 한해서 잠재적으로 side effect 가 있습니다.

Side Effect의 문제

Side Effect 자체는 아무 문제가 안되지만, 프로그래머가 이를 고려하지 않고 사용했을 경우 의도하지 않은 결과가 나올 수 있습니다.

1. 매크로에서 잘못 사용 ( CERT EXP31-C)

#define sqr(x) x*x
sqr(a+b)

매크로를 사용할 때 Side Effect를 고려하지 않고 사용한 예입니다. sqr 매크로는 들어오는 인자 값을 두 번 곱하도록 정의되어 있기 때문에 전처리를 거친 후, 코드는 아래와 같습니다.

a+b*a+b

프로그래머가 의도한 것은 분명 a+b의 값을 제곱ㅎ매크로를 사용할 때, 매크로 정의를 확인하지 않고 무심코 그냥 사용할 경우 위와 같은 참사가 나기 쉽습니다. 이 경우 문제를 찾아내기가 매우 힘듭니다. ?따라서 매크로를

#define sqr(x) ((x)*(x))

와 같이 정의하거나

long value = a + b;
sqr(value)

이렇게 매크로에서 Side Effect가 일어나지 않도록 사용해야 합니다.

2. sizeof 에서 잘못 사용(?AV Rule 166 , MISRA-C:1998 Rule 40, CERT EXP06-C)

sizeof는 어떤 변수나 타입의 크기를 알아낼 때 사용됩니다. 하지만 종종 아래와 같이 sizeof 안에 side effect를 일으키는 연산을 적는 경우가 있습니다. 아래 코드는 GCC 계열에서 동작하는 코드입니다. MSVC 의 경우 2008에서 컴파일 오류가 나는 코드죠. : )

int a = 14;
int b = sizeof(a++);

하지만 VLA(Variable Length Array)의 [] 안의 연산자를 제외하고는 연산이 수행되지 않습니다. 따라서 안전하게 사용하려면 sizeof 에는 어떠한 side effect도 일어나지 않도록 프로그래밍 해야 합니다.

3. && 혹은 || 에서 사용(?AV Rule 157 , MISRA-C:1998 Rule 33, CERT EXP10-C)

논리적 AND연산(&&) 또는 논리적 OR연산(||)의 오른쪽 피연산자는 side effect를 포함하면 안됩니다. 조건에 따라 실행될 수도 실행 안될수도 있는데다가 연산 순서에 따라서 조건 결과가 달리나올 수 있기 때문입니다.

if ( logical_expression && ++x) // 문제
{
// some expression
}
function(x); 

위의 코드의 경우 logical_expression이 false면 ++x가 자동으로 실행이 안되기 때문에 아래 function(x)의 경우 원하는 값이 제대로 나오지 않을 수 있습니다.

Leave a Reply

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *