NOTICE 
회사에서 Log관련 모듈을 만들어야 하는데
너무 괴로워서 다른 문서를 살펴보다가 va_list, va_arg 같은걸 보고, 일은 하기 싫고 코딩을 안하면 불안하고 해서 만들어 봤음... (생산성 0%...?)

GeSHi © 2004, Nigel McNie
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdarg.h>
  4. #include <stdlib.h>
  5.  
  6. int csharpStyleFormat(const char *format, ...)
  7. {
  8.         int extractIdx = -1;
  9.         int argArray[256];
  10.         char buf[1024] = {0,};
  11.         int offset = 0;
  12.  
  13.         va_list ap;
  14.         va_start(ap, format);
  15.  
  16.         const char *cpst = format;
  17.         const char *st = format -1;
  18.         const char *end = NULL;
  19.  
  20.         while (st = strchr(st+1, '{'), st) {
  21.                 char tmpBuf[256] = {0,};
  22.  
  23.                 end = strchr(st+1, '}');
  24.                 memcpy(tmpBuf, st+1, end-(st+1));
  25.                 int index = atoi(tmpBuf);
  26.  
  27.                 while (index > extractIdx) {
  28.                         argArray[++extractIdx] = va_arg(ap, int);
  29.                 }
  30.  
  31.                 int length = (int)(st - cpst);
  32.                 memcpy(buf+offset, cpst, length);
  33.                 offset += length;
  34.                 offset += sprintf(buf+offset, "%d", argArray[index]);
  35.  
  36.                 cpst = end + 1;
  37.         }
  38.  
  39.         int length = strlen(cpst);
  40.         memcpy(buf+offset, cpst, length);
  41.  
  42.         va_end(ap);
  43.  
  44.         printf(buf);
  45.         return 0;
  46. }
  47.  
  48.  
  49. int main()
  50. {
  51.         csharpStyleFormat("{2} = {0} + {1}\n", 10, 20, 10+20);
  52.         return 0;
  53. }
Parsed in 0.047 seconds

코드는 이렇다.

csharpStyleFormat("{2} = {0} + {1}\n", 10, 20, 10+20);
라고 코드에 쓰면
30 = 10 + 20
이라고 출력됨.

변수타입은 오로지 int만 되는데 {2:d} 이런식으로 변수형을 지정하게 하면 다른 변수들도 가능할듯.
하지만 귀찮..

코드는 에러처리, 최적화는 생각도 하지 않았음...

아 가슴이 답답해...
2008/03/09 22:11 2008/03/09 22:11
,
  
트랙백 0  |  댓글 0  |
Stringizing Operator (#)
#define stringer( x ) printf( #x "\n" )

int main()
{
    stringer( In quotes in the printf function call\n );
    stringer( "In quotes when printed to the screen"\n );   
    stringer( "This: \"  prints an escaped double quote" );
}

이런식으로 매크로를 사용할 경우 전처리를 거치면
 
int main()
{
   printf( "In quotes in the printf function call\n" "\n" );
   printf( "\"In quotes when printed to the screen\"\n" "\n" );
   printf( "\"This: \\\" prints an escaped double quote\"" "\n" );
}

이런 코드로 바뀌고

In quotes in the printf function call

"In quotes when printed to the screen"

"This: \" prints an escaped double quotation mark"

결과는 이렇다.


Charizing Operator (#@)
#define makechar(x)  #@x

a = makechar(b);
이렇게 정의하고 사용을 하면
a = 'b';
이런 의미가 된다.

Token-Pasting Operator (##)
#define paster( n ) printf( "token" #n " = %d", token##n )

int token9 = 9;
paster( 9 );
이렇게 하면

printf( "token" "9" " = %d", token9 );
이렇게 바뀌고 결돠적으로
printf( "token9 = %d", token9 );
이렇게 된다.

정리 참고는 MSDN




정리한 이유.

근래에 코드 정리를 하다가 로그를 남기는 부분을 보게되었다.
#ifdef _DEBUG
  LOG("블라블라블라");
#endif

이런식으로 되어 있는데 메크로를 잘 이용하면 저렇게 #ifdef 블럭으로 감싸지 않고도 자동으로 릴리즈바이너리에 포함되지 않도록 할수 있다.

간단하게
#ifdef _DEBUG
  #define LOG(x)    writeLog(x)
#else
  #define LOG(x)
#endif
이렇게 만들수도 있다.
LOG가 메크로고 실제 쓰는 함수는 writeLog일때 릴리즈 모드일때는 아예 writeLog 코드는 사라져 버린다. 하지만 이건 인자가 하나일 경우에만 가능하다. 가변인자(printf같은것)를 사용하는 로그함수일 경우에는 약간더 복잡해 지는데 C99 표준이 지원되는 컴파일러일 경우에는
#ifdef _DEBUG
  #define LOG(fmt, ...) writeLog(fmt, __VA_ARGS__)
#else
  #define LOG(fmt, ...)
#endif
이렇게 하면 전처리기에서도 가변인자를 처리할수 있고 보는것 처럼 릴리즈 모드에서는 소스에 포함되 않게도 할수 있다.

하지만... 비주얼스튜디오 2003 이하는 __VA_ARGS__ 라는 메크로를 지원하지 않는다.
현재로서는 가장 최신인 2005만 지원하고 있다. 물론 저게 없다고 가변인자를 사용하는 함수들을 메크로로 치환시키지 못하는것은 아니다.
#ifdef _DEBUG
  #define LOG writeLog
#else
  #define LOG
#endif

void writeLog(char* fmt, ...);
이렇게 하면 분명 디버그 모드일때는 LOG가 writeLog로 치환되니
LOG("%s = %d", szName, value);
이것이
writeLog("%s = %d", szName, value);
이렇게 정상치환된다.(물론 당연한 메크로의 기능일 뿐이다.)

하지만 릴리즈 모드에서는?
("%s = %d", szName, value);
...이런 코드가 덩그러니 남아버리게 된다.

어떻게 하면 저 덩그러니 남는걸 없앨수 있을까를 고민하다가 어디선가 찾아낸 방법.
바로 Token-Pasting Operator (##) 를 사용하는 방법이다.
(GPG에서 누군가 올렸었던것. 고민중에 돈오하듯이 깨우침.)
#ifdef _DEBUG
  #define LOG writeLog
#else
  #define LOG /##/
#endif

void writeLog(char* fmt, ...);
이렇게 해놓으면 릴리즈 모드에서 LOG를 사용한 코드는
//("%s = %d", szName, value);
이렇게 치환이 되고 컴파일 과정에서 완벽하게 사라진다.

아싸 조쿠나.
2007/02/03 13:41 2007/02/03 13:41
출처 : Effective C++. 항목 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자.
상속을 사용한 객체를 할당하고 해제할때 기반클래스의 포인터를 사용하여 할당 및 해제를 사용하는 경우가 무척 많다. 이 경우 기반 클래스 및 자식 클래스의 소멸자가 virtual 이 붙어야 하는 경우와 붙지 말아야 하는 경우가 있다.
class Base {
    // ...
};

class Derived : public Base {
    // ...
};

...

Base* object = new Derived();
// object 사용
delete object;
위와 같은 코드가 있을 경우 Base 의 포인터를 이용해 할당을 하고 Base형의 포인터를 이용해 메모리를 해제한다.
이 경우의 문제는 Base의 소멸자가 virtual 이 아니라면 소멸되는 과정에서 상속받은 클래스의 소멸자가 정상적으로 호출되지 않게 되서 정의되지 않는 동작이 일어나게 된다. 즉 기반클래스의 소멸자는 virtual로 선언 되어야 한다. 뒤집어 이야기 한다면 virtual 로 선언되지 않은 소멸자를 같는 클래스는 기반클래스(일반적인 의미의 인터페이스를 제공하는)로 사용되 않을 확률이 높다.

예를 들어
class Point{
public:
    Point(int x, int y);
    ~Point();
private:
    int x, y;
};
은 딱 64비트 크기에 맞는 크기다. 이 경우 64비트 레지스터에 쏙 들어감으로써 최적화된 코드라고 볼수도 있다. 하지만 이 Point 클래스의 소멸자를 virtual 로 선언하는 순간 각 Point 클래스의 객체는 버추얼테이블 포인터를 가져야 하며 64비트 크기를 넘어서게 된다. 즉 이런 경우에는 가상소멸자는 어울리지 않는다.

다시 한번 강조하면 클래스가 가상함수를 하나 이상 가지고 있는 경우에만 소멸자를 virtual로 선언한다 를 기억해 둬야 한다.

그런데 많은 실수중에 하나가 C++ , STL 에서 제공하는 표준 클래스들을 상속받는 경우다.
class GoodString: public std::string{
};
// ...
GoodString *pss = new GoodString("어쩌구저쩌구");
std::string* ps = pss;
delete ps;              // GoodString의 소멸자가 호출되지 않음. 에러발생.
std::string, std::vector 등의 클래스들은 virtual 소멸자를 갖지 않으므로 이들을 상속받아 추가적인 내용을 구현하는 경우 기반클래스의 포인터로 할당하고 해제할 경우 정의되지 않는 동작이 일어나므로 이런식으로 사용해서는 안된다.
사견으로 위와 같이 기반클래스의 포인터로 메모리 해제만 안하면 별 문제없는것 같다. 즉 처음부터 끝까지 상속받은 클래스의 포인터를 사용하면 된다.
물론 virtual 소멸자가 아니지만 기반클래스로 제작된 클래스도 있다.
ex) 부스트의 noncopyable
2006/10/25 16:20 2006/10/25 16:20
,
  
트랙백 0  |  댓글 0  |
출처 : Effective C++ 3판. 항목 3 : 낌새만 보이면 const를 들이대자.

const 키워드의 위치에 따른 상수성의 변화
char greeting[] = "Hello";

char *p = greeting;              // 비상수 포인터, 비상수 데이터
const char* p = greeting;        // 비상수 포인터, 상수 데이터
char* const p = greeting;        // 상수 포인터, 비상수 데이터
const char* cosnt p = greeting   // 상수 포인터, 상수데이터

* 에 대한 위치가 중요하다.
void f1(const Widget *pw);       // f1은 상수 Widget 객체에 대한
                                 // 포인터를 매개변수로 취합니다.
void f2(Widget const *pw);       // f2도 마찬가지
2006/10/25 16:19 2006/10/25 16:19
,
  
트랙백 0  |  댓글 0  |

회사에서 코드를 보던중

struct Object
{
    unsigned value : 16;
};

이런 코드가 있길래 뭔가 하고 테스트를 해봤더니 사용하는 비트수를 제한하는 것 같다. (아마도) 테스트를 한 코드는
#include <iostream>
using namespace std;

struct TestStruct
{
  unsigned value : 16;
};

struct TestStruct2
{
  unsigned value;
};

int main()
{
  TestStruct a;
  TestStruct2 b;

  a.value = 65535;
  b.value = 65535;

  cout<<"sizeof TestStruct : "<<sizeof(TestStruct)<<endl;
  cout<<"value TestStruct::value : "<<a.value<<endl;
 
  a.value++;
  cout<<"value TestStruct::value++ after"<<endl;
  cout<<"value TestStruct::value : "<<a.value<<endl;
 
  cout<<"sizeof TestStruct2 : "<<sizeof(TestStruct2)<<endl;
  cout<<"value TestStruct2::value : "<<b.value<<endl;
 
  b.value++;
  cout<<"value TestStruct2::value++ after"<<endl;
  cout<<"value TestStruct2::value : "<<b.value<<endl;
  return 0;
}

이런 느낌!!
결과는
sizeof TestStruct : 4
value TestStruct::value : 65535
value TestStruct::value++ after
value TestStruct::value : 0
sizeof TestStruct2 : 4
value TestStruct2::value : 65535
value TestStruct2::value++ after
value TestStruct2::value : 65536

음...냐..

....
이런것도 있었구나!!!!

ps.
맨 처음에 위에 있는 코드만 보고
- 아 그냥 원래 데이타타입이랑 똑같은 메모리 공간을 사용하는데 비트수만 제한하나 보군
이라고 생각했는데
struct UnionObject
{
  union
  {
    struct
    {
      unsigned small1 : 16;
      unsigned small2 : 16;
    };
    unsigned int value;
  };
};
이 코드를 보니 뭔가 이상하다. 문맥상은 value 와 small1, small2 의 메모리 공간이 공유 되게는걸 의도한것 같은데 위의 결과를 보면 비트수를 제한해도 4바이트를 차지하니 value 와 small1 만 공유될것 같은데... 라고 생각해서 테스트를 해보니 UnionObject는 그냥 4바이트만 차지하는게 아닌가?

그래서 이것저것 테스트
struct TestStruct
{
  unsigned value : 8;
};
이 구조체는 4바이트를 차지하는데
struct TestStruct
{
  unsigned value : 8;
  unsigned value2 : 8;
  unsigned value3 : 8;
  unsigned value4 : 8;
};
이 구조체 역시 4바이트만 차지함. 저기서 하나 더 추가하거나 제한 비트숫자를 1만 올려도 8바이트를 차지한다.

음... 저게 원래 저런건지 아니면 비주얼스튜디오의 4바이트 정렬 옵션 때문에 저런 결과가 나오는건지는 잘 모르겠다.
냠... 책에서 읽었던 기억이 살포시 나는데 쓸일이 없다보니 이미 까먹은.... -_-;
2006/09/05 14:03 2006/09/05 14:03
,
  
트랙백 0  |  댓글 3  |

1 Pro* C/C++ #

Oracle 에서 제공하는 Embaded SQL을 의미
pc 확장자의 파일에 Embaded SQL 구문을 포함하여 C/C++ 문법으로 코딩하여 proc 라는 프리컴파일러로 컴파일하면 c/cpp 파일을 생성한다 해당 파일을 컴파일러로 컴파일해서 클라이언트 프로그램에서 Oracle 로 접속한다.

2006/01/25 17:37 2006/01/25 17:37
, ,
  
트랙백 0  |  댓글 0  |

1 컴파일러 설정 #

가장 중요한 사항이라고 할수 있는데 옮길려고 하는 Window 의 소스를 VC++로 컴파일 할때 워닝레벨을 4로 올리고 컴파일 한다.
워닝 레벨 4를 사용할때의 이점

2 전처리코드 #

2.1 컴파일러 구분 #

컴파일러 구분 매크로
Microsoft Vusial C++ _MSC_VER
GNU gcc __GNUG__

두가지 매크로 다 해당 컴파일러로 컴파일 할때 정의되어 있으며 컴파일러의 버젼값을 나타내고 있다.
#ifdef _MSC_VER
        #include "../ECMMat10/NEWMAT.h"
        #include "../ECMMat10/NEWMATAP.h"
#endif
#ifdef __GNUG__ 
        #include "../newmatlib/newmat.h"
        #include "../newmatlib/newmatap.h"
#endif

2.2 pragma once #

pragma once 코드는 vc++ 에만 존재
#if (_MSC_VER > 1200) 
#pragma once 
#endif

이런석으로 처리해 주거나

#ifndef XXXX
#define XXXX

[...] // A

#endif

로 수정해 준다.

3 데이타 타입 #

3.1 64비트 정수형 #

컴파일러 데이타선언 출력포멧
VC __int64, unsigned __int64 %lld(signed) %llu(unsigned) %llx(hex)
gcc long long, unsigned long long %I64d(signed) %I64u(unsigned) %I64x(hex)

// DiffResKey 데이타타입(64q비트) 설정 
#ifdef _MSC_VER 
    typedef unsigned __int64 DiffResKey;
#endif
#ifdef __GNUG__ 
    typedef unsigned long long DiffResKey;
#endif
2005/11/25 17:39 2005/11/25 17:39
,
  
트랙백 0  |  댓글 0  |
1 CallBack 함수
2 구현방법
3 구현예
4 Comment


1 CallBack 함수 #

음... 윈도우에서는 사용자가 정의 하고 운영체제에 의해서 호출되는 함수를 CallBack 함수라고 한다. 예를 들어 타이머를 설정할때 넣는 함수등등

2 구현방법 #

자체적인 CallBack 함수는 함수포인터를 이용해서 구현된다.(윈도우 CallBack함수가 뭘로 구현됐는지는 모르겠지만 아마도 함수포인터일듯) 일반적으로 라이브러리에서는 함수포인터 변수를 가지고 있고 사용자가 함수를 정의해서 함수포인터에 넣어주면 라이브러리나 운영체제 등에서 필요한 시기에 그 함수를 호출하는 것이다.

3 구현예 #

dll의 정보를 exe에서 출력을 하는것을 구현하는 방법은
  1. exe쪽에서 객체를 넘겨서 DLL에서 출력루틴을 호출
  2. 윈도우 메세지를 이용해서 DLL에서 exe의 윈도우쪽에 메세지를 보내 exe에서 출력루틴을 호출
  3. dll에서 exe쪽에 변수포인터를 넘기고 이 변수를 통해 진행상황을 공유. (이 경우는 exe쪽에서 일정시간마다 변수의 변화값을 체크)
일단 생각나는건 이 세가지이다.
1번의 단점은 객체 출력루틴이나 객체가 변경된다면 DLL을 수정해줘야 하는 어이없는 상황이 생긴다. 장점은 DLL쪽에서 호출하게 되니깐 exe쪽에서는 출력에 관한 사항을 신경쓸 필요가 없다는것.
2번 단점은 윈도우객체에서 처리해야 한다는것.
3번의 단점은 exe에서 귀찮은 코드가 많이 들어간다는것. 장점은 dll에서는 신경쓸 필요가 없다는것.

3가지다 그다지 좋은 코드가 아니라고 생각이든다. 이것을 CallBack 형식으로 구현하는 것이다.

DLL단에서는 함수포인터 변수를 가지고 있고 이걸 exe에서 변경할수 있게 익스포트 해놓는다. 이 함수포인터는 직접 호출되지 않고 DLL에서 내부 함수로 한번 랩핑을 해서 관련정보를 구조체에 집어넣고 이 구조체를 함수포인터의 매개변수로 넣고 함수포인터를 호출하는것 으로 구현을 한다.

exe 단에서는 해당 함수포인터의 형대로 함수를 구현해 놓고 이 함수가 호출될때 마다 넘어오는 매개변수를 이용해서 정보를 출력해주는 것이다.

DLL 단에서의 구현예
아래는 함수포인터를 깔끔하게 선언하기 위한 typedef 문과 함수포인터가 매개변수로 사용하는 구조체선언이다.
// 메세지콜백함수 원형
typedef void (*MSG_FUNC)(void*);

// BINWAFER SSA 콜백함수 구조체 원형
struct AFX_EXT_CLASS BINSSA_MSG_INFO
{
        int             nNowIdx;
        int             nMaxIdx;
};

클래스 내부에 함수포인터인 m_FuncMsg를 가지고 있고 이 변수를 변경시킬수 있는 SetMessageFunction( MSG_FUNC func ) 함수를 구현한다. 이 함수에 의해서 exe에서 사용자가 연결한 함수를 함수포인터에 연결하게 된다.
class AFX_EXT_CLASS CSample : public vector<int>
{
private:
        // 외부에 진행상황을 알리 위한 메세지함수포인터
        MSG_FUNC        m_FuncMsg;


public:
        CSample ();
        virtual ~CSample ();

        void    Run();
        void    SetMessageFunction( MSG_FUNC func ) { m_FuncMsg = func; }
};

뭔가를 열심히 실행하는 함수는 중간중간에 exe에 결과를 보내주기 위해서 m_FuncMsg를 호출하게 된다.(만약 exe에서 m_FuncMsg에 함수를 연결해 두지 않았을 경우에는 런타임에러가 발생하게 되니 if문으로 처리하던지 아무것도하지 않는 함수를 정의해놓고 생성자에서 생성될때 그함수에 연결해 놓도록 한다.)
void CSample::Run()
{
        int nCnt= (int)size();
        for( int i=0; i < nCnt; i++ )
        {
                // 백터의 각 엘레멘트를 어떻게 저떻게 사용한다.

                // 엘레멘트 한개 만큼 뭔가가 처리됐다. 
                // 처리된 상태를 콜백함수를 통해 exe쪽에 보낸다.
                BINSSA_MSG_INFO msgInfo;
                msgInfo.nNowIdx = i;
                msgInfo.nMaxIdx = nCnt;
                m_FuncMsg( &msgInfo );
        }
}

exe에서의 구현예
dll에 정의된 콜백함수에 맞추어 해당함수를 사용자가 정의한다.
// BIN SSA 메세지 함수
void BIN_SSA_MSG( void* lpInfo )
{
        BINSSA_MSG_INFO* pMsgInfo = (BINSSA_MSG_INFO*)lpInfo;

        CString strTmp;
        strTmp.Format("진행:%d/%d", pMsgInfo->nNowIdx+1, pMsgInfo->nMaxIdx );
        theApp.Message(strTmp);
}
이 함수를 dll의 객체에 연결해 준다.
void ExeSampleWnd::OnLButtonDown()
{
        CSample object;
        object.SetMessageFunction( BIN_SSA_MSG);
        object.Run();
}

자 이렇게 해두면 ExeSampleWnd 클래스의 객체에서 OnLButtonDown 함수가 실행될때 DLL에서 정의된 CSample의 object라는 객체를 만들고 그 객체의 Run 이라는 함수가 수행될때 수행상태가 변할때마다 BIN_SSA_MSG 라는 exe에 정의된 함수가 호출되서 진행 : 3/30 같은 메세지를 출력하게 된다.
2005/10/25 16:29 2005/10/25 16:29
,
  
트랙백 0  |  댓글 0  |

1 워닝 레벨4로의 이점과 피하기 힘는 경고의 해결법 #

Debugging Application for Microsoft .NET and Microsoft Windows 에서 참조.
MS 컴파일러 기준.

컴파일시의 모든 경고를 나타나지 않도록 한다는 것은 컴파일러가 추측하는 부분이 없도록 가능한 모호한 부분이 없도록 코드를 작성하는 것을 의미한다. 가장 대표적인 경고로 부호있는 형식과 부호없는 형식으로 변환할때 항상보고 되는 C4244(데이터가 손실될수 있습니다.)가 있으며 이것은 사용자가 컴파일러에게 적절한 형변화을 명시적으로 코딩함으로써 제거 할수 있다.

사실 책에서는 워닝레벨은4! 모든 경고를 에러로 설정해라! 라고 나와있다. 음 나도 경고를 바로바로 올바르게 수정하기 위해서 책이 옳다고 생각은 하지만 실제 컴파일 할때는 그렇게 하지 않고 있다. 으음 상당히 성가시게 느껴져서 인듯. 고쳐야지 -_-;;;

1.1 이점 #

워닝레벨을 4로 올리게 되면 레벨3 까지는 나타나지 않던 워닝중 함수의 매개 변수를 함수내부에서 한번도 사용하지 않았다는 경고와 지역변수중 초기화 이후 한번도 사용하지 않은 변수에 대한 경고가 나타난다. 두가지의 경고를 제거하는것은 런타임시의 속도를 올리는 대에도 많은 도움이 될것이다.
물론 잠재적인 버그의 예방과 이후 유지보수의 유리함은 당연한것이다.

1.2 문제해결 #

하지만 워닝레벨을 4로 올렸을 경우에는 Visual C++과 함께 제공되는 STL에서도 수많은 경고메세지가 나타날수 있다. 이런 경우에는
#pragma warning ( push, 3 )
#include "IDoNotCompileAtWarning4.h"
#pragma warning ( pop )
이런 형식을 사용해서 특정 라이브러리와 자신의 코드를 다른 워닝레벨에서 컴파일 할수 있다. 또한 #pragma 지시자를 사용해서 개별적인 경고를 비활성화 시키는 것이 가능하다.
// Ex::구조체/공용제의 이름이 없습니다. 를 비활성화
#pragma warning ( disable : 4201 )

struct Vector3
{
        union
        {
                double Arr[3]
                struct
                {
                        double X;
                        double Y;
                        double Z;
                }
        }
}

#pragma warning ( default : 4201 )
하지만 이런 방식은 이식가능성을 상당히 떨어뜨리게 된다.
그리고 위에도 잠깐 언급했던 C4100( 참조되지 않은 매개변수 ) 경고는 클래스의 상속구조를 사용시 불가피 하게 발생할수 있는 경고이다. (특정 자식클래스들이 사용하기 때문에 예약된 매개변수 라던지...) 이 경우는 사용하지 않는 함수의 선언부의 변수명을 주석처리 해주면 해결된다.
void CMakeTreeDoc::SetModifiedFlag(BOOL bModified )
{
}
이걸
void CMakeTreeDoc::SetModifiedFlag(BOOL /*bModified*/ )
{
}
으로 처리할수 있다. 만약 STL을 사용하고 있다면 STL헤더를 미리 컴파일된 헤더파일에서만 include 하는것으로 경고에 대한 문제를 좀더 쉽게 회피할수 있다.
2005/08/25 16:42 2005/08/25 16:42
,
  
트랙백 0  |  댓글 0  |
#include "stdio.h"

print( int i1, int i2, int i3 )
{
printf("i1=%d, i2=%d, i3=%d
", i1, i2, i3 );
}

void main()
{
int i=0;
print(i++, i++, i++ );
}

예~~전에 이것저것 해보다가 알게된것 C/C++ 표준인지는 잘 모르겠지만 내가 많이 다뤄본 컴파일러에서는 여러가지 인자를 받는 함수의 경우는 오른쪽 부터 넘어(실행) 된다.

실행을 강조한 이유는 위의 소스코드의 결과는 단순히 생각하기에
i1=0, i2=1, i3=2
라고 생각하기 쉽지만 실제로는
i1=2, i2=1, i3=0
으로 출력된다. 즉 단순히 인자가 넘어가는 순서(아마도 스택에 넣는순서) 뿐만 아니라 함수인자가 어떤 메소드의 리턴값을 바로 받을때 메소드의 실행순서에도 영향을 미친다는것
2005/04/21 19:15 2005/04/21 19:15
  
트랙백 0  |  댓글 0  |
 이전  12   다음 

전체 (842)
잡담 (529)
메모 (7)
감상 (109)
일기 (4)
참고 (187)
기록 (6)
fotowall :: ncloud tattertools RSS Feeds today : 59   yesterday : 112
total : 369461