고전적 방법

// 10라인부터 100라인까지 주석 하기
:10, 100 s/^/\/\//g

// 10라인부터 100라인까지 주석 해제
:10, 100 s/^\/\///g


// 블록만큼 주석 하기
1. Ctrl + v 로 원하는 만큼 블록지정
2. Shift + i
3. // 입력
4. ESC 2번 누름

// 블록만큼 주석 지우기
1. Ctrl + v 로 원하는 만큼 블록지정
2. x

:
signed/unsigned 정수 연산 때문에 생긴 오류 .

다음 코드를 보자.

typedef signed long long int64;
typedef unsigned long long uint64;

int main(int argc, char *argv[])
{
    const uint64 MAX_SIZE = uint64(2)*1024*1024*1024;
    int64 size = int64(2)*1024*1024*1024-1;

   int64 result = size - MAX_SIZE;
    printf("%lld\n", result);

    if( size - MAX_SIZE >= 0 ){
        printf("NO\n");
    }
    else{
        printf("OK\n");
    }
    return 0;
}

여기서 중요한 것은
signed 64비트 정수에서 unsigned 64비트를 빼는 것이다.
size 는 MAX_SIZE 보다 1이 작다. 결과는 물론 -1 이 나와야 한다.

출력 결과는 어떻게 될까 ?
희한하게도 다음과 같다.

-1
NO

이 문제 때문에 엄청난 시간을 낭비했다.
디버깅 끝에 위와 같은 문제를 발견했는데, if문 안에서의 결과가 우리가 예상하는 바와는 반대이다.
결국 MAX_SIZE 를 signed 정수로 선언하여 문제를 해결 하긴 했지만,
뭔가 찜찜한 마음을 지울 수는 없다.

혹시 이유를 아시는 분은 답변 좀 알려주세요.

(아 컴파일러는 gcc (GCC) 4.0.4 20060507)


+ 32비트 정수로 다시 테스트 해 보니 역시 같은 결과가 나온다.
결국은 if 문 안에서 계산할 때는 타입에 신경써야 한다는 결론은 얻었다 -_-;;
:
비주얼 스튜디오에서 쓰는 단축키 중 쓸만한 놈을 모아 봤습니다.
전체 단축 키는 아래 페이지에서 볼 수 있습니다.
여기서는 바로 가기 키라는 단어를 사용하고 있습니다.
그리고 VS 6.0 이라고 되어있는데 .NET 2002 .NET 2005 에서 테스트한 결과 잘 동작 하였습니다.
http://msdn2.microsoft.com/ko-kr/library/dyk768z6(VS.80).aspx




Ctrl + J 또는 Ctrl + [SPACE]
변수, 함수 자동 완성, 아무것도 타이핑 하지 않은 상태에서도 사용 가능

Ctrl + Shift + B
빌드
F7 을 사용하면 되지만, .NET 2002 에서는 이 단축키가 안 먹힌다.

Ctrl + ]
매칭 되는 괄호로 이동한다. 괄호가 아닌 곳에서 사용하면 동작 하지 않는다.

Ctrl + G
줄(line)로 이동할 수 있는 박스가 뜬다.

Ctrl + K Ctrl + C
컨트롤 K 하고 컨트롤 누른 상태에서 C 를 누르면 된다.
해당 라인을 주석 처리 한다(//). 만약 주석이 있으면 한 번더 //가 생긴다.
물론 계속 하면 계속 생긴다.
이것은 블럭에 대해서도 잘 동작한다.

Ctrl + K Ctrl + U
주석을 지운다. 위 단축 키와 반대 동작이라 생각하면 쉽다.
당연히 주석이 없으면 아무 동작도 하지 않으며, //가 여러개 있는 경우
한 번만 //를 지운다.
만약 /가 홀 수개 있는 경우 이 동작을 계속하면 / 하나만 남게 된다.
즉, / 하나에 대해서는 동작하지 않는다.

Ctrl + K Ctrl + K
해당 라인 책갈피 지정.
블럭에 대해서 동작 하지 않으며, 커서가 있는 위치에만 적용된다.
이것은 토글이다. 즉, 책갈피가 지정된 라인에서 이 동작을 하면 책갈피 해제가 된다.

Ctrl + K Ctrl + N
다음 책갈피 지정 라인으로 이동. 만약 마지막 책갈피에서 수행하면 첫번째로 이동한다.

Ctrl + K Ctrl + P
위 동작과 반대. 이전 책갈피로 이동한다.

Ctrl + K Ctrl + L
지정한 책갈피를 모두 지운다.
:
사용자 정의 키워드에 색깔 넣기
자신만의 라이브러리를 만들고, 자체 정의한 변수 타입을 사용할 때, 컬러가 나오게 하는 방법이다.

vim 설정
.vimrc 파일에 다음과 같이 쓴다.
au Syntax cpp call MySyntax()
function MySyntax()
    syntax keyword myType int8 int16 int32 int64 uint8 uint16 uint32 uint64
    hi link myType Type
endfunction


visual studio 7.0 설정
이건 더 쉬움.

devenv.exe 실행 파일이 있는 경로에서
(보통은... C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE)
USERTYPE.DAT 라는 파일을 만들고, 아래와 같이 키워드만 입력하면 된다.
int8
uint8
int16
uint16
int32
uint32
int64
uint64


기본 세팅은 일반 keyword 와 같이 BLUE로 지정되어 있을 것이다.
설정 방법은 [도구]-[옵션] 선택후 /환경-글꼴 및 색/ 에서 고르기만 하면 된다.

사용자 삽입 이미지


























사용자 삽입 이미지
:
http://baboc.tistory.com/49

위 글에서 enum 이 #define 을 대신하여 상수 정의 용도로 사용할 수 있다는 것을 썼다.
물론 #define 도 미리 정해진 타입이 없긴 하지만
사람들은 암묵적으로 4바이트 정수 타입으로 인식하고 있는 듯 하다.

enum 으로 선언된 상수들은 일반적으로 signed int 형을 갖게 되는데,
엄밀히 말하자면, 4바이트 정수형 이하의 타입 중에 항당 된 값을 가장 optimal 하게 표현하는
자료형을 갖게 된다.
다만, 4바이트 정수 이하 타입들의 기본 연산자가 모두 일반 int 형 연산자를 사용하므로
그것이 그렇게 중요한 사항은 아니다.

문제는 다음과 같이 signed int 의 범위를 벗어난 값을 할당하려고 할 때이다.
enum{
    TWO_GIGA_BYTE = 2*1024*1024*1024,
}

위와 같이 선언하면 의도한 대로 값이 할당되지 않는다.
다음과 같이 캐스팅을 하면,
#ifdef _WIN32
typedef unsigned __int64  uint64;
#elif defined(__linux__)
typedef unsigned long long uint64;
#endif

enum{
    TWO_GIGA_BYTE = uint64(2)*1024*1024*1024,
}

타입이 변환되는 것처럼 보인다.
나의 테스트 결과 linux 에서는 아무런 문제 없이 의도한 대로 값이 할당된다.
하지만 windows 위 VC++ 6.0, VC++ 7.0 에서는 위와 같이 하여도 제대로 된 값이 할당 되지 않는다.
온갖 테스트를 다 해 보았지만, 결국 성공하지 못 했다.
아마도 이쪽 컴파일러가 64비트 정수의 타입 변환에 대해 별 다른 처리를 해 주지 않은 게 아닐까 생각된다.
결국, signed int 범위를 벗어난 범위에 대해서는 linux 환경에서만 쓸 수 있다는 것이다.
나는 linux 와 win32 환경에서 모두 돌아가는 코드를 작성해야 하므로,
이 경우에는 결국 다른 선택 없이 const 변수를 사용해야만 한다.

아 ... -_-;;
그동안 enum 으로 정의한 상수를 대부분 사용하여 왔는데,
이렇게 되면 코드의 일관성이 떨어지는 결과를 낳게 된다.
물론 const 정수를 사용한 코드도 많지만 ...
일부는 시즈모드 일부는 퉁퉁퉁퉁퉁 !!!

====

<추가 테스트>

typedef unsigned int  uint32;

enum{
    TWO_GIGA_BYTE = uint32(2)*1024*1024*1024,
}

void function()
{
    uint32 i = TWO_GIGA_BYTE;
    print(i);
}

위와 같이 32비트 unsigned int 타입으로 선언하고 값을 받으면 잘 된다.
결국 win32 환경에서 32비트 타입까지는 잘 지원한다는 얘기 !
:

cvs 나 subversion(이하 svn) 등 버전 컨트롤 프로그램을 사용하면서
소스 코드 변경 사항을 적당히 조절해서 커밋할 필요가 있다는 것을 느낀다.

특히 svn 에서는 로그가 체인지 셋(change set)단위로 남으므로
의미있는 변경 사항 별로 따로 커밋 하는 것이
나중에 다른 개발자들이 변경 사항을 익히는(follow up) 데도 도움이 되고
버그 추적할 때에도 중요한 역할을 한다.

그러나 때로는 엄청나게 많은 변경 사항에 대해서 얼마 단위로 어떻게 나눠서 커밋 할 지 고민 될 때도 있다.
물론 이런 고민 없이 그냥 한꺼번에 커밋하는 경우도 있는데,
이런 경우 미처 확인 하지 못한 코드 실수가 그대로 반영될 가능성이 높아진다.
보통 올바른 커밋 습관은 커밋 직전에 변경 사항을 diff 해 보는 것이다.

당연히 의미 있고 서로 연관 있는 체인지 셋내에서는 논리적으로 코드의 흐름을 파악하기 쉽다.
따라서 만약 마지막 체크 타임에 논리적 오류를 조금이라도 더 발견할 가능성이 높다.
또한 원래 버그라는 것이 시간적으로 개발 시점과 가까운 시일일수록 쉽게 잡을 수 있기 때문에
이와 같은 마지막 확인 작업은 매우 매우 매우 중요하다.

하지만 변경 사항이 많아지고 여러 가지 기능에 대한 변경/개선 등이 섞여 있으면
최후의 기회를 놓칠 가능성이 매우 높아진다.
우선은 변경된 부분이 많으므로 마지막 확인 작업 자체를 생략할 가능성이 높고
둘째로 집중해서 코드를 논리적으로 해석할 수 있는 가능성도 줄어든다.

결국 코드 수정도 계획적으로 할 필요가 있다.
아니면 소스 커밋이라도 적당한 단위로 나누어서 예쁘게 해야한다.
적당한...이라는 말이 사실은 제일 어렵기도 하지만 -_-;;

:

C++ enum

프로그래밍 2007. 11. 13. 22:02 |

enum 은 정수형 type 정의와 상수 정의에 사용된다.
보통 unnamed enum 을 사용하여 #define 을 대신하여 상수 정의에 사용되는데
예를 들면,
// define
#define     TYPE_A      0
#define     TYPE_B      1
#define     TYPE_C      2
#define     TYPE_D      3

// unnamed enum
enum {
    TYPE_A,
    TYPE_B,
    TYPE_C,
    TYPE_D,
};

얼핏 위 두 가지는 비슷해 보이긴 하지만 조금(혹은 많이?) 다르다.
물론 당연히 C++ 에서는 후자를 권장한다.
(reference : Effective C++, Scott Meyers)

내가 일하는 곳에서도 모든 상수 정의는 enum을 사용하고 있다.
가장 큰 장점은 디버깅 시 symbol name으로 표시된다는 점이다.
(이건 디버깅을 해 보면 쉽게 알 수 있다)

또한 enum 은 scope 를 가진다.
중괄호 '{}' 안에서도 scope를 가지며,
클래스 내에서 private, public 등 access modifier 에 대해서도 영향을 받는다.

또 이건 장점이자 동시에 단점이 될 수도 있는데,
별도 값을 assign 하지 않으면 값이 알아서 1씩 증가한다는 점이다.
편리함에서 장점이 될 수 있지만, 의도치 않게 중간에 엉뚱한 값이 끼어드는 경우
상수 값 자체가 바뀌어 버리므로 예상치 못한 오류를 일으킬 수도 있다.

또한 중요한 단점인데,
명확하게 type 이 정의되지 않는다는 점이다.

예를 들면,
enum{
    TWO_GIGA = 2*1024*1024*1024,       // 2GB ?
};

이와 같이 선언된 경우,
원래 의도는 TWO_GIGA 를 2GB 대신으로 사용하고자 하였겠지만,
실제로는 일반 4바이트 정수 값으로 0x800000 값이 들어가 있다.
즉, signed int 타입으로 정의되고 그 값은 -2147483648 이다.

제대로 하려면 다음과 같이 하면 된다.
enum{
    TWO_GIGA = 2llu*1024*1024*1024,       // 2GB !
};

명시적으로 long long 타입이라고 알려주는 것이다.
위의 경우와 마찬가지로 다음과 같이 쓴다면,
enum{
    FOUR_GIGA = 4*1024*1024*1024,       // 4GB ?
};

실제로 값은 0 이다.

이런 경우와 같이 명확하게 type 을 표시해 주면 좋은 경우는
enum 대신에 const 변수를 사용하는 것이 더 좋다.
:
내가 학생 때 프로그래밍을 배우면서 C++ 스타일의 캐스팅을 배운 기억이 나지 않는다.
물론 책에서 몇가지 예제가 있었기 때문에 이런 것들이 있다는 것은 알고 있었지만
실제로 내가 코딩을 하면서는 한 번도 사용해 본 적이 없다.

그러나 실전에서 많은 코드을 보면서
C++ 스타일의 캐스팅이 매우 유용하게 사용된다는 사실을 알게 되었다.
그리고 근래에는 나도 몇 프로젝트를 진행하면서 이 유용한 문법을 사용하면서
진작에 왜 이걸 몰랐을까라고 생각이 들었다.
혹시라도 이 내용을 잘 모르는 사람이 이걸 보고 도움이 되길 바란다.

C++ 스타일 캐스팅에서는 다음 4가지가 있다.
const_cast
dynamic_cast
reinterpret_cast
static_cast



우선은 const_cast 부터 ...

이것은 변수의 상수성을 제거한다. 즉, const 로 선언한 변수에서 const 를 무효화 한다는 의미이다.
그리고 중요한 것은 이 캐스팅이 포인터 혹은 레퍼런스 타입에만 사용하여야 한다는 것이다.

예를 들어, 만약 char * 를 파라미터로 받는 라이브러리가 있는데,
로컬 변수가 const char* 로 선언되어 있다면
const_cast 를 사용해서 별도의 처리 없이 함수를 호출 할 수 있다.

void some_function(char* str)
{
     // what this to do ...
}

void caller()
{
    const char* const_variable;
    some_fuction(const_variable); // <- error !!
    some_fuction(const_cast<char *>const_variable); // <- no problem : )
}

만약 const_cast 가 없다면 함수를 호출하기 위해서 별도 변수를 만들고,
그 변수에 값을 받아서 함수 파라미터로 넘겨야 하는 번거로움이 따른다.

다만, 원래 값이 const 로 선언되었다면, 그 의도에 따라 값을 변경하지 않아야 한다.
문법적인 규칙을 따르기 위해 변수의 상수성을 제거 하였지만,
const 변수의 값을 바꾸려는 의도로 이 캐스트를 사용한다면 부작용이 생길 수 있으니
되도록 사용하지 말아야 한다.
혹자는 const_cast 자체를 사용하지 않는 게 좋다고 하긴 하지만,
그래도 편하라고 있는 건데 잘 쓰면 좋지 않나 생각한다.
학생 시절 그렇게 사용하지 말라던 goto 문도 쓸데가 있으니 ... 그것 보단 낫다고 개인적으로 판단된다 -_-;

논리적으로 바르지 않은 const_cast 사용에 관하여. (아래 블로그 참조)
http://blog.naver.com/arcyze?Redirect=Log&logNo=60041966927

#include <iostream.h>

int main()
{
    const int a = 10;
    int *b = const_cast<int *>(&a);
    *b = 100;

    cout << a << "\t" << &a << endl;
    cout << *b << "\t" << b << endl;
   
    return 0;
}

// 결과
10                  0x0013FF7C
100                0x0013FF7C

실제로 VC++ 6.0, gcc 4.0.4 에서 컴파일 해서 실행하면 위와 같이 희한한 결과가 나온다.
아마도 컴파일러가 위와 같은 문장을 비정상적 코드로 판단하고
내부적으로 자체처리 하고 있는 게 아닐까 생각된다.
reinterpret_cast 에서도 이와 비슷한 기능이 있는 것 같다.
(이에 대해서는 나중에 써 보겠다.)

* const_cast 는 volatile 로 선언된 변수에 대해서도 비슷하게 무효화 해 주는 역할을 한다.
다만, volatile 는 잘 사용하지 않으므로 여기서는 생략한다.
(사실 나도 volatile 는 써 본 적이 없다-_-)

참조 : Effective C++ 3rd Edition / by Scott Meyers
:

코딩 스타일

프로그래밍 2007. 10. 22. 22:27 |
1. 중괄호 쓰기

중괄호 쓰기는 가장 많이 쓰는 3가지가 있는데,
GNU 스타일은 중괄호가 시작할 때부터 들여쓰기가 시작되는 꼴인데
여태껏 이런 식으로 코딩하는 사람은 한 명도 못 봤다.

가장 흔하게 사용되는 것이 K&R, BSD 스타일이다.
보통 교과서에서는 BSD 스타일을 더 많이 쓰는 것 같다.
반면, 프로페셔널 나라에서는 K&R 스타일을 더 많이 보았다.
아무튼 대세는 K&R과 BSD 스타일이 아닌가 생각되므로, 여기서도 이 두 가지만 얘기 한다.

// BSD Style
for(int i = 0; i < SOME_VALUE; i++)
{
    if(SOME_CONDITION)
    {    
         SOME_STATMENT;
    }
    else
    {    
         SOME_STATMENT;
    }
}

// K&R Style
for(int i = 0; i < SOME_VALUE; i++){
    if(SOME_CONDITION){    
         SOME_STATMENT;
    }
    else{    
        SOME_STATMENT;
    }
}

두 스타일의 결정적 차이는 for 나 if 등에서 왼쪽 중괄호('{')가 다음줄에서 시작되는 지 여부이다.
일반적으로는 별 차이가 안 나는 것 같지만, 위와 같이 한 줄 짜리 코드가 들어가면
그 차이가 좀 더 확연해 진다.
K&R 스타일을 옹호하는 쪽은 이런 경우 코드가 깔끔해 지고 쓸데 없이
코드가 많은 공간을 차지하지 않게 되므로 모양새가 좋아지는 점을 강조한다.
하지만 네임 스페이스. 클래스, 구조체, for, if 등 대부분의 경우 이런 스타일을 쓰지만
유독 예외적으로 함수에 대해서는 시작 중괄호를 다음 줄에 쓴다.
이런 면에서는 일관성이 없다고도 할 수 있다.
또한 if 문에서 조건이 길어져 2줄 이상이 되는 경우 모양이 별로 안 예뻐지는 단점도 있다.
// K&R - if 문에서 긴 조건들 ...
if ( THE_FIRST_CONDITION && NEXT_CONDITION &&
     ANOTHER_CONDITION && THE_LAST_CONDITION ) {
     SOME_STATMENT;
}
위와 같이 나열된 조건문들과 중괄호 속의 문장들 사이의 구분이 한 눈에 안 들어오는 경우가 생긴다.
BSD 스타일과 같이 중괄호가 다음 줄에 온다면 K&R 스타일보다는 가독성이 증가할 것이다.

// BSD - if 문에서 긴 조건들 ...
if ( THE_FIRST_CONDITION && NEXT_CONDITION &&
     ANOTHER_CONDITION && THE_LAST_CONDITION )
{
     SOME_STATMENT;
}

어차피 이런 것들은 개인적인 기호와 관련될 가능성이 높으므로 뭐가 딱히 좋다고 할 수는 없다.
하지만 한 조직 내에서는 일관성있게 규칙대로 사용해야 한다.
학교 다닐 때, 컴파일 오류가 나거나 잘 안 풀리는 문제를 물어 보려고 친구를 부르면
항상 그 친구는 제일 먼저 하는 일이 문제를 가르쳐 주는 것이 아니라
자기가 보기 편한 코딩 스타일로 괄호나 들여쓰기를 일일이 수정하는 것이었다는 ...
어찌보면 웃기고도 슬픈 이야기도 있으니 ...


2. 탭과 공백

이런 것 외에도 탭을 스페이스로 대체하는 것이 좋은가 ... 라는 서로 다른 취향도 있다.
학생 때는 당연히 탭을 썼는데,
스페이스로 쓰면 좋은 것이 웹에서도 코드를 잘 볼 수 있기 때문이란다.
(더 많은 장점이 있는 지 모르겠다.)
하지만 여러 사람이 함께 공동 작업을 하고 있음에도 불구하고 cvs 나 svn 등에
'탭 대신 스페이스'라는 코딩 규칙이 있음에도 여전히 탭을 사용한 후 커밋 하는 경우가 많아서
merge 프로그램을 사용할 때 눈에 거슬리는 경우가 상당히 많다.

결론은 ... 한 번 정한 규칙은 반드시 지키자 .
일관성 있는 코딩 스타일이 제일 좋은 것이다


vim 에디트에서 탭을 입력하면 공백으로 처리해 주도록 하려면,
.vimrc 파일에 다음과 같이 설정하면 된다.
set tabstop=4
set expandtab

현재 편집 중인 파일에서 탭을 공백으로 변경하려면 다음과 같이 문자 치환한다.
:%s/\t/    /g
: