C 언어(23) - 문자열 다루기

C 언어 강의, 문자열을 배열의 관점으로 이해

Featured image

🔚 짧게 하는 복습

✅ 1. 버퍼에 대해 안다.

✅ 2. scanf()의 특징을 버퍼와 연결 지어 이해한다.

✅ 3. getchar(), gets()를 자유자재로 사용할 수 있다.

혹시 기억이 안 난다면, 다시 돌아가자


우리는 이때까지 %s를 이용해 문자열을 입력, 출력 방법을 배웠다.

그런데 문자열이라는 것은 도대체 무엇일까?

우리가 배웠던 것들을 조합해서 추론해보자.


문자 배열 vs 문자열

우선 문자열이라는 것은 문자를 여러 개하나의 문장 혹은 단어처럼 다루는 자료구조이다.

무슨 말이냐면, Happy라는 단어에서 H, a, p, y 각각의 문자는 사실 큰 의미가 없다.

비로소 저 순서대로, 5개의 글자를 다룰 때 의미가 있는 것이다.

여기서 우리가 추론할 수 있는 것은, 순서대로전체를 다룰 수 있어야 한다는 것이다.

그렇다면, 우리가 배운 자료구조 중 순서가 중요하고, 전체와 개별 원소를 모두 다루는 방법이 무엇이 있었을까?

바로 배열이다. 그렇다면 아래와 같은 코드를 유추할 수 있다.

#include<stdio.h>

int main(){
    char str[] = {'H', 'a', 'p', 'p', 'y'};

    //printf("%s", str); 이게 안 됨.

    return 0;
}

근데 위의 배열을 %s 형식지정자로 참조하면 제대로 작동하지 않는 것을 확인할 수 있다.

사실 이유는 간단하다.

문자열로 다루려면, 저번에 scanf에서 다루었던 것처럼 0, ‘\0’, (char)NULL 널 문자를 끝에 붙여주면 되기 때문이다.

아래의 코드를 실행해서 한 번 확인해보자.

아래와 같이 초기화도 가능하다.


문자열 리터럴

사실 문자열을 다루는 것은 배열을 다루는 것과 같다는 것을 알았다.

즉 문자 배열로 만든 문자열은 아무리 전체를 다룰 수 있다고 해도 배열이 기본이라서 원소 단위의 접근이 필요하다.

그런데 만약, 원소 단위 참조가 전혀 필요하지 않고 전체를 다루고 싶다면?

그럴 때를 위해서, 사실 문자열을 다루는 관점은 다른 방법이 또 있다.

int age = 7;
double height = 181.2;
char grade = 'A'

여기서 오른쪽 대입되는 값을 우리는 리터럴 상수라고 배운 적이 있다.( 상수가 기억이 안 난다면?)

그런데 리터럴 상수에는 문자열 리터럴이라는 것이 있다. 즉 “Happy”라는 문자열을 리터럴로 써서 상수처럼 취급할 수 있다는 것이다.

이는 굉장히 파괴적인데, 리터럴의 특징은 메모리 구조 중 text 영역에 들어간다.( 메모리 구조란?)

이는 프로그램 실행 중 수정이 불가능한 정적인 공간에 저장이 된다는 뜻이다.

선언 및 초기화는 아래와 같이 한다.

또한, 아래와 같이 선언도 같은 의미이다.

const *char = "Happy";

리터럴의 주소를 받아서, 초기화를 해주면 된다.

배열처럼 원소별 참조는 가능하나, 수정할 수 없다는 차이가 있다.


문자 배열을 통한 문자열 vs 리터럴을 통한 문자열

문자 배열을 통한 문자열은 원소 단위 접근이 기본이기에, 배열에서 사용하는 성질을 모두 사용할 수 있다.

예를 들어 아래의 코드를 직접 실행해보자.

문제 없이 실행된다. 반면 아래의 코드는, 리터럴이기에 수정할 수 없다.


바로 과제로 넘기긴 그러니까, 예제 하나만 해봅시다.

문자열의 길이를 구하는 함수를 만들어보자.

이런 함수를 만들 때 주의할 점은, 함수에 들어가는 매개변수가 값이 바뀌느냐 아닌가이다.

매개변수의 값이 바뀔 일이 없고, 바뀌어서는 안 된다면 const를 이용하는 것을 생활화하자.

참조, 수정 모두 배열의 것과 거의 같다.

나머지는 과제로 넘긴다.


📖 오늘의 핵심(다 알기 전까지는 넘어가지 말자❗)

✅ 1. 문자 배열과 문자열의 차이를 안다.

✅ 2. 문자열을 리터럴로 사용하는 방법을 안다.

✅ 3. 라이브러리 함수를 이용해서 문자열을 다루는 법을 안다.(과제 참조)

⚠️ 문자 하나는 ‘ ‘, 문자열은 “ “를 써아한다.

⚠️ const를 적극적으로 활용하자.

⚠️ 2번 관점으로 문자열을 이용할 때는 절대 개별 원소를 수정할 수 없다.

💣 과제,

  1. 라이브러리 함수를 통해서도 문자열의 길이를 구해보자.(난이도 下)

  2. 문자열이 같은지 비교하는 함수를 만들어보자.(난이도 中)

  3. 2번을 라이브러리 함수를 통해 구하는 방법을 알아보자.(난이도 下)

  4. 문자열을 복사해보자.(난이도 中)

  5. 4번을 라이브러리 함수를 통해 구해보자.(난이도 下)

  6. 문자열 배열을 만들고 다룰 줄 안다.(난이도 上) (꼭 풀어볼 것)

🔜 더 공부해보기,

  1. 읽어볼 거리(1) - 아스키 코드와 숫자 변환(매우 중요)

  2. 읽어볼 거리(2) - 더 많은 라이브러리 함수(사실 위 함수들은 버퍼 오버플로우에 취약하여 사용하지 않는다.)

  3. 읽어볼 거리(3) - 버퍼 오버플로우 대응용 함수