안녕하세요? 바람돌이입니다.
이번에는 간략하게 프로그래밍을 할 때의 팁을 이야기 하겠습니다.
보통 프로그램을 작성하다보면, 디버깅을 위해서 printf문을 사용할 경우가 많습니다.
또한, 배포를 한 뒤에도, 남들이 자신의 코드를 보고 디버깅을 해야하는 경우가 종종있죠.
이를 위해서 상용적인 코드에는 다음과 같은 Tip을 종종 사용하곤 합니다.
다음과 같은 코드를 header 파일에서 보신적이 있으실 겁니다.
#ifdef XXX_DEBUG
#define print_debug(A) printf(A)
#else
#define print_debug(A)
#endif
정확한 명칭은 다를 수 있습니다.
간단하게 살펴보면, "#ifdef"는 뒤에 전처리기명이 정의 되어있으면, 이라는 뜻입니다.
즉, XXX_DEBUG가 선언이 되어있으면, 코드 내에 있는 print_debug(A) 부분은 모두 printf(A)로 치환됩니다.
그리고, 선언이 되어있지 않으면, print_debug(A)는 모두 사라지게 되는 것이죠.
보통 우리는 함수내에 printf(A)를 직접 삽입합니다.
그리곤, 필요없는 상황이 된다면, 삭제를 하지요. 그러나, 매우 귀찮을 일이 될 것이며, 실수로 삭제를 하지 않거나 하는 상황이 발생합니다.
그러나, 위의 코딩 기법을 사용하게 되면, XXX_DEBUG를 선언하지 않는 이상 실제 코드(컴파일한 파일)엔 debugging 코드는 존재하지 않게 되지요.
게다가 만약, 다시 디버깅을 해야하는 상황이 된다면, (다 완성이 되었다고 생각했는데, 오류가 있거나, 버전업을 할 경우) XXX_DEBUG만 다시 선언하면, 곧바로 Debugging을 수행할 수 있습니다.
조금만 더 기법을 사용한다면, debugging을 level 별로도 수행 가능할 것이라 생각합니다.
그 부분은 추후 기법으로 남겨놓죠. @^-^@
전처리기에 대한 것을 더 알고 싶으시다면, #ifndef , #ifdef , #endif #pragma 등등을 검색해 보시기 바랍니다.
* Question (김현수)
조교형들이 올려주신 프로그램상에서는 pidx를 클래스내에서
TelPhoneBook() : pidx(0)
위와 같이 변수를 함수처럼 사용한 부분이 있는데 int pidx = 0; 과 비교해서
어떤 차이가 있는지 그리고 위와같은 사용은 c에서는 본적이 없는데
약간의 문법적인 내용까지 알려주시면 감사하겠습니다...
그럼 수고하십시오~~~~^^;
* Answer (박성환)
저희 조교들도 이번기회에 정확히 배우고자
교수님께 직접 문의를 드렸습니다.
알고보니, 저희가 모르는 심오한 세계가 또 있더군요...
(정말, 배움의 길은 끝이 없나 봅니다... 에궁... 어려버~)
생성자에
class A
{
C c;
A(C c1) : c(c1)
{
}
}
을 A로 하고,
class B
{
C c;
B(C c1)
{
c = c1;
}
}
을 B라고 하면, 실제로 엄청난 차이가 있었습니다.
머, 위의 예 보다는 멤버변수가 또 다른 Class일 때 특히 그런데요,
실제로 Compile을 하게 되면, A와 같은 경우에는 Machine Code가 확연히 줄어들게 됩니다.
즉 C라는 Class를 인자로 받아 대입하게 되면, B와 같은 경우에는
복사생성자가 호출이 됩니다.
실제로 C 객체가 생성이 된 뒤 B class의 C class부분에 복사생성자로 초기화가 진행됩니다. (Memory가 한번 더 할당이 된다는 걸 아시겠죠?)
하지만, A의 경우 A class의 생성자가 호출이 되면, A Class의 C Class부분은 곧바고 C Class의 생성자로 초기화를 하게 됩니다.
(Memory가 순수 A class 만큼 할당이 된다는 걸 아시겠죠?)
때문에 두 코드에는 심오한 차이가 존재했습니다.
위와 같은 경우에는 Memory에도 차이가 나겠지만,
중요한 것은 Machine Code의 양이 줄어든다는 것입니다.
복사생성자가 호출되지 않는 (기본 Type 등등) 경우에는 실제 Memory는 별 차이가 없게 되지만,
Machine Code는 차이가 나게 됩니다.
최근에는 Compiler가 급속한 성장을 하여 기본 type에는 큰 차이를 보이지 않습니다. Compiler에게 "-o" 옵션을 주면 최적화를 진행하게 되는데,
이때 기본 type같은 경우 B경우를 A와 같은 처리를 하여 Machine code를 알아서 줄여주는등, 알아서 변환을 하게 됩니다.
머, 사용하는 입장에서는 큰차이가 없지만, 중요하게 알아두시면 좋을 것 같네요.
왜? C class의 크기가 대용량의 크기를 가진다고 가정해 봅시다...
B와 같이 사용하면... 우리가 모르는 사이에... Program이 죽어버릴지둥...
@^-^@
이런 지식하나하나가 학원에서는 절대 배울 수 없는 지식이겠죠?
잘 알아두시길 바랍니다~
그리고, 현수에게 고맙네요~ 덕분에 좋은것을 알게 되어서~ 쌩유~
@^-^@
■ Requirements
■ Functions
▶ size_t strlen (const char *string)
Parameters
string
Null이 아닌 문자열.
Return Value
string문자열 중에 Null 문자를 제외한 문자의 개수를 반환한다.
Error에 대한 반환값은 정의 되어 있지 않다.
Remarks
이 함수는 string문자열 중에 Null 값을 제외한 문자의 개수를 반환한다.
Security Note
이 함수는 잠재적으로 buffer overrun 문제가 발생할 수 있다.
Buffer overrun은 적절하지 않은 수준의 권한이 되는, System 공격 방법중 하나이다.
더 자세한 사항은 Avoiding Buffer Overruns 을 참조하라.
추가 참조
char string[32] = "hello, world";
int size1 = sizeof (string);
int size2 = strlen (string);
printf("sizeof(string) = %d ,strlen(string) = %d\n");
result:
sizeof(string) = 32,strlen(string) = 12
▶ char* strcpy (char *strDestination, const char *strSource)
Parameters
strDestination
문자열이 복사될 목적 문자열 포인터
strSource
Null로 끝나는 원본 문자열
이 함수는 복사된 문자열 포인터를 반환한다.
Error에 대한 반환값은 정의 되어 있지 않다.
Remarks
strcpy 함수는 Null 문자로 끝나는 strSource 문자열을 strDestination로 복사한다.
원본과 목적 문자열이 겹쳐진다면 strcpy는 오작동 할 수도 있다.
Security Note
strcpy함수는 strSource를 복사하기 전에 strDestination의 공간을 확인하지 않아
잠재적으로 Buffer overrun을 발생시킬 수 있다. 대신 strncpy을 사용하라.
▶ char* strncpy (char *strDest, const char *strSource, size_t count )
Parameters
strDest
문자열이 복사될 목적 문자열 포인터
strSource
원본 문자열
count
복사될 문자 개수
Return Value
strDest을 반환한다. Error를 위한 반환값은 정의 되지 않았다.
strncpy 함수는 strSource의 처음부터 count개의 문자를 strDest로 복사하고,
strDest를 반환한다.
만약 count가 strSource의 길이보다 작거나 같다면,
복사된 문자열에 자동으로 Null문자를 붙여주지 않는다.
만약 count가 strSource의 길이보다 크다면,
목적 문자열은 count길이 뒤에 null문자를 붙여준다.
원본과 목적 문자열이 겹쳐진다면 strncpy는 오작동 할 수도 있다.
Security Note
strncpy는 strDest에 충분한 공간이 남아있는지 검사하지 않는다.
그럼으로 buffer overrun 발생의 잠재적 가능성이 있다.
count가 복사될 문자의 개수를 제한한다는 것을 잊지 마라.
그것은 strDest의 크기를 제한하는 것이 아니다.
▶ char* strdup (const char *strSource)
Parameter
strSource
Null로 끝나는 원본 문자열
Libraries
C run-time libraries의 모든 버전.
Return Value
이 함수는 복사된 문자열의 저장공간을 리턴하거나,
새로 할당할 수 없을땐 Null을 반환한다.
Remarks
strdup함수는 strSource의 복사를 위해 저장공간을 할당하려고 malloc함수를
호출한다. 그리고 strSource를 할당한 저장공간에 복사한다.
▶ char* strcat (char *strDestination, const char *strSource)
Parameters
strDestination
Null로 끝나는 목적 문자열
strSource
Null로 끝나는 원본 문자열
Libraries
C run-time libraries의 모든 버전.
Return Value
이 함수는 strDestination 를 반환한다. Error에 대한 반환값은 정의 되어 있지 않다.
Remarks
strcat 함수는 strDestination 문자열에 strSource 문자열을 붙여주고,
Null 문자로 목적 문자열을 종료 시켜준다.
목적 문자열의 Null 종료문자는 원본 문자열의 첫 문자에 의해 덮어 써진다.
문자열을 복사하거나 붙일때에는 overflow를 검사하지 않는다.
원본 문자열과 목적 문자열이 겹칠때에는 strcat 함수는 오작동 할 수 있다.
Security Remarks
첫번째 인자인 strDestination 은 반드시 strDestination 와 strSource 문자열,
그리고 Null 문자인 '\0'를 합친 크기보다 충분히 커야만 한다.
그렇지 않다면, buffer overrun이 발생할 수 있다.
만약 접근영역을 침범하면 서비스거부공격이 일어날 수 있고,
최악의 상황에서는 공격자가 당신의 프로세스에 실행가능한 코드를 삽입할 수 있다.
만약 strDestination 이 stack-based buffer였다면 특히 그렇다.
strncat 나 wcsncat 함수를 사용하여라.
▶ char* strncat (char *strDest, const char *strSource, size_t count)
Parameters
strDest
Null로 끝나는 목적 문자열
strSource
Null로 끝나는 원본 문자열
count
붙여질 문자 개수
Libraries
C run-time libraries의 모든 버전.
Return Value
이 함수는 strDestination 를 반환한다. Error에 대한 반환값은 정의 되어 있지 않다.
Remarks
strncat 함수는 기껏해야 strSource 문자열의 처음부터 count 개수만큼의 문자를
strDest 문자열에 붙인다.
목적 문자열의 Null 종료문자는 원본 문자열의 첫 문자에 의해 덮어 써진다.
만약 count 개수만큼 붙이기 전에 strSource 안에 Null 문자가 있다면,
strncat 함수는 Null 문자를 포함해 strSource 의 모든 문자를 붙인다.
만약 count 가 strSource문자열의 길이보다 크다면, strSource 문자열의 길이가
count 대신 쓰여진다. 결과 문자열은 Null문자로 끝나게 된다.
원본 문자열과 목적 문자열이 겹칠때에는 strncat 함수는 오작동 할 수 있다.
Security Remarks
첫번째 인자인 strDest은 반드시 strDest와 strSource 문자열,
그리고 Null 문자인 '\0'를 합친 크기보다 충분히 커야만 한다.
그렇지 않다면, buffer overrun이 발생할 수 있다.
만약 접근영역을 침범하면 서비스거부공격이 일어날 수 있고,
최악의 상황에서는 공격자가 당신의 프로세스에 실행가능한 코드를 삽입할 수 있다.
만약 strDestination 이 stack-based buffer였다면 특히 그렇다.
마지막 인자인 count는 strDest의 크기가 이나라
strDest중에 복사될 문자 개수임을 명심해라.
특정 site들에서 참고하여 보기 좋게 정리하며 제 생각을 추가한 것입니다
오래전 자료라서 어느 site인지는 기억이 나질 않네요.