[C/C++] 문자열 관련 함수

개발 노트 2008. 10. 8. 14:16 posted by 무병장수권력자


출처 : http://coffeenix.net/doc/KoreanDoc/String_Ref-KLDP.txt

스트링 함수에 대해서 정리하였습니다. 여기에서 빠진 함수가 있거나
잘못된 내용이 있다면 연락 주시기 바랍니다.
				Written by LHC ( s1662543@kiscos.sarang.net)
 
	
			- 스트링 함수 v1.2-

1. 종류

     int strcasecmp(const char *s1, const char *s2);
     char *strcat(char *dest, const char *src);
     char *strchr(const char *s, int c);
     int strcmp(const char *s1, const char *s2);
	int strcoll(const char *s1, const char *s2);
	char *strcpy(char *dest, const char *src);
	size_t strcspn(const char *s, const char *reject);
	char *strdup(const char *s);
	char *strfry(char *string);
	size_t strlen(const char *s);
	char *strncat(char *dest, const char *src, size_t n);
	int strncmp(const char *s1, const char *s2, size_t n);
	char *strncpy(char *dest, const char *src, size_t n);
	int strncasecmp(const char *s1, const char *s2, size_t n);
	char *strpbrk(const char *s, const char *accept);
	char *strrchr(const char *s, int c);
	char *strsep(char **stringp, const char *delim);
	size_t strspn(const char *s, const char *accept);
	char *strstr(const char *haystack, const char *needle);
	char *strtok(char *s, const char *delim);
	size_t strxfrm(char *dest, const char *src, size_t n);
	char *index(const char *s, int c);
	char *rindex(const char *s, int c);
	
2. 설명
C에서의 string은 단순히 0바이트로 끝나는 문자들의 배열이다.
string에 관한 모든 타입과 선언은 string.h에 되어있다.
string 처리를 위해 필요한  str1의 첫번째 주소를 알기위해선 &str1[0]대신
str1이라고만 하면 된다. C에서 배열의 변수명은 배열의 첫번째 주소를 가르킨다는
것은 이글을 보는 사람이라면 모두 알고 있을 것이다. 배열역시 내부적으로는
포인터로써 처리된다. 즉
str1[i]가 있다면 이의 실제 처리는 다음과 같이 된다. *(str1 + i)
함수의 인자로써 배열을 주는 경우가 종종있는데 이는 좋은 코딩이 아니다. 왜냐하면
인자로 넘겨주는 배열은 위와같이 다시 포인터로 변환되는 과정을 거쳐야 하기때문에
코드의 효율성을 떨어뜨린다.
int func(char []) -> int func(char *) 
위와 같이 한다하여 배열과 포인터가 결코 같다고 생각해선 안된다.
char i[10]; 하면 char 메모리를 10만큼 할당하는 것이지만 char *i; 하면 단지
메모리에 있는 문자를 가르키는 번지만이 할당된다. 다시 위의 함수의 인자를 넘겨주
는것에 대해 살펴보면 이와같은 차이에도 불구하고 왜 저렇게 해야하는지 의문이
들겠지만 함수에서는 인자로 값을넘겨줄때 배열의 모든값을 넘겨줄수가 없다.
따라서 그 번지를 넘겨줘야 되는데 어차피 같은 거라면 배열을 써서 다시 포인터로
변환되는 과정을 거칠필요가 없다는 것이다.
여기에서 더 깊이 들어가는것은 이 문서의 범위를 벗어나므로 더욱 자세한 사항은
포인터와 배열에 대한 책을 참고하기 바란다.(http://pw2.netcom.com/~tjensen/ptr/pointers.htm)
string 를 처리할때 가장많이 사용하는 함수로 strlen이 있는데 이는 아주 유용하다.
strlen은 문자열의 길이를 리턴해준다(null 문자는 길이에서 제외된다). 
다시말하면 null 문자의 offset을 린턴하는 것이다. 보통 문제가 되는 것은 sizeof
와 이거와의 혼동이다.

char a[1024] = "하하하 호호호 히히히 메롱"; 있다고 하면
sizeof(a) 는 1024이고 strlen(a)는 25이다.

앞으로 자주보게 될 size_t는 unsigned int형이다.
스트링 처리를 함에 있어 그 정확한 길이를 알고 싶은 경우가 많은데
거의 대부분의 해답은 strlen()가 된다. 그것으로 해결이 안되는 경우는
함수의 리턴값을 통해서 알 수 있는 경우도 있다. 예를들면 read()나 write()의 
경우에는 리턴값이 읽거나 쓴 길이이다. 

2.1 복사 함수

	 void *memcpy(void *dest, const void *src, size_t n);

이는 src가 가르키는 메모리 영역으로 부터 n 바이트를 dest 영역으로 복사한다.
주의할 점은 두개의 영역이 겹쳐있으면 안된다. 만약 그런경우가 필요하다면 
위해선 memmove를 사용하라.
리턴값은 dest를 가르키는 포인터 

	void *memmove(void *dest, const void *src, size_t n);

이는 src가 가르키는 메모리 영역으로 부터 n 바이트를 dest영역으로 복사하는데
두 메모리 영역은 겹쳐져도(overlap) 된다.
리턴값은 dest를 가르키는 포인터

	void *memccpy(void *dest, const void *src, int c, size_t n);

n 바이트를 복사하는데 문자 c를 만나면 중지한다.
리턴값은 dest에서 c문자 다음위치를 가르키는 포인터. 실패는 NULL

	void *memset(void *s, int c, size_t n);
s가 가르키는 곳을 c로서 n바이트 만큼 채운다.
리턴값은 s를 가르키는 포인터이다.

	char *strcpy(char *dest, const char *src);
src가 가르크는 메모리 영역의 내용을(널문자 포함) dest가 가르키는 메모리 영역으로
복사한다. 주의할 점은 dest는 복사되는 문자에 대해 모두 저장할 수 있어야한다.
리턴값은 dest를 가르키는 포인터이다.

	char *strncpy(char *dest, const char *src, size_t n);
strcpy와 같고 지정된 n 바이트만을 복사한다. 널문자는 복사되지 않는다.
만약 src가 가르키는 곳의 문자열 길이가 n보다 작다면 \0로써 패딩되어 복사된다.
strncpy를 사용하는 것이 strcpy에서
발생할 수있는 버그를 예방하는 것이긴 하지만 보통 이것은 수행속도가 느리다.
왜냐하면 상대적으로 작은 src를 상대적으로 큰 버퍼에 복사하는 과정에서 버퍼의
나머지영역을 널문자로 채우게 되는데 여기에서 상당한 시간을 보내기 때문이다.

	char *strcat(char *dest, const char *src);
src가 가르키는 메모리 내용을 dest가 가르키는 곳의 끝인 '\0' 위치부터 붙여 
넣는다(널문자까지). overlap은 허용되지 않으며 dest는 부여지게 될 string까지 
커버할 수있는 메모리를 가지고있어야한다.
리턴값은 dest를 가르키는 포인터이다.
strcat는 다음과 같다.

     char *
     strcat (char *to, const char *from)
    {
	  strcpy (to + strlen (to), from);
	  return to;
    }
	
	char *strncat(char *dest, const char *src, size_t n);
strcat와 같으며 src의 n byte만을 복사한다. 
그런데 끝에 널문자까지 복사되기때문에 실제 복사되는 크기는 n + 1이 됨을 
주의해야 한다.
src가 n보다 작다면 널문자로 패딩된다. 
strncat의 리턴값은 a[0]에 대한 번지값이고 overlap은 허용되지 않는다.

       char *strdup(const char *s);
string duplicate. s의 문자열을 복사하고 그 문자열을 가르키는 포인터를 
리턴한다. 새로운 문자열을 malloc에 의해서 메모리를 할당 받고
free를 통해서 해제할 수있다. 만약 할당할 메모리가 없다면 널 포인터를
리턴한다. 
	char *name;
	name = strdup("LHC");
	free (name);

       char * strndup (const char *s, size_t size) 
s에서 size 만큼의 문자열을 복사하고 복사된 문자열을 가르키는 포인터를 리턴한다.
size가 문자열 s보다 작다면 size만큼 복사하고 끝에 널포인터를 넣어준다.

2.2 비교 함수

	int memcmp(const void *s1, const void *s2, size_t n);
s1과 s2의 처음 n 바이트를 비교한다. s1이 s2보다 크면 0보다 큰값을 같으면
0을 작으면 음수값을 리턴한다.
임의의 배열에서 두개가 같은지 않같은지를 테스트하는데 이를 사용하는것은
아주 유용하다. 하지만 바이트 수가 다른 배열상의 byte-wise ordering 비교를
하는것은 의미없는 짓이다. 예를들면 실수형 포인터로 구성된 메모리상의
1 byte-wise ordering은 실수형 포인터 변수들사이의 관계에 대해 어떤것도
말해주지 않는다. 또다른 주의해야 할 사항으로 holes를 포함하는 객체들을
비교할 때이다. holes는 패딩하여 생기는 메모리 영역을 말하는데 패딩이란
예를들어 1byte씩 주고받는 상황이 있다고 할때 어떤 함수에서 5bit만을 리턴한다면
그를 규정에 맞게 주고 받기 위해선 8bit로 만들어야 한다. 이때 모자라는 3비트를
널로 채우든지 뭐로 채우든간에 이렇게 채우는 행위를 일컷는 용어이다.
이렇게 패딩된 holes의 내용은 무엇이 들어 갈지 알 수없기때문에 byte-wise 비교
수행시 문제를 일으킬수 있다. 따라서 결과에 대해 확실히 하려면 component-wise
비교를 해야한다.
struct foo
{
     unsigned char tag;
     union
     {
	       double f;
            long i;
	       char *p;
	 } value;
};
즉 위와 같은 foo의 멤버 데이터를 비교하기 위해선 memcpy를 사용하는것보다 
다른 특별한 함수를 사용하는것이 더 낫다.

	int strcmp(const char *s1, const char *s2);
s1과 s2를 비교하고 리턴값은 memcmp와 같다.
strcmp는 다음과 같이 이루어져있다. 
int
strcmp(s1, s2)
register const char *s1, *s2;
{
        while (*s1 == *s2++)
               if (*s1++ == 0)
                        return (0);
 	       return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
								}
	int strncmp(const char *s1, const char *s2, size_t n);
s1의 처음 n 바이트만을 비교하고 나머지는 strcmp와 같다.
다음은 아스키 코드를 기반으로 하는 시스템에서의 결과이다.
strcmp ("hello", "hello")
=> 0    /* 같으므로 리턴값은 0 */
strcmp ("hello", "Hello")
=> 32   /* h와 H의 아스키 코드 차는 32 */
strcmp ("hello", "world")
=> -15  /* h와 w의 아스키 코드 차는 15 */
strcmp ("hello", "hello, world")
=> -44  /* 널문자와 ',' 와의 아스키코드 차는 44 */
strncmp ("hello", "hello, world", 5)
=> 0    /* 처음 5바이트는 같다 따라서 리턴값은 0*/
strncmp ("hello, world", "hello, stupid world!!!", 5)
=> 0    /* 처음 5바이트는 같다 따라서 리턴값은 0 */

       int strcasecmp(const char *s1, const char *s2);
strcmp와 같다.

       int strncasecmp(const char *s1, const char *s2, size_t n);
strncmp와 같다.

2.3비교 정열을 위한 함수 ( collation function )
문자를 비교나 정열하기 위해선 사용하는 문자의 코드값을 알아야 한다. 예를 들면
알파벳의 경우 a는 0x61번의 아스키 코드를 갖는다. 이를 통하여 우리는
문자의 순서를 알수있다. 그런데 문제는 비영어권에서이다. 그들의 문자에 대한
코드값이 필요한다 일반적인 프로그램에서는 그에 대한 정보를 알수 없기때문에
정열을 할수없게된다. 그래서 필요한 것이 로케일이란 것인데 이것은 사용자가
사용하는 나라의 문자에 관한 정보를 가지고있다. 그래서 프로그램은 그 로케일을
참고하여 정열이나 기타 그 나라 문자에 관한 처리를 할수있는 것이다. 
이렇게 현재의 로케일에 맞게 비교 해주는 함수로 strcoll과 strxfrm이 있다.
이들 함수들은 로케일 category중 LC_COLLATE값을 사용한다.
표준 C에서 strcoll의 비교 방식은 strcmp와 동일하다. 

	int strcoll(const char *s1, const char *s2);
현재의 locale를 사용하여 두 문자열을 비교한다. 리턴값은 memcpy와 같다.
(예제)
qsort를 이용한 strcoll 사용예제.
int
compare_elements (char **p1, char **p2)
{
  return strcoll (*p1, *p2);
  }

/* This is the entry point---the function to sort
   strings using the locale's collating sequence. */

void
sort_strings (char **array, int nstrings)
{
  /* Sort temp_array by comparing the strings. */
    qsort (array, sizeof (char *),
             nstrings, compare_elements);
}

size_t strxfrm(char *dest, const char *src, size_t n);
strxfrm은 src 스트링을 어떤 폼으로 변환시키는데 strxfrm으로 변환된 두개의
스트링을 strcmp 처리를 하면 변환되기전의 두개의 스트링을 strcoll 처리한것과
같은 결과를 갖게 된다. 변환된 n개 만큼의 문자들을 dest에 쓴다. 변환은
LC_COLLATE에 설정되 있는 현재 로케일을 따른다. overlap은 허용되지 않는다.
리턴값은 전체 변환된 문자의 바이트 수이고(\0는 제외) n에 영향 받지 않는다.
만약 변환된 문자열의 길이가 n과 같거나 크다면 dest에 들어간 내용은 정확하지
않음을 주의해야 한다. 정확한 dest를 얻으려면 다시한번 더큰 사이즈로 strxfrm을
호출해야한다.  만약 n이 0이면 어떤 문자도 dest에 복사하지 않고 단지 전체
변환된 문자열의 길이만을 리턴하고 dest의 사이즈에도 전혀 영향 받지안기 때문에
변환된 문자의 길이를 알때 유용하게 사용할 수있을것이다. 다음과 같이 하면 된다.
1 + strxfrm(NULL, "변환할 문자열", 0) 
LC_COLLATE는 정규식에서 검색이나 스트링 정렬을 위해 사용되는 로케일 값이다.
로케일 category에 대해서 더 자세한 사항을 보려면 man setlocale로 확인하기
바란다. setlocale은 프로그램에서 사용할 로케일을 설정하는 함수이다.
현재 자신의 운영체제에서 사용중인 로케일을 확인하려면 locale란 명령을
주면된다. 
[root@LHC rc.d]# locale
LANG=ko_KR.eucKR
LC_CTYPE="ko_KR.eucKR"
LC_NUMERIC="ko_KR.eucKR"
LC_TIME="ko_KR.eucKR"
LC_COLLATE="ko_KR.eucKR"
LC_MONETARY="ko_KR.eucKR"
LC_MESSAGES="ko_KR.eucKR"
LC_ALL=ko_KR.eucKR
[예제]
다음은 strcoll의 예제와 같은 역할을 하는 소스인데 스트링 변환을 1번만 하기
때문에 효율적이고 훨씬 빠르다. 여러번 비교를 할때 유용할 것이다.
struct sorter { char *input; char *transformed; };

/* This is the comparison function used with qsort
   to sort an array of struct sorter. */

   int
   compare_elements (struct sorter *p1, struct sorter *p2)
   {
     return strcmp (p1->transformed, p2->transformed);
   }

/* This is the entry point---the function to sort
   strings using the locale's collating sequence. */

   void
   sort_strings_fast (char **array, int nstrings)
   {
    	struct sorter temp_array[nstrings];
    	int i;

/* Set up temp_array.  Each element contains
   one input string and its transformed string. */
  	for (i = 0; i < nstrings; i++)
  	{
       		size_t length = strlen (array[i]) * 2;
       		char *transformed;
       		size_t transformed_lenght;

       		temp_array[i].input = array[i];

/* First try a buffer perhaps big enough.  */
       		transformed = (char *) xmalloc (length);

/* Transform array[i].  */
  		transformed_length = strxfrm (transformed, array[i], length);

/* If the buffer was not large enough, resize it
   and try again.  */
  		if (transformed_length >= length)
  		{
/* Allocate the needed space. +1 for terminating
   NUL character.  */
       			transformed = (char *) xrealloc (transformed, transformed_length + 1);

/* The return value is not interesting because we know
   how long the transformed string is.  */
       			(void) strxfrm (transformed, array[i], transformed_length + 1);
		}

     		temp_array[i].transformed = transformed;
     	}

/* Sort temp_array by comparing transformed strings. */
        qsort (temp_array, sizeof (struct sorter), nstrings, compare_elements);
/* Put the elements back in the permanent array
   in their sorted order. */
  	for (i = 0; i < nstrings; i++)
	      array[i] = temp_array[i].input;
/* Free the strings we allocated. */
  	for (i = 0; i < nstrings; i++)
        free (temp_array[i].transformed);
}

2.4 검색함수
       void *memchr(const void *s, int c, size_t n);
s가 가르키고 있는 문자열에서 n번째 이내에서 처음 나오는 c를 찾는다.
char를 찾는데 int c로 선언되 있음에 의아할 지도 모르겠지만 c는 찾는 과정에서
unsigned char로 변환된다. 리턴값은 찾았다면 문자가 위치한 곳의 포인터를
못찾으면 널포인터를 리턴한다.

       char *strchr(const char *s, int c);
s가 가르키고 있는 문자열에서 c를 찾는다. 종료문자인 널문자 역시 스트링의
부분으로써 간주된다. 따라서 C를 널문자로 줌으로써 문자열의 끝 번지를
알아내는데 이용할 수있다. 리턴값은 찾았다면 문자가 위치한 곳의 포인터를
못찾으면 널포인터를 리턴한다.
[예제] 
strchr ("hello, world", 'l')
     => "llo, world"
strchr ("hello, world", '?')
     => NULL
      
      char *strrchr(const char *s, int c);
s가 가르키는 문자열에서 마지막 발생한 c를 찾는다.
리턴값은 찾으면 마지막 발생한 c의 주소를 못찾으면 널을 리턴한다. 
널문자 역시 스트링의 일부로 간주된다.
[예제]
strrchr ("hello, world", 'l')
     => "ld"

       size_t strspn(const char *s, const char *accept);
이 함수는 s가 가르키는 문자열에서 accept로 구성된 문자가 안나올때 까지 
문자열의 길이를 계산한다. 리턴값은 그 길이이다.
strspn은 string span이라 읽는다.
예를 들면
strspn ("hello, world", "abcdefghijklmnopqrstuvwxyz")
      => 5
accept로 구성된 문자가 안나올때까지의 길이는 5이다. ','는 accept에
정의되어 있지 않기 때문이다. 만약 accpet로 ", abcdefghijklmnopqrstuvwxyz"
해주면 13이라고 문자열 전체 길이를 출력할 것이다. accept 문자의 순서는 어떻게
되든 상관이 없다.

       size_t strcspn(const char *s, const char *reject);
string complement span 이라 읽으며 strspn의 반대이다. 좀 헤깔릴지도 모르겠는데
reject로 구성된 문자가 나올때까지의 길이를 리턴해 준다.
strcspn ("hello, world", ",");
     => 5

       char *strpbrk(const char *s, const char *accept);
string point break라 읽으며 하는 읽은 strcspn과 비슷하다. 다른점은 accept에 
정의된 문자가 처음 나오는 곳의 포인터를 넘겨준다는 점이다. 
[예제]
strpbrk ("hello, world", " \t\n,.;!?")
     => ", world"

       char *strstr(const char *haystack, const char *needle);
strchr과 비슷한데 다른점은 한문자가 아닌 문자열을 찾는다는 것이다.
널문자는 비교하지 않는다.
리턴값은 찾으면 처음 발생한 곳의 포인터를 못찾으면 널을 리턴한다.
[예제]
strstr ("hello, world", "l")
     => "llo, world"
strstr ("hello, world", "wo")
     => "world"

       void *memmem(const void *haystack, size_t haystacklen,
                     const void *needle, size_t needlelen);
haystacklen 길이만큼의 haystack에서 needle의 needlelen만큼의 스트링을 찾아서
처음 발생한 번지를 리턴한다. 
이함수는 GNU 확장이다. libc 5.0.9에서 이 함수를 사용하는 것은 위험하다. 거기에는
needle와 haystack 인자가 서로 바뀌어 있고 needle이 처음 발생한 곳의 끝 번지를
리턴한다. libc 5.0.9는 여전히 많이 사용되고 있기때문에 이 함수의 사용은 위험
하므로 사용을 자제해야 한다. 
또한 libc나 glibc둘다 다음과 같은 버그가 있다.
만약 needle이 널이라면 리턴값은 haystack가 아니라 haystack - 1이 된다.
glibc 2.0.5는 더욱 나쁜 상황을 연출하는데 이는 haystack의 마지막 바이트의 
포인터를 리턴한다. 따라서 결코 needle를 널로 주고 사용해서는 안되며
정 필요한 경우가 아니면 사용을 하지 말아야 할 함수이다.

       char *index(const char *s, int c);
strchr의 다른 이름으로 하는일은 똑같다

       char *rindex(const char *s, int c);
strrchr의 다른 이름으로 하는일은 똑같다.

		     
2.5 토큰 함수
이들은 간단한 구분 분석이나 파싱을 할때 사용된다. 
       char *strtok(char *s, const char *delim);
이 함수는 이해하는데 약간의 노력을 요하는 함수로 주의를 기울여야 할것이다. 

char a[] = "하하하 이것은 토큰을 위한 예입지요";
char *p1, *p2, *p3, *p4, *p5;
p1 = strtok(a, " "); 
p2 = strtok(NULL, " ");
p3 = strtok(NULL, " ");
p4 = strtok(NULL, " ");
p5 = strtok(NULL, " ");
delim이 처음 발생할 때까지의 문자를 찾고 그것을 널문자로 대체하고 문자열의
포인터를 리턴한다. 따라서 p1은 '하'를 가르키는 포인터가 된다. 그리고 다음
단어의 시작을 가르키는 포인터를 내부적으로 기억한다. 그렇기 때문에 첫번째
인자를 더이상 줄필요가 없으므로 NULL을 쓰는것이다. 
다음 예제를 보면 확실히 이해가 될것이다. 
char string[] = "words separated by spaces -- and, punctuation!";
const char delimiters[] = " .,;:!-";
char *token;

...

token = strtok (string, delimiters);  /* token => "words" */
token = strtok (NULL, delimiters);    /* token => "separated" */
token = strtok (NULL, delimiters);    /* token => "by" */
token = strtok (NULL, delimiters);    /* token => "spaces" */
token = strtok (NULL, delimiters);    /* token => "and" */
token = strtok (NULL, delimiters);    /* token => "punctuation" */
token = strtok (NULL, delimiters);    /* token => NULL */

주의해야 할점은 strtok를 사용하면 string 함수의 내용이 바뀌게된다.
따라서 const형 string에는 사용할 수 없다. 또한 내용이 유지되야 하는 스트링에
대해 사용하려면 먼저 복사를 해논다음에 사용해야 한다.

string의 내용을 한문자씩 출력 해보면 다음과 같다. 
wordsseparatedbyspaces-- and punctuation 
delim에 주어진 문자들은 널포인터로 바뀌기 때문에 띄어쓰기가 안된것처럼 보인다.
"-- " 부분은 앞의 공간이 널로 변환되고 delim에 주어지지 않은 문자의 시작인
a를 가르키기 때문에 그와 같이 출력되고 and다음의 공간은 and 바로 다음의 ','가
널문자로 치환되고 delim에 주어지지 안은 첫단어의 시작인 punc~로 가기때문에
그와 같이 출력되는 것이다. 

char * strtok_r (char *newstring, const char *delimiters, char **save_ptr)
strtok와 비슷하다. 차이점은 strtok와 달리 다음 토큰의 시작을 내부적으로
기억하지 않는다는 것이다. 따라서 호출할때 save_ptr을 수동으로 제공해 줘야 한다.
2번째 호출부터 newstring을 널로 하고 save_ptr을 그대로 주면 strtok와 같이
작동하게 된다.
이 함수는 POSIX.1b에서 제안되었다.

       char *strsep(char **stringp, const char *delim);
#include <string.h>
#include <stddef.h>

...

char string[] = "words separated by spaces -- and, punctuation!";
const char delimiters[] = " .,;:!-";
char *running;
char *token;

...

running = string;
token = strsep (&running, delimiters);    /* token => "words" */
token = strsep (&running, delimiters);    /* token => "separated" */
token = strsep (&running, delimiters);    /* token => "by" */
token = strsep (&running, delimiters);    /* token => "spaces" */
token = strsep (&running, delimiters);    /* token => "and" */
token = strsep (&running, delimiters);    /* token => "punctuation" */
token = strsep (&running, delimiters);    /* token => NULL */

첫번째 인자를 부가적으로 줌으로써 중복적인 접근을 피할 수있다.
움직이는 포인터의 초기화는 사용자가 할수있다. strsep가 성공하면 
delim에 의해 구분되는 토큰을 따라 포인터는 움직이게 된다.
리턴값은 다음 토큰의 처음을 가르키는 포인터이다. delim이 발견되지 않으면
null을 리턴한다.
4.3BSD에서 나온 함수이다.
2.6 기타 함수
       char *strerror(int errnum);
errnum에 넘겨진 에러코드를 설명하는 문자열을 리턴한다.
다음은 1 부터 19까지 errnum에 대한 설명이다. 
errornum 0 : 성공
errornum 1 : 명령이 허용되지 않음
errornum 2 : 그런 파일이나 디렉토리가 없음
errornum 3 : 그런 프로세스가 없음
errornum 4 : 중단된 시스템 호출
errornum 5 : 입력/출력 오류
errornum 6 : 장치가 설정되지 않았음
errornum 7 : 인수 명단이 너무 깁니다
errornum 8 : Exec 형식 오류
errornum 9 : 잘못된 파일 기술자
errornum 10 : 자식 프로세스가 없음
errornum 11 : 자원이 일시적으로 사용 불가능함
errornum 12 : 메모리를 할당할 수 없습니다
errornum 13 : 허가 거부됨
errornum 14 : 잘못된 주소
errornum 15 : 블럭 장치가 필요함
errornum 16 : 장치나 자원이 동작 중
errornum 17 : 파일이 존재합니다
errornum 18 : 부적절한 장치간 연결
errornum 19 : 그런 장치가 없음

       char *strfry(char *string);
문자열을 rand()를 사용하여 랜덤화 하고 랜덤화된 문자열에 대한 포인터를 
리턴한다.

[참고자료]
  Linux man page
  Programming Language - C 
  love_C ( Cambridge University Engineering Department)
  The GNU C Library Reference Manual


[C++] 동적으로 메모리 할당하기

개발 노트 2008. 10. 7. 09:51 posted by 무병장수권력자


작성자 : 김문규
최조 작성일 : 2008.10.07

Efffective C++ 이란 책을 보면 C++에서는 malloc/free 대신에 new/delete를 사용하라는 말이 있습니다.
근데 막상 이렇게 선언하려 하면 어색하거나 뭔가 잘 되지 않는 경우가 있습니다. 그래서 정리해 보았습니다.

이유는 아래와 같습니다.
1. malloc과 free는 생성자와 소멸자의 존재자체를 모른다.  <-- 이게 가장 중요한 이유..
2. 1번의 문제로, 메모리릭 발생 위험이 따른다.(사용자가 간과했다간)
3. 1번의 문제로, 초기화 작업을 부수적으로 해야 한다.
4. 1번의 문제로 , 가독성을 떨어뜨릴수 있다(new와 malloc의 혼용 사용으로)

해결 방법은 아래와 같습니다.
1. 혼용 사용을 왠만해선 하지 마라
2. new와 delete를 사용 해라

근데, 2차원 배열만 되도 귀찮아 지는 건 사실이네요....

1차원 배열 선언
1) C 스타일
char *p = malloc(sizeof(char)*100);
free(p);

2) C++ 스타일
char* p = new char[100];
delete [] p;

2차원 배열 선언
1) C 스타일
char *p = malloc(sizeof(char)*100*50);
free(p);

2)C++ 스타일
char **p = new char*[50];
for(int i=0; i < 50; i++) p[i] = new char[100];

for(int i=0; i < 50; i++) delete [] p[i];
delete [] p;


참조
http://hoonspace.tistory.com/23
http://ikpil.com/284


이클립스 에러 - JVM terminated. Exit code = -1 ....

개발 노트 2008. 10. 2. 14:30 posted by 무병장수권력자



eclipse.ini 를 열어서 -Xmx512M --> -Xmx256M 으로 고쳐 주세요.





void GetInterfaceInfomationMyIpAddress()
{
     WORD versionRequested;
     int wsError;
     WSADATA winsockData;
     SOCKET s;
     DWORD bytesReturned;
     char* pAddrString;
     SOCKADDR_IN* pAddrInet;
     u_long SetFlags;
     INTERFACE_INFO localAddr[10];  // Assume there will be no more than 10 IP interfaces
     int numLocalAddr;

     versionRequested = MAKEWORD(2, 2);

     wsError = WSAStartup(versionRequested, &winsockData);
     if (wsError)
     {
          fprintf (stderr, "Startup failed\n");
                return;
     }

     if((s = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0)) == INVALID_SOCKET)
     {
          fprintf (stderr, "Socket creation failed\n");
          WSACleanup();
               return;
        }

     // Enumerate all IP interfaces
     fprintf(stderr, "Scanning Interfaces . . .\n\n");
     wsError = WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &localAddr,
                      sizeof(localAddr), &bytesReturned, NULL, NULL);
     if (wsError == SOCKET_ERROR)
     {
          fprintf(stderr, "WSAIoctl fails with error %d\n", GetLastError());
          closesocket(s);
          WSACleanup();
          return;
     }

     closesocket(s);

     // Display interface information
     numLocalAddr = (bytesReturned/sizeof(INTERFACE_INFO));
     for (int i=0; i<numLocalAddr; i++)
     {
          pAddrInet = (SOCKADDR_IN*)&localAddr[i].iiAddress;
          pAddrString = inet_ntoa(pAddrInet->sin_addr);
          if (pAddrString)
               printf("IP: %s  ", pAddrString);

          pAddrInet = (SOCKADDR_IN*)&localAddr[i].iiNetmask;
          pAddrString = inet_ntoa(pAddrInet->sin_addr);
          if (pAddrString)
               printf(" SubnetMask: %s ", pAddrString);

          pAddrInet = (SOCKADDR_IN*)&localAddr[i].iiBroadcastAddress;
          pAddrString = inet_ntoa(pAddrInet->sin_addr);
          if (pAddrString)
               printf(" Bcast Addr: %s\n", pAddrString);

          SetFlags = localAddr[i].iiFlags;
          if (SetFlags & IFF_UP)
               printf("This interface is up");
          if (SetFlags & IFF_BROADCAST)
               printf(", broadcasts are supported");
          if (SetFlags & IFF_MULTICAST)
               printf(", and so are multicasts");
          if (SetFlags & IFF_LOOPBACK)
               printf(". BTW, this is the loopback interface");
          if (SetFlags & IFF_POINTTOPOINT)
               printf(". BTW, this is a point-to-point link");
          printf("\n\n");
     }

     WSACleanup();
}



[C/C++] 현재 시간 얻기

개발 노트 2008. 9. 29. 15:13 posted by 무병장수권력자



시스템에서 현재시간을 얻어오는 방법이다.

time_t timer;
struct tm *t;

timer = time(NULL); // 현재 시각을 초 단위로 얻기

t = localtime(&timer); // 초 단위의 시간을 분리하여 구조체에 넣기


tm struct (time.h)

struct tm {
  int tm_sec;   /* Seconds */
  int tm_min;   /* Minutes */
  int tm_hour;  /* Hour (0--23) */
  int tm_mday;  /* Day of month (1--31) */
  int tm_mon;   /* Month (0--11) */
  int tm_year;  /* Year (calendar year minus 1900) */
  int tm_wday;  /* Weekday (0--6; Sunday = 0) */
  int tm_yday;  /* Day of year (0--365) */
  int tm_isdst; /* 0 if daylight savings time is not in effect) */
};

출처 : http://dorio.tistory.com/entry/CC%ED%98%84%EC%9E%AC%EC%8B%9C%EA%B0%84-%EC%96%BB%EA%B8%B0


OpenSSL 시작하기

개발 노트 2008. 9. 9. 11:27 posted by 무병장수권력자


작성자 : 김문규
최초 작성일 : 2008. 9 9

윈도우 버젼을 구해서 간단하게 테스트 해 보았습니다. 다음의 링크에서 윈도우 버젼을 구할 수 있습니다.
http://www.slproweb.com/products/Win32OpenSSL.html

다음의 순서를 따라하시면 OpenSSL을 가지고 crypto 라이브러리로서 어떻게 사용하실 지에 대한 감을 잡을 수 있을 것으로 생각됩니다.
이제 실제 라이브러리 함수로 내 프로그램에서 어떻게 동작 시킬지 만 확인하면 되겠네요. ^^

1. private key 생성
openssl genrsa -out alice.private

2. public key 생성
openssl rsa -in alice.private -pubout

3. plain text 암호화

alice.plain

hi!MK!

openssl rsautil -encrypt -in alice.plain -out alice.encrypted -pubin -inkey alice.public

4. encrypted text 복호화
openssl rsautl -decrypt -in alice.encrypted -out alice.decrypted -inkey alice.private

5. 복호화된 결과 확인

alice.decrypted

hi!MK!


** 참고 자료
http://www.dmst.aueb.gr/dds/secimp/crypto/pktest.htm



리눅스에서 메모리 잠그기

개발 노트 2008. 9. 9. 09:13 posted by 무병장수권력자


Locking Memory in LINUX

작성자 : 김문규
최초 작성일 : 2008. 9. 9

리눅스의 paging에 대해서 아십니까? 이는 메모리를 효율적으로 사용하기 위한 기법이지만 프로그래머 입장에서는 골치아픈 존재가 될 수 있습니다. 만약에 메모리 DB를 만들기 위해 데이터를 메모리로 올렸다고 하지요. 그런데, 많이 인덱싱 되지 않는 레코드가 있을 경우에는 리눅스는 과감하게 해당 메모리 영역을 swap-out 해버릴 수 있습니다. 난감한 상황이 아닐 수 없습니다.

그래서 이 경우에는 강제로 swap out 되지 않도록 메모리를 잠그는 명령을 내려주어야만 합니다.

1. 일부 공간 잠그기

#include <sys/mman.h>
int mlock(const void* addr, size_t len);

2. 해당 프로세스의 주소 공간 전체 잠그기
#include <sys/mman.h>
int mlockall(int flags);     // flag : MCL_CURRENT, MCL_FUTURE

위의 명령은 앞서 말한 경우에 사용을 하지만, 섣부른 사용은 시스템 전체를 망칠 수 있음을 명심하기 바랍니다. 개인적인 생각으로는 해당 프로세스에 dedicated 된 machine이 아닌 경우에는 전체를 잠그는 행동은 아주 위험할 수 있으니 더욱 유의하길 바랍니다.


JNI 시작하기 (Java와 C/C++의 연동)

개발 노트 2008. 9. 5. 16:49 posted by 무병장수권력자


작성자 : 김문규
최초 작성일 : 2008. 9. 5

JNI는 Java 클래스에서 C 라이브러리를 호출하는 기술입니다.

구현 방법은 다음과 같습니다.

1. Java 클래스 구현

HelloJniClass.java

public class HelloJniClass {

 native void Hello(); // native 함수
 static {  System.loadLibrary("Hello_DLL");   }  // 로드할 라이브러리 Hello_DLL.dll 을 로드합니다.

 public static void main(String args[]) {   
  HelloJniClass myJNI = new HelloJniClass();
  myJNI.Hello(); // native 함수 호출
 }
}


> javac HelloJniClass.java
HelloJniClass.class 파일 생성됩니다.


2. C용 헤더 파일 생성
> javah HelloJniClass
HelloJniClass.h 가 생성됩니다. 이 파일은 구현해야 할 함수가 선언되어 있습니다.
클래스패스 선언에 유의합니다. (간단하게는 같은 폴더에 놓고 -classpath . 하면 됩니다.)


3. dll 파일 생성
HelloJniClass.h를 이용해서 원하는 기능을 수행하는 dll 파일을 작성합니다.
HelloWorldLib.cpp

#include "stdafx.h"
#include <stdio.h>
#include "jni.h"
#include "HelloJniClass.h"
 
JNIEXPORT void JNICALL Java_HelloJniClass_Hello(JNIEnv *env, jobject jobj) {
     printf("Hello Jni~!");  // 원하는 기능
}
컴파일 하시면 Hello_DLL.dll 이 생성됩니다.
비줠 스튜디오에서 output 파일 이름을 맞추는 설정을 하시고 프로젝트 include 폴더에 (JDK_HOME)/include/win32를 추가합니다.


4. 실행
> java HelloJniClass
(역시나 클래스패스 설정에 유의하시길 바랍니다.)
Hello Jni~!

5. 완벽한 가이드 문서


마이크로소프트의 WPF 관련 whitepaper

개발 노트 2008. 9. 3. 23:30 posted by 무병장수권력자


마이크로소프트 홈페이지에 공개되어 있는 WPF에 대한 백서 입니다.
공부를 시작하는 입장에서 한번 개요를 읽는 느낌으로 확인하면 좋을 듯 합니다.

WPF를 이용해서 이런 UI를 구성할 수 있겠다는 감을 잡으실 수 있는 글이라고 생각됩니다.
(참고로 한글입니다.)


사용자 삽입 이미지


Certificate

보안, 암호화 2008. 9. 3. 11:14 posted by 무병장수권력자


참고 출처 : caislab.icu.ac.kr/Lecture/data/2001/spring/ice525/1998/CA-mid.ppt

1. ISO/IEC/ITU-T X.509
 - Authentication services framework  for X.500
 - PKI에서 사용하는 표준 인증서 형식 (가장 널리 알려짐)
 - version 1 : 1988, version 2 : 1993 (디렉토리 접근 제어 내용 추가) , version 3 : 1996 (e 메일 보안 요소등 추가)
 - X.509 인증서는 Thawte, VeriSign, RSA와 같은 외부 CA(Certificate Authority) 기관에서 승인과정을 통해 발행

사용자 삽입 이미지


2. Certificate Revocation List
 - CA는 인증서가 취소되었을 때 취소되었음을 공개
 - CRL : CA가 서명한 취소 인증서의 time-stamped 리스트
 - 서명과 유효 기간을 확인, 가장 최근의 CRL을 요구, Serial number가 CRL에 존재 여부 확인
 - CRL은 주기적으로 리스트에 추가
 - CRL은 공개키 인증서와 같은 방법으로 분배
사용자 삽입 이미지