42 Starter 스터디 모임에서 진행하는 C 언어 A to Z 스터디에 내용을 정리해서 포스팅을 진행합니다.

해당 스터디에서는 42 Piscine 과정에서 채우지못한 부족한 C언어 역량을 키우는 것을 목적으로 합니다.


1. strlen

size_t strlen(const char *str)

문자열을 처리하기 위해 문자열의 길이가 필요하다. 이럴때 사용하는 함수가 strlen이다.

문자열의 길이를 구하는 함수 strlen에 대해서 알아보기 이전에 문자열의 길이는 어떻게 구할까에 대해서 생각해보자.

생각해볼 수 있는 방법은 두가지 정도가 있다.

  1. 문자열의 길이를 문자열 앞에 저장을 해두는 방법
  2. Null Terminating

1번의 방법은 첫 메모리 위치에 문자열 길이를 저장하고 실제 문자열이 뒤따라오게 하는 방법인데 문자열의 길이를 저장할때에 문제가 있다.

그 문제는 문자열을 모두 char 형으로 이용하기 위해 unsigned char 로 문자열의 길이를 저장해놓게되면 최대 255자를 나타내는 길이까지만 저장할 수 있다. 이렇기 때문에 길이를 int 이상으로 저장해야한다.

1번의 방법은 첫 주소만 보는 것으로 총 글자 수가 몇자인지 알 수 있는 장점이있다. 하지만 C언어에서는 불편함이 있는데 데이터를 읽을때에 문제이다. 첫 데이터를 int* 로 캐스팅하여 읽고 나머지를 char* 로 읽어야 하기 때문에 번거로움이 있다. 그렇기 때문에 순수 C언어에서는 어려움이 있다.

2번의 방법은 길이를 지정해서 저장하는 것이 아니라 문자열이 끝나는 위치에 특수한 문자를 두는 방식이다. 즉, null 캐릭터를 문자열 끝에 넣는 방식이다. 그렇기 때문에 문자열을 담는 배열의 길이는 문자열 + 널 캐릭터 가 된다.

이 방법의 장점은 가장 최소한의 메모리를 사용한다는 점이다. 앞선 방법에서와 다르게 문자열의 길이를 저장하지 않기 떄문이다. 그리고 한 가지의 데이터형으로 문자열과 길이를 다 표현할 수 있기 때문에 캐스팅과 같은 방법이 필요하지 않다. 하지만 문자열의 길이를 알려면 배열을 끝까지 봐야하기 떄문에 O(N)의 시간복잡도가 든다.

*추가정보: 꼭 시간복잡도 빅오 표기법에 대해서 알아보자.

*추가정보: 문자열의 길이를 구할때는 인덱스를 이동하는 것이 아니라 포인터의 연산을 이용하는 것이 더 효율적이다. 어셈블러 코드를 뜯어보자.

size_t	get_string_length(const char* str)
{
	const char* p = str;

	while (*p++ != '\0'){}
	return (p - str - 1);
}

2. strcmp, strncmp

int strcmp (const char* str1, const char* str2)

int strncmp(const char *str1, const char *str2, size_t n)

두 문자열을 비교하는 함수 strcmp 에 대해서 알아보자.

strcmp 함수는 사전식 순서로 어떤 문자의 아스키코드가 더 작은 지, 큰 지, 같은 지를 판별한다. 여기서 사전식 순서란 길이가 긴 문자열일수록 더 크고 아스키코드가 클수록 더 큰 문자인 것을 말한다. 즉 ABC 보다 ABCD가 크고 ABCD보다 ABCE가 더 크다.

strcmp 은 두 가지를 매개변수로 갖는다. 좌측 문자열이 더 크면 반환값은 양수, 우측 문자열이 더크면 음수, 같으면 0이다. 따라서 int 형을 반환한다.

함수의 알고리즘은 두 문자열을 처음부터 탐색하기 시작하여 두 문자열이 다른 부분까지 이동을한다. 그 이후 현재 두 문자열 각각의 두 문자를 비교하여 결과를 반환한다.

int	compare_string(const char* str0, const char* str1)
{
	while (*str0 != '\0' && *str0 == *str1)
	{
		++str0;
		++str1;
	}
	return (*str0 - *str1);
}

*추가정보: strncmp 는 최대 n 문자까지만 비교를 하는 함수이다. strcmp 에서 종료조건 하나가 추가된 함수이다.

3. strcpy, strncpy

char* strcpy(char* dest, const char* src)

char *strncpy(char *dest, const char *src, size_t n)

문자열을 복사하는 함수 strcpy 에 대해서 알아보자.

strcpy 함수는 dest 문자열에 src 문자열을 복사하는 함수이다. src 문자열이 Null 캐릭터가 되기전까지 destsrc 값을 대입하는 방식으로 알고리즘이 되어있다.

위와 같은 방식으로 구현되어있기 때문에 dest 문자열의 길이가 src 의 문자열 길이보다 짧으면 잘못된 메모리에 src 의 내용을 채우는 문제가 생길 수 있다. 따라서 사용을 할때 srcdest 의 길이를 확실하게 통제해야한다. C11에서는 이보다 안전한 strcpy_s() 함수가 나와있다.

위에 문제를 어떻게 하면 그나마 안전하게 구현할 수 있을지 생각해보자. 그 방법 중 하나로 dest 길이를 알고 있고 dest 의 길이만큼만 src 의 내용을 복사하는 방식이다. 바로 이것이 strncpy 이다.

strncpy 는 매개변수로 n 만큼의 길이를 받는다. 즉 srcn 만큼의 내용만을 복사한다. 하지만 이런 방식을 하더라도 문제가 생기는데, ndest 의 길이만큼이 된다면 destnull 캐릭터 자리까지 src 의 값으로 채워질 수 있는 것이다. 따라서 보편적으로 strncpy 를 사용하고 나서는 dest[n - 1] = '\0' 처리를 해준다. (strlcpy 의 내용에서 사용되는 것과 같다.)

반환 값으로 dest 의 주소를 반환한다.

4. strcat, strncat

char *strcat(char *dest, const char *src)

char *strncat(char *dest, const char *src, size_t n)

문자열을 합치는 함수 strcat 에 대해서 알아보자.

strcat 함수는 src 문자열을 dest 뒤에 덧붙이는 함수이다. 즉, dest 의 널 문자가 들어있는 위치부터 src 문자열을 추가한다.

이 함수도 strcpy와 같이 dest 의 길이가 충분하지 않을 경우 길이를 넘어 잘못된 메모리에 값을 저장하는 문제가 발생한다. 이를 그나마 보완한 함수 strncat 가 있다. strncpy와 같이 매개변수로 얼마만큼 붙일 것인지 길이 n 을 받고 n 만큼 src 의 내용을 붙여주고 널 문자를 붙여준다. 이 함수도 dest 의 남아있는 공간보다 n 의 크기가 큰 경우에 정의되지 않은 결과가 발생한다.

반환 값으로 dest 의 주소를 반환한다.

5. strstr

char *strstr(const char *haystack, const char *needle)

문자열안에서 문자열을 찾는 함수 strstr 에 대해서 알아보자.

strstr 함수는 haystack 매개변수를 기준으로 needle을 찾는다. 만약 needle이 존재하지 않으면 Null을 존재한다면 haystack 에서 needle 이 시작하는 주소를 반환한다. 따라서 존재하게 되었을 때 반환값을 출력해보면 haystack 에서 needle 의 값과 일치하는 값이 시작하는 주소로부터 haystack 이 끝나는 지점까지의 내용이 모두 출력된다.

haystack 에서 찾은 needle 값을 복사하지 않는 이유는 메모리 효율을 생각해서 새로운 메모리를 할당하지 않고 그대로 주소값만을 반환한다.

6. 문자열 토큰화 strtok

char *strtok(char *str, const char *delim)

문자열을 토큰화해주는 함수 strtok 에 대해서 알아보자.

문자열 토큰화란 구분 문자를 기준으로 각각의 문자열을 substring 으로 나누는 작업을 말한다.

// 구분 문자(delims) : ',' '.' ' '
// 토큰화할 문자열 : Hi, There. Hello. Bye
		msg : Hi, there. Hello. Bye\0
1단계	msg : Hi\0there. Hello. Bye\0 -> Hi 의 시작 주소를 반환
2단계	msg : Hi\0there\0Hello. Bye\0 -> there의 시작 주소를 반환
3단계	msg : Hi\0there\0Hello\0Bye\0 -> Hello의 시작 주소를 반환
4단계	msg : Hi\0there\0Hello\0Bye\0 -> Bye의 시작 주소를 반환

strtok은 위와 같이 단계별로 토큰을 만들어서 반환해주는 함수이다. 처음에 strtok 를 호출 할 때에는 str 매개변수 자리에 토큰화할 문자열을 넣는다. 그리고 다음 토큰을 반환받을 때에는 NULL을 넣는 방식으로 토큰을 반환받는다.

그렇다면 NULL을 넣었을 때 어떻게 strtok 함수는 토큰화하고 있는 문자열의 주소를 기억하고 있을지 생각할 수 있을까? 그 방법은 바로 strtokstatic 으로 처음에 받은 문자열의 주소를 저장해두고 계속해서 이동하면서 사용하고있는 것이다. 만약 1단계가 지났으면 Hi\0가 지난 there의 시작주소가 저장되어 있을 것이다.

*추가정보 : 소문자를 대문자로 바꾸는 방법으로 비트마스킹을 사용할 수 있다.

// c 는 대문자
char lower;
lower = c & ~0x20;
⤧  Next post Oh-My-C-Lang - Section 6 - 05 ⤧  Previous post Oh-My-C-P-P 05