본문 바로가기
프로그래밍/C언어

10. c언어 문자열, 문자열입력, 문자열배열, 리터럴

by 수삼이는코딩중 2023. 3. 20.
728x90

문자열

scanf()와 문자열입력

컴퓨터는 문자를 문자로 저장하지 않고, 숫자로 저장한다. 다만 우리가 읽을 때 문자로 읽느냐, 숫자로 읽느냐의 차이. 

아스키코드의 값을 따라 문자로 저장하든, 숫자로 저장하든, 포맷에 따라 읽어준다. 

즉 char형으로 문자를 저장해도 컴퓨터는 숫자로 저장하고, 우리가 읽고자하는 포맷 %d or %c에 따라 읽어준다. 

#include <stdio.h>

int main() {
  char a;
  scanf("%c",&a);
  printf("%c,%d",a,a);
}

결과

d,100

하지만 모든나라의 문자를 숫자로 변환하면 아스키 코드 안에 다 담을 수 없다. 그래서 생긴 것이 유니코드. utf-8따위.

아래 내용과는 별 상관이 없다


 

문자열 입력 포맷

char str[];
scanf(“%s”,str);
 str[]=“”

위와 같이 선언한 문자열은 배열에 한 문자씩 저장된다.
마지막 문자는 종료문자 null. “”(큰따옴표) 안에 문자를 작성하면 자동으로 마지막에 null처리 문자를 입력한다. 따라서 문자의 총 길이는 문자의 개수+1이 된다.
문자열의 이름은 문자열이 저장된 메모리의 시작주소값을 가리키는 포인터(배열에 저장되니까 배열이랑 비슷하다)
-> 포인터에 문자열의 주소를 저장할 때 문자열의 이름으로 저장 가능(배열처럼~)
str[i]로 문자열의 문자를 읽을 수 있고 수정할 수 있다.
아래 설명하듯이 printf함수는 null을 만날 때까지, scanf함수는 공백문자를 만날 때 까지(%c제외) 문자열을 읽는다.

입력 : scanf()함수는 사용자가 입력한 문자를 버퍼인 stdin에 일시적으로 저장하고 입력이 종료되면 한꺼번에 처리한다. (입력의 종료는 엔터. 즉 입력을 종료하였으니 버퍼에 들어있는 문자를 가지고 놀아라는 뜻.)
%d는 숫자가 아닌 문자가 와도 입력받는 것을 종료한다. 공백문자를 입력하면 수가 나타날 때까지 입력을 계속 받는다. (아무리 엔터를 쳐도 숫자를 입력받기 전까지 계속 입력을 받는다.)

읽기 : 이제 문자 읽기를 시작한다. scanf함수는 stdin에 있는 문자열을 읽어오는데 공백문자(띄어쓰기, 엔터, 탭)를 만날 때 까지 읽는다.
%c : 문자열을 읽어올 때 공백문자도 읽고, 한문자만 반환한다. 입력없이 공백만 stdin에서 읽어오는 경우가 많으므로,, 여러모로 쓰지 않는 게 좋다
%s, %d : 문자열을 읽어올 때 공백문자를 뛰어 넘어 버리고 의미있는 문자부터 공백문자가 나올 때 까지 읽는다. (위에 먼저 나온 scanf 절에서 엔터를 쳐도 엔터부터 읽지 않는다)
단 scanf가 연속되어 나올 때, 띄어쓰기를 하면, $d나 %s는 띄어쓰기 까지 읽어버리고, 그 다음 scanf가 입력받지 않고 띄어쓰기 뒤의 나머지 문자를 읽는다. 즉, 입력은 제대로 받으나 출력에 오류가 생기는 것.

printf()와의 차이

* printf()함수에서 문자열을 읽는 %s는 Null이 나올 때 까지 문자를 계속 출력한다.  
Null은 ‘\0’,0,(char)NULL이 있다

 

char s[];
printf(“s : %s \n”, s);

 

에서 s는 배열의 시작점을 가리키는 포인터(실제 포인터는 아닌,)이다.
위 코드는 s배열의 시작점부터 들어있는 문자열을 출력해달라 는 의미.
(“”는 문장, ‘’는 한개의 단어를 사용할 때 쓰는 따옴표)

리터럴(literal)

리터럴이란, 소스코드 상에서 고정된 값을 가지는 것. 특히 C언어의 경우 큰 따옴표로 묶인 것들을 문자열 리터럴 이라고 부른다 컴퓨터는 리터럴을 따로 모아서 보관한다

 

프로그램이 실행되어서 메모리에 로드되면 5가지 종류의 영역(text segment, data segment, bss segment, heap, stack)이 존재한다. 이 때 text segment에서 프로그램 코드와 상수, 리터럴이 정의 된다. text segment에 있는 내용들은 읽기만 가능하기 때문이다

char *pstr = “goodbye”;

 

는 “goodbye”의 시작주소값을 가져와서 pstr에 대입해라 는 의미의 작업을 실행한다.
정말 지멋대로인것 같지만 “goodbye”라는 문장에는 “goodbye”라는 문자열이 저장된 시작 주소값이 저장되어있다…….(복잡 그자체…배열이름도 주소값, 문자열이름도 주소값,,)
따라서 pstr은 “goodbye”라는 리터럴을 가리킨다.

#include <stdio.h>

int main() {
  char *pstr="good bye";
  printf("%s,%p",pstr,pstr);
}

결과

good bye,0x402004

 

을 했을 때 "goodbye"를 출력한다. (c언어 개발자들이 문자열에서, %s를 이용해 그 문자열을 가리키는 포인터 또는 문자열의 이름을 읽으라고 명령하면, 문자열 시작주소값에 들어있는 문자부터 null이 나올 때까지 읽도록 만들어놨다. 물론%p로 읽으라고 명령하면 주소가 나온다)

#include <stdio.h>

int main() {
  char str[]="sentence",*pstr;
  pstr=str;
  printf("%p\n%p\n%s",str,pstr,str);
  return 0;
}

결과

0x7ffdc03d4d50
0x7ffdc03d4d50
sentence


그런데 여기서 pstr을 가지고 위의 예시인 “goodbye”를 수정할 수는 없다. 왜냐하면 ”goodbye“는 text segment에 저장되어 있기 때문이다. 단, 문자열의 배열로 정의한

char str[]=“sentence”;

 

에서는 변경할 수 있다. 이건 str이라는 배열에 hello라는 문자열을 복사하게 될 뿐이기 때문이다. 그리고 이 배열은 세그먼트가 아니라 스택이라는 메모리 수정이 가능한 영역에 정의가 된다. 따라서 str안의 문자열은 수정이 가능하다. 

text segment에 저장된 문자열에는 대입도 못한다. 포인터가 포인터 상수이기 때문에.. 포인터가 가리키는 변수를 변경할 수 없는 포인터 상수..(뭐 그렇단다..)

 

문자열 다루기

문자열을 다루기 위해서는 함수가 필요하다

  • 문자열 내의 총 문자의 수를 세는 함수
  • 문자열을 복사하는 함수
  • 문자열을 합치는 함수
  • 문자열을 비교하는 함수


함수를 만들 때 주의할 것

  1. 이 함수는 무슨 작업을 하는가?
  2. 함수의 리턴형이 무엇이면 좋을까?
  3. 함수의 인자로는 무엇을 받아야 하는가?


는 다음 이시간에..

댓글