문자열 기본 개념
- 문자들의 배열을 의미한다. 정확히는 널종단문자(\0)까지를 포함한 문자들의 배열.
- 문자열을 가리키는 변수는 포인터이고, 문자열의 첫 번째 주소를 가리킨다.
#include <stdio.h>
int main(void)
{
//string is actually char array
//name pointer has char array's first address
//so, name is just address
char *name = "EMMA";
printf("%s\n", name);
printf("EMMA's(char array) address: %p\n", name);
printf("EMMA's first char address: %p\n", &name[0]);
printf("EMMA's second char address: %p\n", &name[1]);
printf("EMMA's third char address: %p\n", &name[2]);
}
문자열 메모리 그림으로 표현
- string의 크기는 다른 변수처럼 정해져있지 않다.
- 따라서, string이 언제 끝나는지 알아야할 것이다.
- 이를 알려주기 위한 것이 null문자 (\0)이다. 즉,
- string의 변수명 → 문자열의 시작위치 (포인터)
- \\0 → 문자열의 끝위치
- 문자열의 크기: 글자수 + 1 byte
- null문자 (\0)는 이진수로 0000 0000을 의미함.
- 문자 ‘0’은 아스키 코드 값으로 48이다.
문자열 구현 방식
1. String Literal(문자열 상수)
char 포인터 = "문자열";
char *s = "EMMA";
- char 포인터로 선언하면 문자배열이고, 상수이다. 해당 문자열 상수 "EMMA"는 메모리의 읽기전용 영역에 저장된다.
- 따라서, s[0] = 'A'와 같이 수정이 불가(segmentation fault)하다.
- ※ 포인터는 수정가능 여부와 연관이 없다. 단지, 위와 같이 구현한 상수문자열은 메모리의 읽기전용 영역에 생성되고, 포인터 s는 읽기전용 영역을 가리키고 있으므로, 수정이 불가한 것이다. (페이지 테이블의 수정가능 bit를 보고 판단)
- 변수 s는 "EMMA"배열의 첫번째 주소를 저장하고 있는 포인터이다.
2. char array(문자 배열)
char 변수명[] = "문자열";
char s[] = "EMMA";
- char 배열로 선언하면 문자 배열이다. 해당 문자열 "EMMA"는 메모리의 읽기쓰기 영역에 저장된다.
- 따라서, s[0] = 'A'와 같이 수정이 가능하다.
- 변수 s는 "EMMA" 배열의 첫번째 주소를 저장하고 있는 포인터이다.
※ 즉, 위 두 방법은 문자열을 상수로 선언할 것인지, 문자 배열로 선언할 것인지 구현 방법의 차이인 것이고,
이에 따라, 메모리의 읽기전용 영역에 저장하는지, 메모리의 읽기쓰기 영역에 저장하는지의 차이가 발생하는 것이다.
※ s는 모두 포인터이다.
※ 두 방법 모두 근본적으로는 포인터이지만, 선언한 타입은 엄연히 다르기 때문에 타입을 잘 고려해야 한다.
아래와 같이 return type: void *인 malloc을 char[] 타입 t로 받는다면 type 오류가 발생한다.
문자열 접근
code example1
#include <stdio.h>
int main(void)
{
char *s = "EMMA";
printf("%p\n", s); //print: EMMA
// printf("%p\n", *s); //compile error
printf("%c", *s); // print: E
printf("%c", *(s+1)); // print: M
printf("%c", *(s+2)); // print: M
printf("%c\n", *(s+3)); // print: A
printf("%c\n", *s + 2); // print: E + 1 -> F
//use bracket
//if use bracket, compiler automatically convert to above case
printf("%c", s[0]); //print: E
printf("%c", s[1]); //print: M
printf("%c", s[2]); //print: M
printf("%c\n", s[3]); //print: A
}
code example2
/*
descrption:
An Example of two ways to represent a string(char array).
*/
#include <stdio.h>
int main(void)
{
char *s = "JONADAN";
char m[] = "EMMA";
printf("%s\n", s);
printf("%s\n", m);
printf("char array(string literal) print\n");
printf("%c%c%c%c\n", s[0], s[1], s[2], s[3]);
printf("%c%c%c%c\n", *s, *(s+1), *(s+2), *(s+3));
printf("char array print\n");
printf("%c%c%c%c\n", m[0], m[1], m[2], m[3]);
printf("%c%c%c%c\n", *m, *(m+1), *(m+2), *(m+3));
}
- 문자열을 저장하는 char 포인터 변수 s는 문자열의 첫번째 주소를 저장하고 있다.
- char m[]는 문자열 배열로 선언한 것이고, char *s 문자열 상수로 선언한 것이다.
- 즉, 근본적으로 m, s는 문자열 배열의 첫번째 주소값을 저장하고 있는 포인터이다.
※ 선언한 타입 자체는 다르기 때문에, 타입을 잘 맞춰줘야 한다. - 즉 두 방법 모두, *(s+i)로 원하는 문자에 접근할 수 있다.
- s[i]를 통해 원하는 문자에 접근하는 경우는 컴파일러가 자동으로 *(s+i) 형태로 변환해주고 있는 것이다.
문자열 비교
#include <stdio.h>
int main(void) {
char *s1 = "ABC"; // char array(String Literal)
char *s2 = "ABC"; // char array(String Literal)
char s3[] = "SS"; // char array
char s4[] = "SS"; // char array
// result: Same
// if each String Literal is same, each pointer is same by compiler's optimization.
printf("s1: %p, s2: %p\n", s1, s2);
if(s1 == s2) {
printf("s1 and s2 is Same\n");
} else {
printf("s1 and s2 is Difference\n");
}
// result: Difference
// each char array "SS" is stored at difference memory area,
// So each pointer is difference.
printf("s3: %p, s5: %p\n", s3, s4);
if(s3 == s4) {
printf("s3 and s4 is Same\n");
} else {
printf("s3 and s4 is Difference\n");
}
}
- 문자열을 표현하는 방법은 두 가지이다.
- 첫째, char *s = "EMMA"; // char array(String Literal)
- 둘째, char s[] = "EMMA"; // char array
- 문자열 상수의 경우, 읽기 전용 메모리 공간에 할당되고, 같은 문자열인 경우(EMMA, EMMA) 동일한 메모리 공간이다.
- char array의 경우, 읽기 쓰기 메모리 공간에 매번 할당된다. 즉, 서로 다른 메모리 공간이다.
문자열 수정
- 첫번째 방법인 String Literal은 읽기 전용 메모리 공간에 할당된다.
- 두번째 방법인 char array는 읽기-쓰기 메모리 공간에 할당된다.
- 따라서,
문자열 복사
#include <stdio.h>
#include <string.h> //for strlen
#include <stdlib.h> //for malloc
#include <ctype.h> //for toupper
int main(void)
{
char s[] = "emma";
//malloc: allocate memory
//param: want byte size
char *t = malloc(4 + 1);
// by for-loop
for(int i = 0, n = strlen(s); i <= n; i++) {
t[i] = s[i];
}
t[0] = toupper(t[0]);
printf("%s\n", s);
printf("%s\n", t);
char *t2 = malloc(4 + 1);
// by strcpy(new, old)
strcpy(t2, s);
t2[0] = 'A';
printf("%s\n", t2);
}
printf 형식 지정자
- %s: 문자열 전체를 출력 (널 종단문자 만날 때 까지)
- %c: 문자를 출력
- %p: 주소값