3.16(금) 우선순위에 대해서..

from Study/C언어 2007/03/16 10:27 view 28301
 

연산자

종류
결합성
우선순위
()  []  .  ->
최우선연산자
좌->우
높다
!  ~  -  *  &  sizeof  ++  -- cast
단항연산자
우->좌

* /  %  +  -
<<  >>
<  <=  >  >=  !=  ==
&&  ||  &  |  ^
이항연산자
좌->우

?:
삼항연산자
우->좌

=  +=  -=  *=  /=  %=  <<=  >>=  &=  |=  ^=
대입연산자
우->좌

,
순차연산자
좌->우
낮다

x = x + 1; x += 1; 의 줄여 쓰는게 가능하다. 하지만 =+ 는 되지 않는다.

이는 이항연산자가 대입연산자보다 우선순위가 빠르기 때문이다. 우선순위에 따라

좌->우로 컴파일하는데 +=를 만나면 x  와 1을 + 하는 이항연산을 하고 이값을 = 에

따라 lvalue에 대입시킨다.


int i = 5;
j = ++i + ++i;
// j 는 14가 된다.
i=5;
j = i++ + i++;
 // j는 10이 된다.

 ++i + ++i 는 언뜻보면 결과가 13 일 것 같다. 하지만 VC++ 컴파일러는 ++i 연산을 하게되면

스택내에 저장 되어 있는 값을 변경해준다. 그래서 6 + 7 일것 같지만 + 연산을 하면

변수 i에 저장된 값으로 이항연산을 하게 된다. 그래서 7 + 7 의 연산을 하는 애매모호한

값이 나오게 된다.

 i++ + i++ 는 5 + 6 으로 착각하기 쉽다. 하지만 후위증가는 문장의 끝인 ;(세미콜론)이

나와야 증가를 수행하게 된다. 즉 5 + 5로 연산을 수행 하고 문장이 끝나야 증가해서

i는 7이 된다.


int i = 5
printf("%d , %d", i , ++i);
//출력 6,6

 C는 함수의 인수를 뒤에서부터 순서대로 전달하도록 함수호출규약으로 정해 놓았다.


 if(y + 4 == 4 || ++y * 4 == 0)
 {
  printf("%d\n", y);
  printf("!\n");
 }

 그냥 혹시나 해서 이상한 코드를 만들어 보았다. if 문의 조건을 살펴볼때 + || * 이나

* || ( + ) 형식의 조건이 들어 있다면 왼쪽 먼저 일까 오른쪽이 먼저 일까 생각해 봤는데


이항연산자를 만나면 다른 이항연산자가 없다면 우선순위 비교 자체를 하지 않을 것이므

로....하여간 좀 바보같다..ㅠ_ㅠ

추가 : *&a[3] 을 우선순위대로 풀어보면 a 의 3번째 주소값의 indirection 을 행한다.

여기엔 * & [] 세가지의 연산자가 있는데 [] > & == * 이지만 & 와 *가 우선순위가 같다면 결합성이 우->좌

이기 때문에 & 먼저 연산되는 것을 알고 있자.
 

(Quiz)어떤 회사의 입사문제.

from Study/Quiz 2007/03/16 08:57 view 121344

출처 : http://blog.naver.com/comsik76/30005559569

컴파일시 에러 날(?) 곳을 찾아보아요.(문법적 오류 or 논리적 오류)

// composite.h

#include <list>
#include <memory>

// Composite 패턴을 구현하도록 하는 클래스
template <typename Type, typename Pointer>
class  xListComposite : public std::list<Pointer>
{
public :
 // 생성자
 xListComposite();
 // 소멸자
 ~xListComposite();

 // 부모 composite를 설정한다.
 inline void       setParent(const xListComposite* parent);
 // 부모 composite를 반환한다.
 inline xListComposite*    getParent();

 // 자식을 추가한다.
 void        insertChild(Type* child);

 // 자식들을 순환 호출한다.
 template <typename Function>
 void        order(Function& func);
 // 자식들을 순환으로 찾는다.
 template <typename Function>
 Type*        find(Function& func);

 xListComposite*      parent_;
};

// 생성자
template <typename Type, typename Pointer>
xListComposite<Type, Pointer>::xListComposite()
{
}

// 소멸자
template <typename Type, typename Pointer>
xListComposite<Type, Pointer>::~xListComposite()
{
}

// 부모 composite를 설정한다.
template <typename Type, typename Pointer>
void   xListComposite<Type, Pointer>::setParent(const xListComposite* parent)
{
 parent_ = parent;
}

// 부모 composite를 반환한다.
template <typename Type, typename Pointer>
xListComposite<Type, Pointer>* xListComposite<Type, Pointer>::getParent()
{
 return parent_;            
}

// 자식을 추가한다.
template <typename Type, typename Pointer>
void   xListComposite<Type, Pointer>::insertChild(Type* child)
{
 Pointer ptr(child);
 ptr->setParent(this);
 push_back(ptr);
}

// 자식들을 순환 호출한다.
template <typename Type, typename Pointer, typename Function>
void   xListComposite<Type, Pointer, Function>::order(Function& func)
{
 func(this);
 for (iterator i = begin(); i != end(); i ++)
  i->order(func);
}

// 자식들을 순환으로 찾는다.
template <typename Type, typename Pointer, typename Function>
Type*   xListComposite<Type, Pointer, Function>::find(Function& func)
{
 if (func(this))
  return this;

 for (iterator i = begin(); i != end(); i ++)
  if (i->find(func))
   return i->get();
}


/// leaf.h
                 
#include "composite.h"
// composite를 이용하는 노드를 생성한다.
class  xLeaf : public xListComposite<xLeaf>
{
public :
 xLeaf(const char* name)
 {
  name_ = new char [128];
  strcpy(name_, name);          
 }

 ~xLeaf()              
 {
  delete name_;
 }

 char*    name_;          
}; // xLeaf

/// program.cpp

#include "leaf.h"

struct  Ldisplay
{
 void operator () (xLeaf* leaf)
 {
  std::cout << leaf->name_;
 }
};

struct  Lfind
{
 Lfind(char* find)
 {
  strcpy(find_, find);
 }
 bool operator () (xLeaf* leaf)
 {
  return strcmp(leaf->name_, find_) == 0;
 }
 char  find_[128];
};

void  main()
{
 xLeaf root("과일");

 root.insertChild(new xLeaf("사과"));
 root.insertChild(new xLeaf("바나나"));
 root.insertChild(new xLeaf("복숭아"));
 root.insertChild(new xLeaf("배"));

 xLeaf* leaf = root.find(Lfind("사과"));
 leaf->insertChild(new xLeaf("부사"));
 leaf->insertChild(new xLeaf("국광"));

 root.order(Ldisplay());
}

// 버그 수정및 결과 예측

Tag |

if(input == 5)
 num = 5;
else
 num = 0;


이런 패턴이 있다면 더 줄여 보고자 하는 욕심을 가지는 것이 좋다.

삼항조건연산자는 그저 이게 맞다면 앞에꺼 리턴 이런 생각을 가지기 쉬운데 더 응용해보자.

num = (input == 5) ? 5:0; 로 위에 코드를 대폭 줄일 수 있고 또,

(input == 5) ? ((output == 'a') ? 5 : 0) : 0; 같은 코드를 사용해서 if문을 중첩한 효과를 낼수

있다.

0과 다른수를 리턴하고자 할때 아주 간단하게 표현 가능한데

num = (input == 5) * 5;

생각해보면 간단하지만 참일때 1 , 거짓일때 0을 리턴한다는 것을 잘 활용한게 아닌가 싶다.


여러코드를 코드를 보다보면 이렇게도 응용을 할수 있구나라는 생각이든다.

과연 그 코드를 모르는 상태에서 이러한 코드를 생성할수 있을까...

/*10진수를 16진수로 출력하는 프로그램*/
#include <stdio.h>

void main()
{
 int input;
 int low, hi;

 while(1)
 {
  printf("0~255사이의 수를 입력하시오: ");
  scanf("%d", &input);
 
  hi = input >> 4;
  low = input & 0xf;
  printf("입력한 수의 16진 표기 = %c%c\n", hi+'0'+(hi > 9)*7, low+'0'+(low > 9)*7);

 }
}

10진에서 16진수를 구하기 위해 상위비트는 비트연산자를 통해 구하고

하위비트는 논리연산자로  마스크(?)를 만들어서 변수에 저장 시켰다.

그리고 나서 이를 16진수로 바꾸기 위해서 0의 아스키코드값 48과 더하면 9를 초과하는지의

여부를 알 수 있다. (hi >9)가 참이면 1 아니면 0을 리턴하는 것을 응용하여 9를 초과 했다면

아스키코드값이 대문자 A가 부터 표현하기 위해 7을 더해준다. (브라보!)

3.15(목) float형에 대해서

from Study/C언어 2007/03/15 10:27 view 27926

  scanf("%f", &f);
  printf("%f\n", f);

float형 변수 f에 임의의 수를 입력했을 때 컴파일러가 문제인지 몰라도 12.2를 입력하면

12.200000 이 출력 되는데

  printf("%d\n", i=(int)(f * 100));
  printf("i = %d\n", i%100);

이런 코드를 통해 소수점 이하 두자리를 구하려고 하니깐 19가 나온다..-_-;;

초난강 모드로 빠져 든다.

float형 변수는 다루기 힘든 건가..

3.14(수) Shift연산자

from Study/C언어 2007/03/14 23:32 view 25949
매번 느끼지만 이놈의 쉬프트 연산자는 감이 도통 잡히지 않는다. 왜냐.. 평소에 연산작업을

할 때 10진법을 주로 사용하므로 비트연산을 하는 이놈의 연산자는 친숙하지 않기 때문이다.

그래픽 쪽 프로그래밍 할때는 필수적(마스크나 반전,복원등)이라고 하나 해본적이 있어야지..

하지만 CPU입장에서 a*2를 한번 할 시간에 a<<1를 10번정도 할수 있다는 것은 큰 매리트가

아닐까 싶다.
 
 쉬프트 연산이 곱셈에 비해 불리한 점은 2의 거듭승에 대해서만 곱셈이 가능하다는 점이다.

2배,4배,8배,16배 등만 할 수 있으니 얼마나 불편한 일인가...

3배 : a << 1 + a;
9배 : a << 3 + a;
15배: a << 4 - a;
60배: a << 6 - a << 2;  //64배에서 4배를 제한 60배...와

속도가 중요하다면 이런 코드들도 욕심 부려 볼만 하지 않을까..

3.14(수) 이동효과 나타내기

from Study/C언어 2007/03/14 17:01 view 26244
for(i = 1; i <= 80; i++)
{
 gotoxy(i, 10);
 putch('#');
 gotoxy(i-1, 10);
 putch(' ');
 delay(100);
}


'#'을 왔다리 갔다리 하는 효과를 보여줄 때 출력,지움,시간끌기를 통하여 애니메이션 효과를

보여 줄 수 있다. 새위치에 그리고 이전 위치 지운 다음에 시간을 조금 끌어서 움직이고 있다

는 효과를 낼 수 있다.

3.14(수) 스캔코드와 아스키코드

from Study/C언어 2007/03/14 16:52 view 27546

 벌써부터 어깨가 뻐근한게 죽을 맛이다. 몸이 찌뿌둥 하다고 해야하나.. 앉아 있으면 시간이

물 흐르듯이 지나가서 하루가 이리 빨리 가나 싶다.

ch=getch();
  if (ch == 0xE0 || ch == 0) {
   ch=getch();
}
 getch()함수로 키보드로 입력되는 값을 ch 변수에 저장하고자 할때 아스키코드값 이외의

값을 받게 되면 0xE0(224)나 0 값을 리턴하게 되는데 이때 다시한번 getch()함수로 표준입력

을 변수에 대입시키게 되면 스캔코드를 얻을 수 있게 된다.

 콘솔창내에서 커서를 이동시키고자 할 때 방향키를 누르면 그때 발생되는 스캔코드를 읽어

드려서 커서의 위치를 변경할때 쓰인다.

 

getch 와 _getch

from Study/C언어 2007/03/14 09:12 view 29326

ANSI C에서 정의한 함수가 getch이고 VC++ 컴파일러 에서 지원하는 함수가 _getch이다.

굳이 표준 외에 함수를 만든 이유가 무엇일까 생각해봤는데

#include <conio.h>
#include <stdio.h>

void main( void )
{

   /* Display message until key is pressed. */
   while( !_kbhit() )
      _cputs( "Hit me!! " );

   /* Use _getch to throw key away. */
   printf( "\nKey struck was '%c'\n", _getch() );
   _getch();
}

만약에 이 소스에 getch라는 변수를 추가 했을때 getch()함수를 사용했다면

error C2064: term does not evaluate to a function

와 같은 에러를 볼 수 있다. 이를 방지 하기 위해서 '_'를 붙인 함수를 만든건 아닐까라는

생각이 든다. 그래서 변수명앞에 '_'를 사용하는 것을 금하는 거 아닐까-_-..

터보C(TurboC.h )

from Study/C언어 2007/03/13 18:52 view 27431

출처:http://winapi.com 

커서의 움직임을 보다 효율적으로 하기 위해 TurboC.h을 제작한 것 같다.

이를 사용하면 cmd창내에서의 커서를 간편하게 제어할수 있다.

웬만한 해더파일을 전부 포함시켜 놓았다.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <windows.h>

커서의 사이즈와 타입을 위해 선언해 놓았다.
typedef enum { NOCURSOR, SOLIDCURSOR, NORMALCURSOR } CURSOR_TYPE;
void setcursortype(CURSOR_TYPE c);

화면을 command명령어인 cls를 이용하여 초기화 해준다.
void clrscr();

커서의 좌표를 매개변수로 입력하여 원하는곳에 goto해준다.
void gotoxy(int x, int y);

커서의 현재좌표를 구하는 함수이다.
int wherex();
int wherey();

#define delay(n) Sleep(n)       // n/1000초만큼 시간 지연

rand함수만을 호출 할경우 매번 똑같은 난수가 발생 하므로 time함수와 srand함수를 이용하여  난수의 초기값을 매번 다르게 해준다.
#define randomize() srand((unsigned)time(NULL))  // 난수 발생기 초기화
#define random(n) (rand() % (n))     //0~n까지의 난수 발생