본문 바로가기
Tech/Coding

C언어 고급] 12🚀문자열

by redcubes 2024. 3. 31.

목차


문자열과 포인터

- 컴파일 과정에서 문자열은 첫 번째 문자의 주소로 바뀜. = 배열을 출력하는 것과 같은 과정을 거침.


문자열 상수 구현 방법

- 문자열의 크기는 일정하지 않아서 char 배열로 보관, 문자열 상수가 있던 곳에는 배열의 위치 값 사용.
- 주소로 접근해서 문자열을 바꾸면 안 됨. 컴파일은 되어도 운영체제가 상수를 읽기 전용 메모리 공간에 저장하므로 운영체제에 따라 강제종료할 수 있음.

char 포인터로 문자열 사용

- 문자열은 결국 주소 = char포인터에 대입 사용 가능. 첫 주소만 저장, 포인터 연산으로 전체 문자열 사용 가능.
같은 문자열을 여러 번 사용하면 컴파일러가 하나의 문자열만 저장하고 주소를 공유하도록 번역함.
= 최적화 = 컴파일러 종류에 따라 다름.
같은 문자열을 반복 사용할 때는 헤더에 매크로로 정의해서 쓰는 편이 한 번에 수정하기 좋음.

#include <stdio.h>

int main(void)
{
	char* dessert = "apple";                     // 포인터에 문자열 초기화

	printf("오늘 후식은 %s입니다.\n", dessert);  // 문자열 출력
	dessert = "banana";                          // 새로운 문자열 대입
	printf("내일 후식은 %s입니다.\n", dessert);  // 바뀐 문자열 출력

	return 0;
}


scanf함수를 사용한 문자열 입력

- 문자열 상수는 값을 못 바꿈. 바꿀 수 있는 문자열을 원하면 char배열을 사용.
- scanf는 버퍼에서 문자열 가져와 배열에 저장. 공백 탭 개행문자 이전까지만 저장.
- 나머지는 버퍼에 저장되어 있음.
- scanf 로 입력할 때 배열의 크기를 넘으면 메모리 침범 발생! 배열 크기 -1까지만 입력 가능.


gets 함수를 사용한 문자열 입력

- 공백이나 탭 문자를 포함해 한 줄 입력 개행 문자를 버퍼에서 빼고, 널 문자로 바꾸어 저장함!
-이것도 입력할 때 배열의 크기를 넘으면 메모리 침범 발생! 배열 크기 -1까지만 입력 가능.

- scanf는 문자열 입력하기까지 스페이스, 탭, 엔터를 눌러도 계속 입력 기다림.
- gets는 개헹문자도 입력을 받아 널문자로 바꾸어 저장하므로 입력이 종료됨.(첫 요소로 널문자 저장)

fgets 함수를 사용한 문자열 입력

배열의 크기 - 1 까지만  문자열 입력. fgets(str, sizeof(str), stdin);
개행 문자도 저장하고 널을 붙임! gets와 다름!
개행 문자를 제거하려면 str[ strlen(str) - 1] = '\0' 을 씀.

#include <stdio.h>
// 나중에 입력할 공간입니다.

int main(void)
{
	char str[80];

	printf("공백이 포함된 문자열 입력 : ");
	fgets(str, sizeof(str), stdin);              // 문자열 입력
	// 나중에 입력할 공간입니다.
	printf("입력된 문자열은 %s입니다\n", str);   // 문자열 출력

	return 0;
}


표준 입력 함수의 버퍼 공유 문제

 필요한 경우 버퍼 지우기.

getchar(); // 표준입력에서 하나의 문자를 읽어서 반환, 반환 문자는 버림
scanf("%c"); // 표준입력에서 하나의 문자를 읽어서 변수에 저장, 변수는 필요 없음
fgetc(stdin); // 표준입력에서 하나의 문자를 읽어서 반환, 반환 문자는 버림

 


문자열을 출력하는 puts, fputs 함수

시작 위치부터  널 문자 나올 때 까지 모든 문자를 출력.
char 배열명이나 문자열 상수 시작 포인터 인수로 제공 가능.
puts는 자동 줄바꿈.


gets 함수 구현

#include <stdio.h>

int main(void)
{
    int i = 0;
    char str[20];
    char ch;

    do
    {
        ch = getchar();
        str[i] = ch;
        i++;
    } while (ch != '\n');

    str[--i] = '\0';
    // printf("%s", str);
    return 0;
}

 

문자열 연산 함수


문자열을 대입하는 strcpy함수

#include <stdio.h>
#include <string.h>                        // strcpy 함수를 사용하기 위해 인클루드함

int main(void)
{
	char str1[80] = "strawberry";          // char 배열에 문자열 초기화
	char str2[80] = "apple";               // char 배열에 문자열 초기화
	char* ps1 = "banana";                  // 포인터로 문자열 상수 연결
	char* ps2 = str2;                      // 포인터로 배열 연결

	printf("최초 문자열 : %s\n", str1);
	strcpy(str1, str2);                    // 다른 char 배열의 문자열 복사
	printf("바뀐 문자열 : %s\n", str1);

	strcpy(str1, ps1);                     // 문자열 상수를 연결한 포인터 사용
	printf("바뀐 문자열 : %s\n", str1);

	strcpy(str1, ps2);                     // 배열을 연결한 포인터 사용
	printf("바뀐 문자열 : %s\n", str1);

	strcpy(str1, "banana");                // 문자열 상수 사용
	printf("바뀐 문자열 : %s\n", str1);

	return 0;
}


원하는 개수의 문자만을 복사하는 strncpy함수

#include <stdio.h>
#include <string.h>                // strncpy 함수 사용을 위한 헤더 파일 포함

int main(void)
{
	char str[20] = "mango tree";   // 배열 초기화

	strncpy(str, "apple-pie", 5);  // "apple-pie"에서 다섯 문자만 복사

	printf("%s\n", str);           // 복사 받은 문자열 출력

	return 0;
}


문자열을 붙이는 strcat, strncat 함수

#include <stdio.h>
#include <string.h>      // strcat, strncat 함수 사용을 위한 헤더 파일 포함

int main(void)
{
	char str[80] = "straw";     // 문자열 초기화

	strcat(str, "berry");       // str 배열에 문자열 붙이기
	printf("%s\n", str);
	strncat(str, "piece", 3);   // str 배열에 3개의 문자 붙이기
	printf("%s\n", str);

	return 0;
}


문자열 길이를 계산하는 strlen 함수

#include <stdio.h>
#include <string.h>                            // strlen 함수 사용을 위한 헤더 파일 포함

int main(void)
{
	char str1[80], str2[80];                   // 두 문자열을 입력할 배열
	char* resp;                                // 문자열이 긴 배열을 선택할 포인터

	printf("2개의 과일 이름 입력 : ");
	scanf("%s%s", str1, str2);                 // 2개의 문자열 입력
	if (strlen(str1) > strlen(str2))           // 배열에 입력된 문자열의 길이 비교
		resp = str1;                           // 첫 번째 배열이 긴 경우 선택
	else
		resp = str2;                           // 두 번째 배열이 긴 경우 선택
	printf("이름이 긴 과일은 : %s\n", resp);   // 선택된 배열의 문자열 출력

	return 0;
}


문자열을 비교하는 strcmp, strncmp 함수

#include <stdio.h>
#include <string.h>

int main(void)
{
	char str1[80] = "pear";
	char str2[80] = "peach";

	printf("사전에 나중에 나오는 과일 이름 : ");
	if (strcmp(str1, str2) > 0)    // str1이 str2보다 크면(사전에 나중에 나오면)
		printf("%s\n", str1);      // str1 출력
	else                           // str1이 str2보다 크지 않으면
		printf("%s\n", str2);      // str2 출력

	return 0;
}

`strcmp` 함수는 문자열 "apple"과 "Banana"를 비교할 때, 'a'와 'B'의 아스키 코드 값의 차이만을 리턴한다.
이 경우에는 'a'의 아스키 코드 값이 'B'의 아스키 코드 값보다 크기 때문에 양수 값을 반환.
이 양수 값은 "apple"이 "Banana"보다 "크다"는 것을 의미하지 않는다. 실제 문자열을 사전순으로 비교할 때 "Banana"가 "apple"보다 앞.
따라서 `strcmp` 함수의 반환값으로 사전 순서를 판단할 때는 대소문자를 고려하여 비교.
`strcmp` 함수는 대소문자를 구분하기 때문에, 대소문자가 섞여 있는 경우에는 대소문자의 아스키 코드 값 차이로 인해 예상치 못한 결과가 나올 수 있습니다.


연산 함수 직접 구현

#include <stdio.h>

char* my_strcpy(char* pd, char* ps);           // 함수 선언

int main(void)
{
	char str[80] = "strawberry";

	printf("바꾸기 전 문자열 : %s\n", str);
	my_strcpy(str, "apple");                   // 문자열 "apple" 복사
	printf("바꾼 후 문자열 : %s\n", str);
	printf("다른 문자열 대입 : %s\n", my_strcpy(str, "kiwi"));   // 반환값으로 출력

	return 0;
}

char* my_strcpy(char* pd, char* ps)  // 복사 받을 곳(pd)과 복사할 곳(ps)의 포인터
{
	char* po = pd;                   // pd 값을 나중에 반환하기 위해 보관

	while (*ps != '\0')              // ps가 가리키는 문자가 널 문자가 아닌 동안
	{
		*pd = *ps;                   // ps가 가리키는 문자를 pd가 가리키는 위치에 대입
		pd++;                        // 복사 받을 다음 위치로 포인터 증가
		ps++;                        // 복사할 다음 문자의 위치로 포인터 증가
	}
	*pd = '\0';                      // 복사가 모두 끝난 후 복사 받을 곳에 널 문자로 마무리

	return po;                       // 복사가 끝난 저장 공간의 시작 주소 반환
}

 


정리하기


요약

문자열상수는 첫 문자의메모리 주소로 번역됨.
scanf는 공백문자를 구분자로, gets는 한 줄 입력
fgets는 배열 크기 검사 기능 있음.

strcpy함수에서 문자열 복사받는 곳은 배열이어야 함.
strcat 문자열을 최초로 붙일 때는 초기화 해야 함.
배열에 저장된 실제 문자열의 길이를 알고 싶을 때는 strlen함수
strcmp 문자열의 사전(아스키) 순서를 확인할 수 있다.

문제풀이

 


궁금한 점

 

https://twitter.com/overflow_meme/status/1265663580764069888  https://www.reddit.com/r/ProgrammerHumor/comments/hd6ayh/c_doesnt_have_strings/