Study - Programming/C/C++2007. 9. 15. 01:01

  프로그래밍 처음 배울때 goto쓰지 말라는 소리 많이 들었을 것이다. 그 이유는 goto는 강제적으로 움직이는 것이라 프로그램의 움직임이 자연스럽지 못하고 잘못하면 스파게티 코드를 만들기 쉽기 때문이다. 그리고 goto를 쓰지 않고도 충분히 코드를 짤 수 있고. (참고로 내 친구의 코드 중에 함수를 쓸줄 몰라서 goto를 100개 게 쓴 친구가 있었다.)

  만일 이런 식의 코드가 있다고 했을 때

for(i = 0 ; i < 10 ; i++)
   for(j = 0 ; j < 10 ; j++)
      if(j == 3) break;

  break가 걸리면 break는 안쪽 for문만 멈춘다. 그런데 이 코드에서 break의 목적이 모든 for문을 빠져 나가는 것이라면 어떻게 해야 할까? 여러가지 방법이 있겠지만 그 중의 하나가 forward goto이다. goto를 남용하는것은 매우 위험한 일이지만 이것은 backward goto를 썼을 때 많이 발생한다. 프로그램의 진행방식은 위에서 아래로의 흐름인데 이것을 backword goto로 끊으면 순서를 역행하는 것이라 위험하겠지만 그에 비해 forword goto는 안전한 편이다. 단 뒤처리는 잘해 줘야겠지. 그래도 goto가 속도가 빠른 면이 있어(함수 호출 같은 경우는 스택 왔다 갔다 해야 하고 그렇겠지만 goto는 그런건 없다.)  어떤 면에서 보면 낫다고도 볼수 있다. 그러나 이런 특수한 경우가 아니라면 그나마 추천하고 싶지는 않다. 위 코드같은 경우는 이렇게 수정할수도 있을 것이다.

for(i = 0 ; i < 10 ; i++)
{
   for(j = 0 ; j < 10 ; j++)
   {
      if(j == 3)
      {    
           Break = 1;
           break;
      }
    }
    if (break = 1) break;
}

  나름 신호 변수를 놔둬서 하는 것인데 안정성은 이게 나을수도 있겠지. 속도면을 추구한다면 goto, 아니라면 위 같은 코드도 괜찮을 것이다.

Posted by 머리
Study - Programming/C/C++2007. 8. 30. 23:30

임시 변수를 사용하지 않고, 두 변수를 교환하는 방법


a = a + b;
b = a - b; --------------> b = ( a + b ) - b = a;
a = a - b; --------------> a = ( a + b ) - b = ( a + b ) - a = b;

뭐 a ^=b ^= a ^= b 이거랑 비슷한 착상이네.. a와 b는 달라야 한다고 하더라.. 그리고 물론 덧셈(a+b)시에 오버플로우가 나서는 안되겠지.. 그래서 잘 쓰지는 않는다더라..


잠깐 생각났던 건데 a,b를 값으로 하지 말고 포인터로 넘겨주면 어떨까 생각했는데 젠장.. 포인터는 덧셈 연산인 안되지..

Posted by 머리
Study - Programming/C/C++2007. 8. 29. 01:35

출처 : http://www.winapi.co.kr/clec/cpp2/15-1-4.htm

volatile

volatile 키워드는 const와 함께 변수의 성질을 바꾸는 역할을 하는데 이 둘을 묶어 cv 지정자(Qualifier:제한자라고 번역하기도 한다)라고 한다. const에 비해 상대적으로 사용 빈도가 지극히 낮으며 이 키워드가 꼭 필요한 경우는 무척 드물다. 어떤 경우에 volatile이 필요한지 다음 코드를 보자.

 

int i;

double j;

 

for (i=0;i<100;i++) {

     j=sqrt(2.8)+log(3.5)+56;

     // do something

}

 

이 코드는 루프를 100번 실행하면서 어떤 작업을 하는데 루프 내부에서 j에 복잡한 연산 결과를 대입하고 있다. j값을 계산하는 식이 조금 복잡하지만 제어 변수 i값을 참조하지 않기 때문에 i 루프가 실행되는동안 j의 값은 상수나 마찬가지이며 절대로 변경되지 않는다. 루프 실행중에는 상수이므로 이 값을 매 루프마다 다시 계산하는 것은 시간 낭비이다. 그래서 제대로 된 컴파일러는 이 루프를 다음과 같이 수정하여 컴파일한다.

 

j=sqrt(2.8)+log(3.5)+56;

for (i=0;i<100;i++) {

     // do something

}

 

j의 값을 계산하는 식을 루프 이전으로 옮겨서 미리 계산해 놓고 루프 내부에서는 j값을 사용하기만 했다. 어차피 루프 내부에서 j값이 바뀌는 것이 아니므로 이렇게 코드를 수정해도 원래 코드와 완전히 동일한 동작을 할 것이다. 똑똑한 컴파일러는 프로그래머가 코드를 대충 짜 놓아도 속도를 높이기 위해 자동으로 최적화를 하는 기능을 가지고 있으며 이런 암묵적인 최적화 기능에 의해 프로그램의 성능이 향상된다.

그렇다면 위 두 코드가 정말로 완전히 동일할까 의심을 가져 보자. j는 분명히 루프 내부에서 상수이므로 미리 계산해 놓아도 아무 문제가 없음이 확실하다. 그러나 아주 특수한 경우 최적화된 코드가 원래 코드와 다른 동작을 할 경우가 있다. 어떤 경우인가 하면 프로그램이 아닌 외부에서 j의 값을 변경할 때이다.

도스 환경에서는 인터럽트라는 것이 있고 유닉스 환경에서는 데몬, 윈도우즈 환경에서는 서비스 등의 백그라운드 프로세스가 항상 실행된다. 이런 백그라운드 프로세스가 메모리의 어떤 상황이나 전역변수를 변경할 수 있으며 같은 프로세스 내에서도 스레드가 여러 개라면 다른 스레드가 j의 값을 언제든지 변경할 가능성이 있다. 또한 하드웨어에 의해 전역 환경이 바뀔 수도 있다.

예를 들어 위 코드를 실행하는 프로세스가 두 개의 스레드를 가지고 있고 다른 스레드에서 어떤 조건에 의해 전역변수 j값(또는 j에 영향을 미치는 다른 값)을 갑자기 바꿀 수도 있다고 하자. 이런 경우 루프 내부에서 매번 j값을 다시 계산하는 것과 루프에 들어가기 전에 미리 계산해 놓는 것이 다른 결과를 가져올 수 있다. i루프가 50회째 실행중에 다른 스레드가 j를 바꾸어 버릴 수도 있는 것이다.

이런 경우에 쓰는 것이 바로 volatile이다. 이 키워드를 변수 선언문 앞에 붙이면 컴파일러는 이 변수에 대해서는 어떠한 최적화 처리도 하지 않는다. 컴파일러가 보기에 코드가 비효율적이건 어쨌건 개발자가 작성한 코드 그대로 컴파일한다. 즉 volatile 키워드는 "잘난척 하지 말고 시키는 대로 해"라는 뜻이다. 어떤 변수를 다른 프로세스나 스레드가 바꿀 수도 있다는 것을 컴파일러는 알 수 없기 때문에 전역 환경을 참조하는 변수에 대해서는 개발자가 volatile 선언을 해야 한다. 위 코드에서 j 선언문 앞에 volatile만 붙이면 문제가 해결된다.

 

volatile double j;

 

이 키워드가 반드시 필요한 상황에 대한 예제를 만들어 보이는 것은 굉장히 어렵다. 왜냐하면 외부에서 값을 바꿀 가능성이 있는 변수에 대해서만 이 키워드가 필요한데 그런 예제는 보통 크기가 아니기 때문이다. 잘 사용되지 않는 키워드이므로 여기서는 개념만 익혀 두도록 하자.

Posted by 머리
Study - Programming/C/C++2007. 8. 29. 01:31

#include <stdio.h>

int main(void)
{
 int line;
 char star[50] ="";
 int i = 0;

 printf("Line : ");
 scanf("%d",&line);

 for(i = 0 ; i < line; i++)
 {
  star[i] = '*';
  printf("%s\n",star);
 }
 return 0;
}

Posted by 머리
Study - Programming/C/C++2007. 8. 29. 01:13

혹시 이런 생각 해본적 있는가? 보통 함수의 인자의 개수는 정해져 있는데, printf함수 같은 경우는 그 수가 제한이 없지 않은가?


예전에 이런 것과 관련해서 함수 하나를 구현할 일이 있었는데 미루다 미루다 이제야(한 일주일 된듯 -_-;;) 한번 찾아 보았다.


먼저 printf 함수 원형을 한번 보자

int printf(const char*,...);

이렇게 되어 있다. 사실.. 이거는 컴파일러가 'printf(' 여기까지만 치면 원형을 보여주는데 쓸데없이 stdio.h를 열어보는 뻘짓까지 했다 ㅋ


한번 분석해 보자.


  우선 printf의 리턴 데이터 형식은 int형이였다. 이 함수의 리턴값은 출련한 변수의 갯수이다. 예를 들어 ("%d,"%d",a,b)했으면 리턴 값은 2.


 그리고 첫 번째 인자는 우리가 "name %s\b"하는 이런 거고.. 중요한 것은 두 번째 인자 '...'이다. 인자를 생략한다는 표시인데, 이 표시를 통해서 함수 호출 시 인자를 얼마든지 넣을 수 있게 된다. 그것을 처리하는 것은 원형에서 해줄 몫이고.. 그리고 이것을 '가변 인수'라고 한다. 뭐 자세한 사용법은 알아서 공부해볼것, 이건 대충 사용법만 보고 거기에서 합계 구하는 예제 있길래 안보고 만들어 본 것. 이해 자체는 그리 어렵지 않은데.. 음 좀더 공부해 봐야지.



#include
#include

int GetSum(int Num,...)
{
 va_list ap;
 va_start(ap,Num);
 int arg;
 int i;
 int Sum = 0;

 for(i = 0 ; i < Num ; i++)
 {
  arg = va_arg(ap,int);
  Sum+= arg;
 }

 va_end(ap);
 return Sum;
}

void main()
{
 printf("%d\n",GetSum(10,1,2,3,4,5,6,7,8,9,10));
}



참, 참고로 C#에서는 가변 인수를 사용하는 방법이 따로 지원한다. 이것보다 더 쉽게.. (맞나?) 그때는 이게 말 뜻 자체도 이해가 안되다가 아! printf! 해서 이해만 하고 그래서 뭘 만들어볼까할때 아이디어가 생각이 안나 넘어갔는데.. 이렇게 다시 해볼 기회가 생겨서 다행이다.  뭐 이거 찾다가 VB에서도 가변인수 쓸수 있다는거 알았고, VB,C# 둘다 연습해 봐야겠다 함.. 그리고 C/C++에서도 계속 공부하고..

Posted by 머리