POCU C언어 정주행 7회차 - 문자열 함수 특징, 문자열 함수 구현 및 설명

2022. 12. 12. 03:29C언어 복습

1. C언어의 문자열 함수 특징

새로운 메모리를 선언하지 않음

C언어는 문자열 함수를 구현하는 과정에서 정말 어지간하면 새로운 char배열을 위한 메모리를 선언하지 않는다. 심지어 반환형이 char*로 되어있어서 동적으로 할당시킨 메모리를 반환해주지 않을까? 라는 생각이 들만한 함수도 절대 추가로 메모리를 할당시키지 않는다. 그 이유는 메모리 관리적인 측면 때문이라고 하는데 동적으로 생성하는 경우와 스택에 생성하는 경우를 보자.

 

만약 동적으로 할당을 시켰는데 프로그래머가 제대로 메모리를 해제시키지 않으면 메모리 누수가 발생할 수 있다. 그런데 프로그래머는 해당 함수가 동적으로 메모리를 추가로 할당시키는지 그렇지 않는지를 문서를 보기 전까지는 알 수 없으며 이것은 꽤 번거로울 것이다. 그렇다고 스택에 할당을 시키자니 이 문자열은 함수가 끝나면 메모리에서 해제되기 때문에 반환을 시킬 수 없을 것이다. 그리고 메모리를 추가로 할당하지 않더라도 충분히 구현이 가능하다면 메모리를 절약하는 측면에서 새로운 메모리를 선언하지 않는 것이 더 좋을 것이라고 생각했을 것이다.

 

몇몇 상황을 제외하고 src를 변경하지 않음

문자열 함수를 구현하는 것에 있어서 또 다른 특징은 어지간하면 src라는 매개 변수로 들어온 문자열을 변경시키지 않는다는 것이다. 그래서 대부분의 문자열 함수들을 보면 src라는 이름의 매개 변수가 char*가 아니라 const char* 라는 것을 볼 수 있다. 다만 예외적으로 strtok 등과 같은 함수는 예외적으로 어쩔 수 없이 매개 변수로 들어온 문자열을 변경시킨다고 한다. 다만 문자열 복사, 붙이기 함수와 같은 경우는 dest에 변경 사항을 저장하고 src는 변경시키지 않는다.

 

추가로 아래에 실습으로 직접 구현한 것들의 소스 코드를 첨부했는데 대부분 [ ] 라는 기호로 인덱스 연산을 하지 않은 것을 볼 수 있다. 이유는 가장 최선의 코드를 작성하기 위한 것인데 [ ]를 쓰는 것보다 포인터 연산을 통해서 아래와 같이 구현하는 것이 개미 눈알만하기는 하지만 더 성능이 좋기 때문이다. 보통 반복문에서 [i]라고 하면 반복문을 매번 돌 때마다 현재 포인터에서 i만큼 건너뛰어야 한다. 하지만 포인터로 반복문을 돌면서 덧셈 연산을 해주면 인덱스 연산에서 했던 그런 연산을 할 이유가 없다는 것이다.

 

2. 문자열 함수 및 실습 구현

최선을 다해서 직접 구현한 것들인데 혹시 뭔가 개선 사항이 보이면 댓글로 달아주시면 정말 감사할 것 같다. (이 부분은 존댓말로 써야할 것 같기는 한데 이 부분만 존댓말로 써버리면 뭔가 이상할 것 같았다... 죄송... ㅠㅠ)

 

string.h

#ifndef __STRING_H__
#define __STRING_H__

#include <stddef.h>

#define TRUE (1)
#define FALSE (0)

/* string.h - strcmp */
int compare_string(const char str1[], const char str2[]);

/* string.h - strcat */
char* concatenate_string(char dest[], const char src[]);

/* string.h - strcpy */
char* copy_string(char dest[], const char src[]);

/* string.h - strlen */
size_t get_string_length(const char str[]);

/* string.h - strtok */
char* get_string_token(char str[], const char delims[]);

/* ctype.h - isalpha */
int is_alpha(const int c);

/* string.h - strrev */
char* reverse_string(char str[]);

/* string.h - strchr */
char* search_character(const char str[], int c);

/* string.h - strrchr */
char* search_character_reverse(const char str[], int c);

/* string.h - strstr */
char* search_string(const char str[], const char substr[]);

/* string.h - strcspn */
size_t search_string_span(const char str1[], const char str2[]);

/* string.h - strnset */
char* set_character_in_string(char str[], const int c, const size_t count);

/* string.h - strlwr */
char* string_to_lower(char str[]);

/* string.h - strupr */
char* string_to_upper(char str[]);

/* ctype.h - tolower */
int to_lower(int c);

/* ctype.h - toupper */
int to_upper(int c);

#endif

 

string.c

#include "string.c"

int compare_stirng(const char str1[], const char str2[])
{
    while (*str1 == *strr2 && *str1 != '\0')
    {
        str1++;
        str2++;
    }
    
    return str1 - str2;
}

char* concatenate_string(char dest[], const char src[])
{
    dest += get_string_length(dest);
    
    copy_string(dest, src);
    
    return dest
}

char* copy_string(char dest[], const char src[])
{
    while (*src != '\0')
    {
        *dest = *src;
        src++;
        dest++;
    }
    
    *dest = '\0';
    
    return dest;
}

size_t get_string_length(const char str[])
{
    const char* ptr = str;
    
    while (*ptr != '\0')
    {
        ptr++;
    }
    
    return ptr - str;
}

static int is_delim(const char c, const char delims[])
{
    while (*delims != '\0')
    {
        if (c == *delims)
        {
            return TRUE;
        }
            
        delims++;
    }
    
    return c == '\0';
}

static char* set_delims_null(char str[], const char delims[])
{
    while (*str != '\0' && is_delim(*str, delims))
    {
        *str = '\0';
        str++;
    }
    
    return str;
}

char* get_string_token(char str[], const char delims[])
{
    static char* token = NULL;
    
    char* token_ptr = NULL;
    
    char* const ret = token = (str != NULL) ? set_delims_null(str, delims) : token;
    
    token_ptr = token;
    
    while (is_delim(*token_ptr, delims) == FALSE)
    {
        token_ptr++;
    }
    
    token = set_delims_null(token_ptr, delims);
    
    return *ret != '\0' ? ret : NULL;
}

int is_alpha(const int c)
{
    return (c >= 'A' && c <= 'Z')
        || (c >= 'a' && c <= 'z');
}

char* reverse_string(char str[])
{
    char temp = '\0';
    char* begin = str;
    char* end = str + get_string_length(str) - 1;
    
    while (end - begin > 0)
    {
        temp = *begin;
        *begin = *end;
        *end = temp;
        
        begin++;
        end--;
    }
    
    return str;
}

char* search_character(const char str[], const int c)
{
    while (*str != '\0')
    {
        if ((int)*str == c)
        {
            return (char*)str;
        }
        
        str++;
    }
    
    return NULL;
}

char* search_character_reverse(const char str[], const int c)
{
    char* ret = NULL;
    
    while (*str != '\0')
    {
        if ((int)*str == c)
        {
            ret = (char*)str;
        }
        
        str++;
    }
    
    return ret;
}

size_t search_character_span(const char str1[], const char str2[])
{
    size_t i = 0;
    
    while (*str1 != '\0')
    {
        if (search_character(str2, *str1) != NULL)
        {
            break;
        }
        
        i++;
        str1++;
    }
    
    return i;
}

char* search_string(const char str[], const char substr[])
{
    const char* ret = str;
    const char* sub_begin = substr;
    
    while (*ret != '\0')
    {
        while (*str == *substr)
        {
            str++;
            substr++;
        }
        
        if (*substr == '\0')
            return (char*)ret;
            
        ret++;
        
        str = ret;
        substr = sub_begin;
    }
    
    return NULL;
}

size_t search_string_span(const char str1[], const char str2[])
{
    size_t i = 0;
    
    while (*str1 != '\0')
    {
        if (search_character(str2, *str1) == NULL)
        {
            break;
        }
        
        i++;
        str1++;
    }
    
    return i;
}

char* set_character_in_string(char str[], const int c, const size_t count)
{
    size_t i = 0;
    char* ret = str;
    
    for (i = 0; i < count; i++)
    {
        if (*str == '\0')
        {
            break;
        }
        
        *str = c;
        str++;
    }
    
    return ret;
}

char* string_to_lower(char str[])
{
    const char* ret = str;
    
    while (*str != '\0')
    {
        if (is_alpha(*str))
        {
            *str = to_lower(*str);
        }
        
        str++;
    }
    
    return ret;
}

char* string_to_upper(char str[])
{
    const char* ret = str;
    
    while (*str != '\0')
    {
        if (is_alpha(*str))
        {
            *str = to_upper(*str);
        }
        
        str++;
    }
    
    return ret;
}

int to_lower(int c)
{
    if (is_alpha(c))
    {
        c |= 0x20;
    }
    
    return c;
}

int to_upper(int c)
{
    if (is_alpha(c))
    {
        c &= ~0x20;
    }
    
    return c;
}

 

pocu_string.h

#ifndef __POCU_STRING_H__
#define __POCU_STRING_H__

#define BUFFER_LENGTH (32u)

#define SCALE (1024.0f)

#define BYTE (1.0f)
#define KB (BYTE * SCALE)
#define MB (KB * SCALE)
#define GB (MB * SCALE)
#define TB (GB * SCALE)
#define PB (TB * SCALE)

#define DATA_STORAGE_LENGTH (6u)

/* 문자열을 받아서 저장했다가 문자열을 저장한 양이 버퍼 크기에 도달하면
버퍼 크기만큼 문자열을 한꺼번에 출력하고 버퍼를 비우며 남은 문자열은 다시 저장 */
void buffered_print(const char str[]);

/* 서식에 맞춰서 아스키 코드 표를 출력 */
void print_ascii_table(void);

/* 각 바이트 단위를 서식에 맞춰서 출력 */
void print_byte_conversion_chart(void);

/* 각 바이트 단위를 지수 표기법으로 서식에 맞춰서 출력 */
void print_byte_conversion_chart_scientific(void);

#endif

 

pocu_string.c

#include "pocu_string.h"
#include "string.h"

#include <stdio.h>

static const char* DATA_STORAGE_NAMES[DATA_STORAGE_LENGTH] = 
    { "Byte", "Kilobyte", "Megabyte", "Gigabyte", "Terabyte", "Petabyte" };

void buffered_print(const char str[])
{
    static char buffer[BUFFER_LENGTH];
    static char* buffer_ptr = buffer;
    static size_t left_buffer_size = BUFFER_LENGTH - 1u;
    
    size_t int left_data_size = get_string_length(str);
    
    while (left_data_size > 0u)
    {
        *buffer_ptr = *str;
        
        str++;
        buffer_ptr++;
        
        left_buffer_size--;
        left_data_size--;
        
        if (left_buffer_size == 0u)
        {
            printf("%s", buffer);
            
            buffer_ptr = buffer;
            *buffer_ptr = '\0';
            left_buffer_size = BUFFER_LENGTH - 1u;
        }
    }
}

void print_ascii_table(void)
{
    const int MIN_ASCII = 33;
    const int MAX_ASCII = 126;
    const int COL_NUM = 3;
    const int ROW_NUM = (MAX_ASCII - MIN_ASCII) / COL_NUM + 1;
    
    int c = 0;
    
    size_t i = 0u;
    size_t j = 0u;
    
    for (i = 0u; i < COL_NUM; i++)
        printf("Dec Hex  Char\t");
    
    printf("\n");
    
    for (i = 0u; i < ROW_NUM; i++)
    {
        c = MIN_ASCII + i;
        
        for (j = 0u; j < COL_NUM; j++)
        {
            printf("%3d %#X %3c\t", c, c, (char)c);
            c += ROW_NUM;
            
            if (c > MAX_ASCII)
                break;
        }
        
        printf("\n");
    }
}

void print_byte_conversion_chart(void)
{
    double divisor = 1.0f;
    size_t i = 0u;
    
    printf("%9s", "");
    
    for (i = 0u; i < DATA_STORAGE_LENGTH; i++)
        printf("%17s", DATA_STORAGE_NAMES[i]);
    
    printf("\n");
    
    for (i = 0u; i < DATA_STORAGE_LENGTH; i++)
    {
        printf("%8s %17.15f %17.12f %17.9f %17.6f %17.3f %17.0f\n",
            DATA_STORAGE_NAMES[i], BYTE / divisor, KB / divisor,
            MB / divisor, GB / divisor, TB / divisor, PB / divisor);
        
        divisor *= SCALE;
    }
}

void print_byte_conversion_chart_scientific(void)
{
    double divisor = 1.0f;
    size_t i = 0u;
    
    printf("%9s", "");
    
    for (i = 0u; i < DATA_STORAGE_LENGTH; i++)
        printf("%17s", DATA_STORAGE_NAMES[i]);
    
    printf("\n");
    
    for (i = 0u; i < DATA_STORAGE_LENGTH; i++)
    {
        printf("%8s %17.2e %17.2e %17.2e %17.2e %17.2e %17.2e\n",
            DATA_STORAGE_NAMES[i], BYTE / divisor, KB / divisor,
            MB / divisor, GB / divisor, TB / divisor, PB / divisor);
        
        divisor *= SCALE;
    }
}