본문 바로가기
Language & Framework & GIT/C

[씨앤씨뿔] C/ 문자열 -1

by veganwithbacon 2023. 4. 11.
반응형

  문자와 문자열

문자열(String)은 연속된 문자들의 모임이다.

C에서 문자열은 큰따옴포(" ")로 묶어주며, 문자열 끝에는 널 문자('\0')를 함께 저장한다.

위 언급처럼 끝을 나타내는 널 문자를 함께 저장하는 문자열을 널 종료 문자열(null-terminated string)이라고 한다.

 

문자열도 상수와 변수로 나뉘는데 상수는 문자열 리터럴이라고도 하며, 문자열 변수는 프로그램 수행 중에 변경될 수 있는 문자열이며 문자열 변수로 문자 배열을 사용한다. 프로그램 수행 중에 사용자로부터 입력받은 문자열을 저장하거나, 실행 중에 내용이 변경되는 문자열을 저장하려면 문자 배열을 사용해야한다.

프로그램 수행 중에 사용자로부터 입력받은 문자열을 저장하거나, 실행 중에 내용이 변경되는 문자열 저장 시, 문자 배열을 사용해야한다.

 


  문자 배열의 선언 및 초기화

문자 배열을 선언할 때는 '저장할 문자열의 길이 +1'만큼 배열의 크기를 지정한다.

널 문자를 함께 저장해야 하기 때문이다.

 

보통 문자 배열을 초기화할 때, 문자열 리터럴을 이용한다.

{ } 안에 문자를 작은 따옴표를 통해 초기화값을 초기화할 수 있다.

문자열 리터럴로 초기화 시 컴파일러가 자동으로 문자열의 끝에 널 문자를 저장한다.

 

문자열 리터럴의 길이가 '문자 배열의 크기-1'보다 작으면 배열의 나머지 원소를 널 문자로 초기화한다.

문자열 리터럴의 길이가 '문자 배열의 크기 -1'보다 크면 컴파일 경고가 발생한다.

이때, 주어진 문자열로 배열이 초기화되지만 맨 끝에 널 문자가 저장되지 않는다.

 

초기값 지정에는 문자 배열의 크기를 생략가능, 컴파일러는 '초기값으로 지정한 문자열의 길이 + 1'의 크기로 문자 배열을 할당한다. 

 

가능하면 모든 변수는 초기값이 따로 없다면 널로 초기화하는 것이 좋다.

 


  문자 배열의 사용

다른 배열과 마찬가지로, 인덱스를 이용해 배열의 원소에 접근할 수 있다.

문자 배열에서 각 원소는 char형이므로 %c 형식 문자열과 for문을 통해 출력할 수 있다.

간단한 방법은 %s 형식 문자열을 이용하는 것이다.

 

%s를 통해 출력하면 쭈르륵 다나와유. 참 재밌쥬?

알아요 노잼이에유

 

#include<stdio.h>

int main(void)
{
	char str1[10] = {'a', 'b', 'c'};
    char str2[10] = "abc";
    char str3[] = "abc";
    char str4[10] = "jgip boenjuoyo jgip gago shipda"; //컴파일 경고
    int size = sizeof(str1)/ sizeof(str1[0]);
    inti;
    
   printf("str1= ");
   for(i=0; i<size; i++)
   	printf("%c", str1[i]);
   printf("\n");
   
   printf("str2 = %s\n", str2);
   
   printf("str3 = ");
   printf(str3); //문자 배열을 직접 printf함수의 인자로 전달 가능
   printf("\n");
   
   printf("str4=%s\n",str4);
   return 0;
   }

 

문자열 배열에서의 출력은, printf함수로 문자열을 출력할 때 '\n'을 만날 때까지 출력한다.


  표준 C의 문자열 처리 함수

표준 C 라이브러리는 다양한 문자열 처리 함수를 제공한다.

문자열 처리함수를 사용하려면 <string.h>를 사용해야한다.

보통 함수명은 str로 시작하며, 표준 C 라이브러리의 문자열 처리 함수 목록이다.

 

 

문자열 처리 함수 설명
strlen(str); str 길이를 구한다(널 문자 제외)
strcmp(lhs, rhs); lhs와 rhs를 비교해서 같으면 0을, lhs>rhs면 0보다 큰값을, lhs<rhs면 0보다 작은 값을 리턴한다.
strcmp(lhs,rhs,cnt); lhs와 rhs를 cnt개만큼 비교하고, 리턴 값은 strcmp이다
strcpy(dest, src); src를 dest로 복사한다
strncpy(dest, srt, cnt); src를 dest로 cnt개만큼 복사한다
strcat(dest, src); dest의 끝에 src를 연결한다
strncat(dest, src, cnt); dest의 끝에 src를 cnt개 연결한다
strchr(str, ch); str에서 ch문자를 찾는다
strstr(str, substr); str에서 substr문자열을 찾는다
strtok(str, delim); str을 delim을 이용해 토큰을 분리한다

 

strlen을 통해 문자열의 길이를 구한 후에 한 글자를 삭제하려면 문자열의 길이(n)를 구해서 len-1 위치에 널 문자를 저장하면 된다.

len = strlen(s1);
if(len>0)
	s1[len-1]='\0'; //마지막 한 글자를 삭제한다

  문자열의 복사

: strcpy 함수는 src 문자열을 dest 문자 배열로 복사한다. 

--------------------------------------------------------------------------------------------------

strcpy의 함수 원형

char *strcpy(char *dest, const char *src);  //dest는 출력, src는 입력 매개변수

--------------------------------------------------------------------------------------------------

strcpy함수를 통해 배열을 복사하려면 복사를 받으려는 배열의 크기가 복사당하는 배열보다 크기가 같거나 커야한다.

 

그렇지 않으면 할당 받은 메모리 범위를 넘어서 메모리에 값을 저장할 때 메모리가 변조(corrupt)되는 상황인 버퍼 오버런이 발생할 수 있다.

 

src 문자열의 일부만을 복사할 때는 strncpy 함수를 이용한다.

char *strncpy(char *dest, const char *src, size_t count);

src 문자열의 일부만을 복사해야하는 경우 맨 끝에 널 문자는 저장하지 않으므로 직접 널 문자를 저장해야한다.

 

strcpy 함수를 통해 입력받은 2개의 문자 배열의 내용을 맞바꾸는 코드를 다음과 같이 짤 수 있다.

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#define SIZE 32

int main(void)
{
   char str1[SIZE] = "";
   char str2[SIZE] = "";
   char temp[SIZE];
   
   printf("2개의 문자열? ");
   scanf("%s %s", str1, str2);
   printf("str1 = %s, str2=%s\n", str1, str2);
   
   //문자열 swap 과정
   strcpy(temp, str1); //str1를 temp에 복사
   strcpy(str1, str2); //str2를 str1에 복사
   strcpy(str2, temp); //temp를 str2에 복사
   
   print("str1 = %s, str2 = %s\n", str1, str2);
   return 0;
 }

  문자열의 비교

strcmp 함수는 lhs 문자열과 rhs 문자열을 알파벳 순으로 비교한다

int strcmp(const char *lhs, const char *rhs);

srcmp함수는 lhs와 rhs가 같으면 0을, lhs가 rhs보다 알파벳 순으로 아스키 코드값이 앞이면 음수를,뒤쪽이면 양수를 리턴한다.


  문자열의 검색

char *strchr(const char *str, int ch);

char *strstr(const char* str, const char* substr);

 

strchr와 strstr은 문자나 문자열을 찾으면 찾은 위치의 주소를 리턴한다.

 

strchr와 strstr은 문자나 문자열을 찾으면 찾은 위치의 주소를 리턴한다.

그러나 문자나 문자열을 찾을 수 없다면 NULL을 리턴한다.

 

예시를 통해 확인하자면,

strchr(filename, '.');은 filename 배열에서 '.' 문자가 들어 있는 위치를 리턴한다.

'.'을 제외하고 파일 확장자 부분만 출력하려면 p+1를 printf함수의 인자로 전달하면 된다.  


  문자열의 토큰 나누기

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

 

토큰 : 어떤 문장에서 더 이상 나눌 수 없는 최소 단위

 

strtok 함수는 주어진 문자열을 delim에 있는 문자들을 통해 토큰으로 쪼개고 토큰의 주소를 리턴한다.

더 이상 토큰이 없으면 NULL을 리턴한다. strtok함수를 호출하고 나면 첫 번째 매개변수인 str이 변경되므로 주의해야한다.


  문자열의 입출력

 

보통 c언어에서 입력을 받아올 때는 scanf를 쓴다.

 

scanf함수는 "chicken soup"처럼 공백을 포함한 문자열을 입력하면 "chicken"만 str로 받아오게 된다.

scanf 함수의 기본 동작 때문에, scanf 함수는 입력 버퍼에서 문자열을 읽어 올 때 항상 공백 문자까지만 입력으로 가져온다. 그렇기에 공백을 포함한 모든 문자열을 입력받을 때는 scanf함수 대신 gets함수를 사용한다.

 

char *gets(char *str); //버퍼 오버런에 매우 취약하므로 사용하지 않는 것이 좋다

 

배열의 크기를 전달하지 않고, 사용자로부터 입력을 받기 때문에 버퍼 오버런에 매우 취약한 함수이다.

gets 함수 대신 fgets함수 또는 gets_s함수를 사용하는 것이 좋다.

 

char *fgets(char *str, int count, FILE *stream);

char *gets_s(char *str, size_t n);

 

fgets함수는 파일로부터 줄바꿈 문자를 만날 때까지 한 줄의 문자열을 읽어 오는 함수다.

이 함수의 마지막 인자로 stdin을 지정하면 표준 입력, 콘솔에서 한 줄의 문자열을 입력받는다.

두 번째 인자로 배열의 크기를 지정 시 줄바꿈 문자가 입력될 때까지/ 최대 count-1 크기의 글자를 str에 저장하고

문자열의 끝에 널 문자를 저장한다. str에는 줄바꿈 문자도 포함된다.

 

fgets 함수 대신 gets_s 함수를 사용할 수도 있는데, fgets함수처럼 한 줄의 문자열을 읽어 오는 함수지만, gets_s 함수는 읽어 온 문자열의 맨 끝에 줄바꿈 문자는 포함되지 않는다.


printf 함수를 통해 출력을 하면 필요한 곳에 직접 줄바꿈 문자를 출력해야한다.

그러나 puts함수를 인자로 사용하면 문자열의 마지막에 있는 널 문자를 줄바꿈 문자로 바꿔 출력 처리하기 때문에, 출력 후에 커서가 항상 다음 줄로 이동한다.

 

문자열 처리 함수 설명
scanf("%s", str); 공백 문자까지 문자열을 입력받아서 str에 저장한다
printf(str);
printf("%s",str)
str을 출력한다
gets_s(str, count); 한 줄의 문자열을 읽어 줄바꿈 문자를 빼고 str에 저장한다
fgets(str,count,stdin); 줄바꿈 문자를 포함한 한 줄의 문자열을 읽어서 str에 저장한다
puts(str) str과 줄바꿈 문자를 출력한다
sscanf(str, "형식문자열", ...) str에서 형식 문자열에 지정된 대로 값을 읽어 온다
sprintf(str, "형식문자열", ...) str을 형식 문자열에 지정된 대로 만든다

 

개념만 정리한 것이기 때문에, 코드 접근을 통해 이해를 원한다면 따로 에디터나 idea를 통해 사용해보는 것이 좋다

반응형

'Language & Framework & GIT > C' 카테고리의 다른 글

[씨앤씨뿔] C/ 포인터 - 2  (0) 2023.04.08
[씨앤씨뿔] C/ 포인터 - 1  (0) 2023.04.06
[씨앤씨뿔] C/ 배열  (0) 2023.04.04
[씨앤씨뿔] C/ 함수  (0) 2023.04.04
[씨앤씨뿔] C/ 제어문  (0) 2023.03.28

댓글