Taking baby-developer steps

11. 문자열 - 문자열의 개념 / 널(NULL)값 / 문자열과 포인터 / 문자열 리터럴 / 문자열 입출력 함수 / gets () 함수 /gets_s() 함수 / sizeof()함수 / 문자열 처리 함수 본문

CS 지식/C언어_basic

11. 문자열 - 문자열의 개념 / 널(NULL)값 / 문자열과 포인터 / 문자열 리터럴 / 문자열 입출력 함수 / gets () 함수 /gets_s() 함수 / sizeof()함수 / 문자열 처리 함수

Surin Lee 2021. 3. 25. 14:16

문자열

전통적인 C언어에서 문자열을 다루는 방법과 다양한 문자열 관련 함수를 익히고 활용한다.

 

문자열의 개념

문자열이란 말 그대로 문자들의 배열이다. 문자열은 컴퓨터 메모리 구조상에서 마지막에 널(NULL)값을 포함한다. 컴퓨터에서 널(NULL)이란, '존재하지 않는다'라는 뜻으로, 즉 어떠한 값도 의미있는 값이 들어가 있지 않음을 의미한다. 이 널(NULL)값은 '\0'이라는 문자 형태로도 표현한다.

0 1 2 3 4 5
H E L L O \0

위의 문자열은 총 5개의 문자와 NULL값으로 이루어져 있다.

 

널값은 문자열의 끝을 알리는 목적으로 사용된다. printf() 함수를 실행하면 컴퓨터는 내부적으로 NULL을 만날 때 까지 출력한다. 이처럼 NULL값은 문자를 처리 할 때 중요한 요소로 사용된다.

 

문자열과 포인터

문자열 형태로 포인터를 사용하면 포인터에 특정한 문자열의 주소를 넣게 된다.

#include <stdio.h>

int main(void){
	char *a = "Hello World";
    printf("%s\n", a);
	return 0;
}

위의 코드는 "Hello World"라는 문자열을 읽기 전용으로 메모리 공간에 넣은 뒤에 그 위치를 처리한다. 위의 코드에서

char *a = "Hello World" ; 

처럼 포인터 변수에 '문자열 자체'를 마치 상수처럼 넣을 수 있는데, 이런 문자열 자체를 '문자열 리터럴'이라고 한다. 이렇게 문자열 리터럴 형태로 쓰면, 컴파일러가 알아서 "Hello World"라는 문자열이 특정한 컴퓨터 메모리 주소에 담길 수 있도록 알아서 남아있는 메모리 공간 중 '주소'를 결정 해주며, 이 주소를 포인터가 가지게 된다.

이렇게 문자열 리터럴 방식으로 선언을 해주면, 이 문자를 자체는 상수로서 '읽기 전용'으로 사용이 된다. (즉, 변경이 불가능 해진다.)

+ 굳이 문자열을 변경해서 사용하고 싶다면, 포인터 a가 가리키고 있는 주소를 바꾸어(다른 주소를 가리키게 한 후) 사용해야 한다.

 

포인터로 문자열을 선언 했을 때, 기존의 배열 처럼 처리할 수 있다.

#include <stdio.h>

int main(void){
	char *a = "Hello World";
    printf("%s\n", a[1]);
    printf("%s\n", a[4]);
    printf("%s\n", a[8]);
	return 0;
}

-> e o r (각각 줄 바꿔서 출력 됨)

기존의 배열 처럼 각 인덱스에 해당하는 값이 출력되는 것을 확인 할 수 있다. 배열과 포인터는 치환이 가능하므로, Printf()함수 내에서 주소 연산자 &없이 쓰인것을 볼 수 있다.

 

문자열 입출력 함수

gets() 함수 / gets_s() 함수

scanf() 함수는 공백을 만날 때까지 입력 받지만, gets() 함수는 공백까지 포함하여 한 줄을 입력 받는다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void){
	char a[100];
    gets(a) ;
    printf("%s\n",a);
	return 0;
}

gets() 함수는 버퍼의 크기를 벗어나도 입력을 받아버린다는 취약점이 있다. (배열의 전체 범위를 고려하지 않는다는 점에서, 보안상의 취약점이 있다. 공부하는 입장으로 gets()가 더 이해가 쉬우므로, _CRT_SECURE_NO_WARNINGS를 쓰고, 사용한다. 실무에선 gets_s()를 사용한다.) gets()함수가 버퍼의 크기를 벗어나도 입력을 받아버리면, 입력하는 사용자가 프로그램의 다른 부분을 임의대로 덮어쓰기 할 수도 있다는 취약점이 생긴다.

 이런 취약점을 보안하기 위해, C11 표준부터는 버퍼의 크기를 철저히 지키는 gets_s()함수가 추가 되었다. gets_s()함수는 매개변수를 2개를 받고, 특정한 범위 만큼만 정확히 문자를 입력 받아 다소 안정적이다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void){
	char a[100];
    gets_s(a,sizeof(a));
    printf("%s\n",a);
}

-> 입력 값으로 버퍼의 크기(100자)를 벗어나게 되면, 오류메세지가 뜨면서 프로그램이 종료된다.

gets_s()를 이용하는 경우, 입력 범위를 넘으면 그 즉시 런타임(Runtime) 오류가 발생한다.

 

+ sizeof() 함수

C언어에서 기본적으로 제공하는 함수로서, 특정한 배열의 "전체 크기가 얼마인지"를 알려주는 함수 이다.

 

문자열 처리를 위한 다양한 함수

C언어에서의 문자열 함수는 <string.h> 라이브러리에 포함되어 있다. C언어의 문자열 처리와 관련한 기본적인 문자열 함수를 알고 있는 것이 좋다. 후에 C++을 이용하면 더욱 간편하고 다양한 함수를 사용 할 수 있다.

 

strlen() / srtlen(a) 문자열의 길이를 반환한다.
strcmp() / strcmp(a, b) 문자열 1이 문자열 2보다 사전적으로 앞에 있으면 -1,
뒤에 있으면 1을 반환한다.
strcpy() / strcpy(a, b) 문자열을 복사한다.
strcat() / strcat(a, b) 문자열 1에 문자열 2를 더한다.
(이어서 작성한다.)
strstr() / strstr(a, b) 문자열 1에 문자열 2가 어떻게 포함되어 있는지를 반환한다.

+전통적인 C언어에서는 위의 함수들을 이용하려면, #include <string.h>를 써야하지만, 최근 C언어는 문자열 함수를 기본적으로 포함하는 경우가 많아, 써주지 않아도 제대로 동작한다.

 

strcat()에서 합쳐지는 배열의 크기가 부족하면, 다양한 오류가 발생 할 수 있으므로 늘려주는게 좋다.

strstr()은 긴 문자열에서 짧은 문자열을 찾아 그 위치를 반환한다. 짧은 문자열을 찾은 주소 값 자체를 반환하므로, 단순히 출력하도록 하면 찾은 이후 모든 문자열이 반환된다.

#include <stdio.h>

int main (void){
	char a[20] = "I like you";
    char b[20] = "like" ; 
    printf("찾은 문자열 : %s\n", strstr(a, b));
	return 0;
}

-> like you

 

요약

  1. C언어에서 문자열은 배열이므로 포인터 형태로 사용할 수 있다.
  2. C언어에서 문자열 비교, 연산, 탐색 등의 알고리즘의 사용 방법은 각각 함수 형태로 제공 된다.
Comments