2022. 12. 12. 03:29ㆍC언어 복습
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;
}
}
'C언어 복습' 카테고리의 다른 글
POCU C언어 정주행 9회차 - 파일 입출력, 예외 처리, 스트림 위치 (0) | 2022.12.16 |
---|---|
POCU C언어 정주행 8회차 - 스트림, 입출력 함수, 버퍼, 입력 알고리즘 (0) | 2022.12.14 |
POCU C언어 정주행 6회차 - 댕글링 포인터, 포인터 연산, 캐스팅, ASLR (0) | 2022.12.10 |
POCU C언어 정주행 4회차 - scope(범위), goto문, const (2) | 2022.12.08 |
POCU C언어 정주행 3회차 - 평가 순서, Sequence Point (0) | 2022.12.08 |