[Warming up C Programming] Chapter 9 : 문자열

eunee22·2023년 7월 23일

Warming-up C Programming

목록 보기
9/10

제가 대학교 1학년 때 C언어 수업에서 배운 내용을 교재와 ppt를 중심으로 정리한 내용입니다. (2022.3 ~ 2022.6)
당시에 공부를 위해서 HWP 파일로 정리해 놓은 것을 그대로 올립니다.

대학에 처음 들어와 정리한 내용이라 모든 내용을 담고 싶은 욕심에 정리가 많이 지져분하고 어설픈점 양해 부탁드립니다..!

🍑문자 배열

문자와 문자열

  • 문자열 : 연속된 문자들의 모임

    • 문자나 문자코드는 ‘ ’로 묶어주는 반면, 문자열은 “ ” 로 묶어줌.
  • 널 종료 문자열 : 문자열의 끝을 나타내는 널 문자(‘\0’)를 함께 저장하는 문자열

  • 문자열 상수(문자형 리터럴) : 값이 변경되지 않는 문자열
    ex) “hello” “\n”와 같이 표현

  • 문자열 변수

    • 문자 배열을 사용하여 문자열을 변수에 저장함.
    • 실행 중에 사용자로부터 입력받은 문자열을 저장하거나, 내용이 변경되는 문자열을 저장하려면 문자 배열을 사용해야함.

문자 배열의 선언 및 초기화

  • 문자 배열을 선언할 때 배열의 크기는 널문자까지 고려해 저장할 문자열의 길이’ + 1로 지정
  • 문자 배열을 초기화 할때는 문자열 리터럴 이용 → 문자열 리터럴로 초기화되면 자동으로 맨 뒤에 널문자 추가
    1. 문자 배열의 크기 > 문자 리터럴의 길이 → 배열의 나머지 원소는 널 문자로 초기화
    2. 문자 배열의 크기 < 문자 리터럴의 길이 → 널문자가 없으면 문제가 발생할 수 있어서 컴파일 경고가 발생.
  • char 배열명[배열의크기] = {‘a’, ‘b’} 형식으로도 초기화 가능.
  • 초기값을 지정하는 경우, 문자 배열의 크기 생략 가능.
  • 문자 배열 전체를 널 문자로 초기화하려면 널문자열(“ ”)로 초기화.
    • 널 문자열 : 문자열 하나로 이루어진 문자열. 연속된 큰따옴표 2개로 표시.
      널문자열(‘\0’)과 빈칸(‘ ’)은 다름.

문자 배열의 사용

  • 문자 배열의 인덱스를 사용하면, 문자열의 문자를 하나씩 읽거나 변경 가능

  • 문자열 전체를 출력하려면 %s 서식지정자 사용
    형식문자열 없이 문자 배열을 직접 printf 함수의 첫 번째 인자로 전달 가능
    ex) printf(“%s”, str); 대신 printf(str)
    문자열 출력 서식 지정자 %s는 널 문자를 만날 때까지 str 배열의 원소를 모두 출력

  • 문자 배열에 문자열 리터럴을 대입하면 안됨.(컴파일 에러)

    • 배열의 이름은 배열의 시작 주소이므로 변경이 불가하기 때문
    • 배열 초기화때는 가능하지만, 선언 이후에는 불가
    • 배열의 값을 변경하려면 문자 하나씩 변경해야함 → 이때 널 문자가 자동으로 추가되지 않으니 직접 추가해야함
  • printf 함수는 널 문자를 만날 때까지 출력하는데, 배열의 크기보다 긴 문자열로 초기화시 문자열을 끝을 확인할 수 없으므로 쓰레기값이 함께 출력됨

🍑표준 C의 문자열 처리 함수

  • 문자열 처리 함수를 이용하면 구체적인 처리 과정은 알 필요없이 간단하게 문자열을 다룰수 있음.
  • 문자열 처리 함수를 사용하려면 <string.h>를 포함해야함. (종류는 아래에)
  • 문자 처리 함수를 사용하려면 <ctype.h>를 포함해야함.
  • 종류
    1. isalnum(ch); → 알파벳이나 숫자인지 검사
    2. isdigit(ch); → 숫자인지 검사
    3. isupper(ch); → 대문자인지 검사
    4. isxdigit(ch); → 16진수 숫자인지 검사
    5. toupper(ch); → 대문자로 변환
    6. isalpha(ch); → 알파벳인지 검사
    7. islower(ch); → 소문자인지 검사
    8. isspace(ch); → 공백 문자인지 검사
    9. tolower(ch); → 소문자로 변환

표준 C데이터 변환 함수

  • 헤더파일 <stdlib.h>
  1. int atoi(const char* str); → 문자열을 정수로 변환.
  2. double atof(const char* str); → 문자열을 실수로 변환.
  3. long atol(const char* str); → 문자열을 long형 값으로 변환.
  • 헤더파일 <stdio.h>
  1. int sscanf(const char* buff, const char* format, ....); → 문자열을 정수나 실수로 변환해서 읽어옴.
  2. int sprintf(char* buff, const char* format, ......); → 형식 문자열을 이용해서 정수나 실수를 문자열로 변환.

표준 C문자열 입출력 함수

  1. scanf(“%s”, str); → 공백 문자까지 문자열을 입력받아서 str에 저장.
  2. printf(str); printf(“%s”, str);str을 출력
  3. gets_s(str, count); → 한 줄의 문자열을 읽어서 str에 저장.
    (줄바꿈 문자 포함 X)
  4. fgets(str, count, stdin); → 한 줄의 문자열을 읽어서 str에 저장.
    (줄바꿈 문자 포함)
  5. puts(str);str을 출력하고 줄을 바꿈.

문자열의 길이 구하기

  • strlen 함수 : 널 문자를 제외한 문자열의 길이를 구함
    • 문자열의 길이 → 빈칸 포함
  • size_t strlen(const char* str);를 원형으로 가짐

문자열의 복사

  • strcpy 함수 : src문자열을 dest 문자 배열로 복사한 다음 dest를 리턴
    • 기존 내용 삭제, 널문자도 함께 복사
  • char* strcpy(char* dest, const char* src); 가 원형 → strcpy(dest, src);
  • 첫 번째 매개변수 dest에는 반드시 문자 배열을 지정(아니면 실행 에러. 변경불가 이기 때문), 두 번째 매개변수인 src에는 문자 배열, 문자 리터럴 전부 사용 가능.
  • vs2019에서 사용시 컴파일 에러 발생.
    • strcpy 함수는 dest문자 배열의 크기에 관계없이(공간이 충분한지 확인하지 않고 널 문자까지 1:1로 복사) 무조건 src 문자열을 복사하므로 버퍼 오버런의 위험을 갖고 있음
    • 버퍼 오버런 : 할당 받은 메모리 범위를 넘어서 값을 저장할 때 메모리가 변조되는 상황.
      • c99 이상을 지원하는 컴파일러의 경우 표준 C라이브러리 함수 중 버퍼오버런이 있는 함수에 대해 컴파일 경고/에러 발생 시킴.

컴파일 경고/에러 없애는법

  1. 안전 문자열 처리 함수인 strcpy_s를 대신 사용 → vs에서만 적용. 표준X
  2. _CRT_SECURE_NO_WARNINGS 매크로를 라이브러리 헤더 파일을 포함하는 문장 앞에 적어줌 → 대체 코드는 scanf 함수 안정성 문제 해결 코드와 동일
    문자열의 비교
  • strcmp 함수 : lhs 문자열과 rhs 문자열을 알파벳 순으로 비교
  • int strcmp(const char* lhs, const char* rhs); 원형 → srrcmp(lhs, rhs);
  • lhs문자열과 rhs문자열을 알파벳순으로 비교.
  • 대소문자까지 완벽히 같으면 0, lhsrhs 보다 알파벳순으로 앞쪽이면 음수, 뒤쪽이면 양수를 리턴.
  • 문자 배열의 이름을 관계연산자를 통해 비교하면 문자열의 주소를 비교함. 내용을 비교하려면 strcmp함수를 이용해야함.

문자열의 연결

  • strcat 함수 : dest문자 배열에 저장된 문자열의 끝에 src 문자열을 복사해서 연결.
  • char* strcat(char* dest, const char* src); 가 원형 → strcat(dest, src);
  • 첫 번째 매개변수인 dest엔 반드시 문자 배열을 지정해야함.
  • dest 문자 배열에 src 문자열을 연결할 만큼 메모리가 충분한지를 확인하지 않으므로 버퍼 오버런의 위험이 있음.
  • 널문자까지 복사
  • 빈칸을 붙이려면 src 자리에 “ ”를 붙이면 됨.

문자열의 검색

  • strchr 함수 : str에서 ch문자를 찾음
  • strstr 함수 : str에서 substr 문자열을 찾음.
  • char* strchr(const char* str, int ch);strchr(str, ‘ch’);
    • char* strstr(const char* str, const char* substr); 가 원형 → strstr(str, “substr”);
  • 문자나 문자열을 찾으면 찾은 위치에 있는 문자의 주소(char*형)를 리턴, 찾을수 없으면 NULL을 리턴.
    문자열의 토큰 나누기
  • strtok 함수 : str문자열을 delim 문자열에 있는 문자들을 이용해서 분리
  • char* strtok(char* str, const char* delim); 가 원형 → strtok(dtr, “delim”);
  • 토큰 : 문장에서 더 이상 나눌수 없는 최소 단위
  • 토큰으로 쪼개고 토큰의 주소를 리턴, 토큰이 없으면 NULL을 리턴
  • strtok 함수 호출 후에 첫 번째 매개변수인 str 이 변경.
  • strtok 함수의 첫 번째 인자로 NULL을 지정하면 계속해서 다음 토큰을 얻을수 있음.
    → 토큰을 찾은 후, 토큰 분리를 위해 사용된 애는 자동으로 NULL이 되는데, strtok()은 토큰으로 사용한 위치를 알기에 NULL뒤 ~ 다음 분리기호 직전까지 토큰으로 만듦.
  • 문자열(문자배열)의 맨 뒤에는 delim이 없지만, NULL문자가 있는 경우 문장의 마지막으로 인식하고 직전까지 토큰으로 만듦.

문자열의 입출력

  • scnaf 함수로 문자열을 받으려면 %s 서식지정자를 이용함.

  • scanf 함수는 입력 버퍼에서 공백 문자를 만날 때 까지 문자열을 읽어옴 → 공백문자는 받아들이지 못함

  • 빈칸을 포함한 문자를 입력 받으려면 fgets, gets_s 사용

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

    • char* fgets(char* str, int count(배열의크기), FILE* stream); 가 원형 → fgets(str, sizeof(str), stdin);
      마지막 매개변수인 steam에 stdin을 전달하면 콘솔에서 한 줄의 문자열을 입력받음.
    • str(읽어온 문자열)에 줄바꿈 문자도 함께 저장.
  • gets_s 함수 : 줄바꿈 문자를 입력 될 때까지 한 줄의 문자열을 파일로부터 읽어오는 함수

    • char* gets_s(char* str, size_t n(배열의크기)); 가 원형 → gets_s(str, sizeof(str));
    • str(읽어온 문자열)에 줄바꿈 문자가 포함되지 않음.
  • printf 함수의 첫 번째 인자로 문자열을 바로 전달 가능.
    ex) printf(str);

  • puts 함수 : 한 줄의 문자열을 출력
    출력후 자동으로 줄이 바뀜.
    int puts(const char* str); 가 원형 → puts(n);

  • sscanf 함수
    int sscanf(const char* buffer, const char* format, ...);sscanf(str, “%d”, &n);

    • buffer 문자열로부터 형식 문자열인 format에 지정된 대로 값을 읽어옴(주어진 문자열에서 값을 읽어옴)
    • 주로 gets_s 함수와 함께 사용
  • sprintf 함수
    int sprintf(char* buffer, const char* format, ...);sprintf(str, “n = %d”, n);

    • 형식 문자열에 지정된 대로 문자열을 만들어서 buffer 문자 배열에 저장(출력할 내용을 문자 배열에 저장)
    • 주로 puts 함수와 함께 사용

🍑문자열 포인터

문자열 포인터의 용도

  1. 주로 문자 배열을 가르키는 데 사용
  2. 문자열 포인터가 가르키는 문자 배열을 변경할 수 있음
  3. 문자열 포인터를 이용하면 문자 배열의 특정 위치를 가리키도록 변경해가면서 문자열에 대한 처리 가능.

char* 형의 문자열 포인터

  • char* 형의 포인터는 char형의 변수 또는 char형 배열의 원소를 가르킬수있음
  • 주로 문자열을 가르키는 용도로 사용하므로 문자열 포인터라고함
  • 문자열 포인터에 다른 문자열을 대입하면 포인터가 가르키는 대상만 달라짐. (문자열 내용 변경X)

문자열 리터럴 : 문자열 리터럴의 주소

  • 상수는 임시값이므로 메모리에 할당되지 않으며, 주소가 없으나 문자열 리터럴은 예외적으로 문자열 리터럴만 저장되는 전용 메모리 공간에 할당.
  • 메모리에 저장되지만, 변경할 수 없는 메모리에 저장되기 때문에 값을 변경할 수 없음.(실행에러)
  • 읽기 전용으로만 사용해야함.
  • 문자열 리터럴은 변경 불가하지만, 문자열 리터럴을 문자 배열에 할당한것이므로 문자 배열의 내용은 변경 가능

const char* 형의 문자열 포인터

  • 읽기 전용의 문자열 포인터이므로 포인터가 가르키는 문자열(문자 배열)의 내용을 읽어볼 수만 있고, 변경할 수 없음. (컴파일 에러)
  • const char* 형을 char* 형으로 변환하면 컴파일 경고가 발생
  • 주로 문자열 리터럴을 가르키는 용도로 사용.
    • 문자배열도 가능
  • 문자열을 입력 매개변수로 갖는 함수를 정의 시 유용하게 사용.
    • 이때는 널 문자로 문자열의 끝을 판단 가능하기에 배열의 크기를 전달할 필요가 없음
  • 읽기 전용 포인터가 다른 문자를 가르키게 만들순 있음

문자열 사용을 위한 가이드 라인

문자열을 어떤 형의 변수에 저장할지 선택하는 기준

  1. 사용자로부터 입력 받거나 변경 가능한 문자열 → 문자열 변수
  2. 프로그램 실핼 중에 변경되지 않는 문자열 → 문자열 상수

어떤 형의 문자열 포인터를 사용할지 선택하는 기준

  1. 문자 배열(변경 가능한 문자열)을 가리킬 때만 사용 → char*
  2. 변경 불가한 문자열을 가리킬 때 → const char*
  3. 문자열 리터럴을 가리킬 때 → const char*
  4. 문자 배열을 읽기 전용으로 접근 → const char*

문자열을 매개변수로 갖는 함수를 정의시 주의사항

  1. 문자열이 출력 매개변수 → char*형의 매개변수를 사용, 배열의 크기도 매개변수로 전달.
  2. 문자열이 입력 매개변수 → const char*형의 매개변수를 사용. 문자열의 끝을 널 문자로 확인 가능하므로 배열의 크기를 매개변수로 받아올 필요x. 함수안에서 문자열을 변경해선 안됨.
  3. 함수 안에서 문자열 포인터 이용시 문자 배열처럼 인덱스 사용 가능.

문자열을 매개변수로 갖는 함수를 호출할 때의 주의 사항

  1. 매개변수의 데이터형이 char*형일 때는 문자배열과 char*형의 포인터만 인자로 전달 가능.
    함수 호출 후 문자열의 내용 변경 가능,
  2. 매개변수의 데이터형이 const char*형일 때는 문자 배열, 문자열 리터럴, char*형의 포인터, const char*형의 포인터를 모두 인자로 전달 가능. 함수 호출 후에도 문자열의 내용 변경x

🍑문자열의 배열

  • 여러개의 문자열을 저장하려면 2차원 배열 필요.
  • 문자열 포인터의 배열을 사용하여 변경되지 않는 문자열을 여러개 저장 가능. 이때는 문자열 리터럴의 주소만 배열로 정리하면됨.

2차원 문자 배열

  • 2차원 문자 배열 선언 시 널 문자를 포함한 문자열의 길이를 열 크기로, 문자열의 개수를 행의 크기로 지정.
  • { } 안에 문자열을 나열해서 초기화.
  • 문자열 하나에 접근하려면 행 인덱스만 사용
  • i번째 문자열의 j번째 문자에 접근하려면 행과 열 인덱스 모두 사용.
    문자열 포인터 배열
  • 문자열 포인터 배열에 문자열 리터럴을 모아두면 문자열 리터럴을 관리하기 쉬워짐
    • 같은 문자열 리터럴을 여러 번 사용할 때는 문자열 포인터에 문자열 리터럴의 주소를 저장해두고 사용하는 것이 좋으므로
profile
보안 공부하는 대학교 4학년 / 시리즈에서 더욱 편하게 글을 찾아보실 수 있습니다:)

2개의 댓글

comment-user-thumbnail
2023년 7월 23일

정리가 잘 된 글이네요. 도움이 됐습니다.

1개의 답글