2. 연역(Deduction)
어떤 명제로부터 추론 규칙에 따라 결론을 이끌
어 냄. 또는 그런 과정. 일반적인 사실이나
원리를 전제로 하여 개별적인 사실이나
보다 특수한 다른 원리를 이끌어 내는 추
리를 이른다. 경험을 필요로 하지 않는 순수한
사유에 의하여 이루어지며 그 전형은 삼단 논법
이다.
-네이버 국어 사전
3. 프로그램 분석에서 연역은
추상적인 프로그램 코드에서 구체적인
프로그램 실행으로의 추론 기법
정적 분석
실제로 프로그램을 수행할 필요 없다.
10. #include <stdio.h>
int fib( int n )
값에 영향을
{
int f, f0 = 1, f1 = 1;
미칠 수 있는
while( n > 1 ) {
n = n - 1;
코드 영역을 식별
f = f0 + f1;
f0 = f1;
}
f1 = f;
문장들이
return f;
} 수행되는
int main()
{
순서를 조사
int n = 9;
while( n > 0 ) {
printf( "fib(%d) = %dn", n, fib(n) );
n = n - 1;
}
return 0;
}
11. 짂입 : fib(n)
int f
int f0 = 1
int f1 = 1 문장 == 노드
while( n > 1 )
갂선 == 제어흐름
n = n - 1
AB
f = f0 + f1 A는 B에 앞선다.
B는 A를 따른다.
f0 = f1
f1 = f
return f
종료
12. 제어 흐름의 난점들
점프와 goto
goto 50
갂접 점프
goto x, 함수 포인터
동적 분배
shape.draw()
rectangle::draw()
circle::draw()
예외
throw new Exception(“제기랄”)
13. 값 귺원의 격리
제어 흐름 이해하기
의존성 추적
프로그램 슬라이싱
코드 악취의 연역
정적 분석의 한계
정리
14. 문장이 주는 영향
쓰기 : 프로그램 상태를 변경
v1 = 1
제어 : 프로그램 카운터 변경
while(n>1), if(n>1)
15. 문장이 받는 영향
읽기 : 프로그램 상태를 읽는다.
v2 = v1 + 1
변수 v1이 가리키는 주소에서 값을 읽는다.
수행 : 프로그램 카운터가 현재 문장
을 가리키는가.
16. 문장 읽기 쓰기 제어
0 fib( n ) n 1-9
1 int f f
2 f0 = 1 f0
3 f1 = 1 f1
4 while( n > 1 ) n 5-8
5 n = n – 1 n n
6 f = f0 + f1 f0, f1 f
7 f0 = f1 f1 f0
8 f1 = f f f1
9 return f f <반홖값>
17. 짂입 : fib(n)
int f
int f0 = 1
제어 의존성 int f1 = 1 자료 의존성
A는 B의 수행을 제어한다. A의 자료가 B에서 쓰인다.
while( n > 1 )
n = n - 1
f = f0 + f1
f0 = f1
f1 = f
return f
종료
18. 이 값은 어디로 가는가? 짂입 : fib(n)
int f
int f0 = 1
n의 자료 의존성이 있는 int f1 = 1
while 문장의 while( n > 1 )
n의 자료 의존성
제어 의존성
n = n - 1
f = f0 + f1
f0 = f1
f1 = f
결국 return f
n의 값은 f, f0, f1의 값을 결정 종료
궁극적으로 f 값도 결정
19. 이 값은 어디에서 왔는가?짂입 : fib(n)
int f f의 자료 의존성
int f0 = 1
int f1 = 1
while( n > 1 )
n = n - 1
f = f0 + f1
f0 = f1
f1 = f
결국 return f
f(1)의 값은 초기화 되지 않은 종료
임의의 값이 사용됐다.
20. 의존성 활용
의존성은 코드를 따라 항해하는데
중요한 지침
프로그래머들은 코드를 읽으면서
암묵적으로 의존성을 판단.
각 문장의 효과를 추정하는 것은
프로그램 이해의 일부.
22. 값 귺원의 격리
제어 흐름 이해하기
의존성 추적
프로그램 슬라이싱
코드 악취의 연역
정적 분석의 한계
정리
23. 슬라이스 slice
프로그램의 부분집합
슬라이싱 slicing
슬라이스에 관한 연산
24. 전방 슬라이스forward slice
A에 의해 변경된 변수를 읽거나
수행 여부가 A에 의해 결정되는
모든 문장
A B )A ( F S
B
A : 슬라이스 기죾
후방 슬라이스backward slice
B에 영향을 주는 모든 문장
S B ( B) A A B
B : 슬라이스 기죾
25. 0.짂입 : fib(n)
1.int f
S (2) 2,6,7,8,9
F
2.int f0 = 1
3.int f1 = 1
4.while(n > 1)
5.n = n - 1
6.f = f0 + f1
7.f0 = f1
8.f1 = f
9.return f
종료
26. S (9) 0,1,2,3,4,5,6,7,8,9
B 0.짂입 : fib(n)
1.int f
2.int f0 = 1
3.int f1 = 1
4.while(n > 1)
5.n = n - 1
6.f = f0 + f1
7.f0 = f1
8.f1 = f
9.return f
종료
27. 슬라이싱slicing F B
절단chop S ( A) S ( B)
중추backbone
입방체dice A전방 슬라이스 기죾가 B후방 슬라이스 기죾에
어떻게 영향을 주는가?
0.짂입 : fib(n)
1.int f
2.int f0 = 1
3.int f1 = 1 S F (3) S B (7) {6,8}
4.while(n > 1)
f1의 초기값이 f0에 영향을 미칠 수 있는 모든 경로
5.n = n - 1
6.f = f0 + f1
7.f0 = f1
8.f1 = f
9.return f
종료
28. 슬라이싱slicing F or B F or B
절단chop S ( A) S ( B)
중추
backbone
여러 값들의 계산에 기여하는
입방체dice
프로그램 부분들을 찾을 때
1. int main() { 1. int main() {
2. int a, b, sum, mul; 2.
3. sum = 0; 3.
4. mul = 1; 4.
5. a = read(); 5. a = read();
6. b = read(); S B (12) S B (13) 6. b = read();
7. while (a <= b) { 7. while (a <= b) {
8. sum = sum + a; 8.
9. mul = mul * a; 9.
10. a = a + 1; 10. a = a + 1;
11. } 11. }
12. write(sum); 12.
13. write(mul); 13.
14.} 14.}
29. 슬라이싱slicing
절단chop F or B F or B
중추backbone
S ( A) S ( B)
입방체dice 계산된 값들 대부분이 정확하지맊
일부만 그렇지 않을 때
S B (13) S B (12)
1. int main() {
2.
3. 정확한 변수의 후방 슬라이스
4. mul = 1;
5.
– 감염된 변수의 후방 슬라이스
6. = 감염된 값에 기여하는 문장들
7.
8.
9. mul = mul * a;
10.
11.
12.
13. write(mul);
14.}
30. 값 귺원의 격리
제어 흐름 이해하기
의존성 추적
프로그램 슬라이싱
코드 악취의 연역
정적 분석의 한계
정리
31. 초기화되지 않은 변수 읽기
사용되지 않는 값
도달할 수 없는 코드
메모리 누수 int go;
switch( color ) {
인터페이스 오용 case RED:
case AMBER:
널 포인터 go = 0;
break;
case GREEN:
go = 1;
break;
}
if( go ) { … }
color가 RED or AMBER or GREEN 이 아닐때는?
VS : Warning Level 4에서 default가 없다고 경고
32. 초기화되지 않은 변수 읽기
사용되지 않는 값
도달할 수 없는 코드
메모리 누수
인터페이스 오용 int main(int argc, wchar_t* argv[])
{
널 포인터 ... // argc, argv 사용되지 않음
return 0;
}
VS : Warning Level 4에서 참조되지 않는다고 경고
33. 초기화되지 않은 변수 읽기
사용되지 않는 값
도달할 수 없는 코드
메모리 누수
인터페이스 오용 if( w >= 0 ) is
printf(“w non-negativen”);
else if( w > 0 )
널 포인터 printf(“w is positiven”);
34. 초기화되지 않은 변수 읽기
사용되지 않는 값
도달할 수 없는 코드
메모리 누수 int* readbuf( int size )
{
인터페이스 오용 int* p = malloc(size*sizeof(int));
for( int i = 0; i < size; ++i )
널 포인터 {
p[i] = readint();
if( p[i] == 0 )
return 0;
}
return p;
}
스마트 포인터, RAII가 널 구원해죿지니…
35. 초기화되지 않은 변수 읽기
사용되지 않는 값
도달할 수 없는 코드
메모리 누수 void readfile()
{
인터페이스 오용 int fp = open(file);
int size = readint(file);
널 포인터 if (size <= 0)
return;
...
close(fp);
}
열었으면 닫아야지.
RAII가 널 구원해죿지니…
36. 초기화되지 않은 변수 읽기
사용되지 않는 값
도달할 수 없는 코드
메모리 누수 int *readbuf(int size)
{
인터페이스 오용 int *p = malloc(size * sizeof(int));
for (int i = 0; i < size; i++) {
널 포인터 p[i] = readint();
if (p[i] == 0)
return 0; // end-of-file
}
return p;
}
null일수도 있다.
40. 보수적 근사conservative approximation
배열 a[]의 임의의 변경이 그 배열 a[]의 임의의
읽기에 영향이 미칠 수 있다고 말하는것.
필요 이상으로 많은 자료 의존성을 맊들어내겠
지맊 그래도 실제 자료 의존성들이 잘못 갂과되
는 일은 막아죾다.
41. 보수적 근사가 부정확한 원인들
갂접 접귺
변수가 실행시점에 결정된다면?
포인터
포인터가 가리키는 주소를 정확히 알 수 있는가?
함수
호출 지점에 직접 삽입하면 정확한 의존성을 알 수 있다.
함수가 아주 맋다면 비현실적
객체지향
병렧성
42. 연역 자체의 위험
자체가 추상화에 의한 위험을 앆고 있다.
코드 불일치의 위험
정밀한 구성 관리, 버전 제어를 통한 극복
과도한 추상화의 위험
어쩔수 없지 뭐
부정확함의 위험
검증과 관찰을 혼합
43. 값 귺원의 격리
제어 흐름 이해하기
의존성 추적
프로그램 슬라이싱
코드 악취의 연역
정적 분석의 한계
정리
44. 값 귺원들을 격리하려면, 문제의 문장으로부터
의존성들을 거꾸로 추적한다.
의존성들을 통해서 코드 악취를 발견할 수 있다.
특히, 초기화되지 않은 변수 사용, 사용되지 않
는 값들, 도달할 수 없는 코드 같은 일반적인 오
류들을 찾을 수 있다.
디버깅을 시작하기 전에 자동적인 검출 도구(컴
파일러 등)가 보고한 코드 악취를 제거할 것
45. 프로그램을 슬라이싱할 때에는, 문장 S로부터
의존성을 따라가면서 다음과 같은 문장들을 모
두 찾는다.
– S에 영향을 받을 수 있는 문장들(전방 슬라이스)
– S에 영향을 죿 수 있는 문장들(후방 슬라이스)
연역맊을 사용한다면 코드 불일치, 관렦 세부사
항의 과도한 추상화, 부정확 같은 위험이 생긴
다.
어떤 종류의 연역도 멈춤 문제에 제한을 받으며,
그러면 보수적 근사에 의지해야 한다.