프롤로그
ELITE HACKER Bootcamp 3rd 4주차 수업 공부 내용
aws 우분투 서버 하나 파서 연습
문자열의 개념, 용도, 선언과 초기화, 배열, 포인터
문자열
2024.11.14 - [KnockOn] - [KnockOn] Linux/Ubuntu C언어 입출력함수
여기서 한 번 언급을 했었는데, 왜 입출력함수에 언급을 했었는지 모르겠네 ㅋㅋ
아무튼 다시 작성해보자면
생각을 해보자. 기존에 자료형에도 char 은 단어 한 글자만 입력받을 수 있다. 즉 'C', 'A' 이런식으로 받아야하고, 심지어 저거 입력받을 때도 "A" 이렇게 입력 자체가 안 된다.
그럼 Hello 이런 건 어떻게 입력받야아하는가?
char a = 'h'; char b = 'e' 이런식으로 일일이 해야하는가....?
이럴 때 사용하는 게 배열 또는 포인터이다.
문자열 개념부터 알아보자면, 문자열은 문자들이 연속적으로 나열된 데이터의 집합이다. 연속으로 나열되었을 때, 문자열의 마지막 부분이라는 것을 알려주기 위해 항상 마지막에는 \0을 적어줘야하한다. 저거 없으면 문자열 끝이라는 것을 알 수가 없다.
다시 hello 를 문자열로 한다면 "hello\0" 이렇게 6글자를 저장한다는 것이다. (\0은 한 글자)
문자열 선언 및 초기화
크게 두 가지가 있다. 하나는 char 배열을 이용하는 방법이고, 나머지 하나는 포인터를 이용하는 방법이다. 익둘이 메모리 를 사용하는 방법이 조금 다르기 때문에 어떻게 설명하고 이해해야하나 고민이다.
(1) 배열을 이용하는 방법
char str[6] = "hello";
char str[] = "hello";
char str[6] = {'h', 'e', 'l', 'l', 'o', '\0'}
char str[6];
str[0] = 'h';
str[1] = 'e';
str[2] = '\0';
배열 선언방법도 여러 가지만 있지만 위와 같이 이용할 수 있다. 예시 코드를 보자면
#include <stdio.h>
int main(){
char str[6] = "hello";
for(int i=0;i<6;i++){
printf("%c", str[i]);
}
printf("\n");
}
이렇게 사용할 수 있다.
따로따로 넣거나, 아니면 크기만 할당 받은 경우에는 문자열의 끝을 알려주는 \0을 잊으면 안 된다.
배열롤 선언된 문자열의 경우에는 중간의 문자열을 수정할 수 있다.
#include <stdio.h>
int main(){
char str[6] = "hello";
for(int i=0;i<6;i++){
printf("%c", str[i]);
}
printf("\n");
str[0] = 'W';
str[1] = 'o';
str[2] = 'r';
str[4] = 'd';
for(int i=0;i<6;i++){
printf("%c", str[i]);
}
}
이렇게 수정이 되어서 World가 밑에줄에 출력된 것을 확인 할 수 있다. 각 인덱스별로 접근하였다.
(2) 포인터를 이용하는 방법
다음은 포인터로 문자열을 입력해보자.
char* str = "hello";
포인터로 문자열을 넘겨준 느낌이다.
#include <stdio.h>
int main(){
char* str = "hello";
for(int i=0;i<6;i++){
printf("%c", *(str+i));
}
printf("\n");
}
이렇게 사용할 수 있다. 이건 배열의 크기를 생각 하지 않고 그냥 바로바로 문자열을 넘겨서 사용할 수 있다.
*(str+i)를 안해주어도 str[i] 로 충분히 printf 를 진행할 수 있다.
다만 둘은 큰 차이점이 있다. 포인터로 선언한 것은 수정을 진행할 수가 없다. 한글자 한글자를 변경해줄 수 없다..
#include <stdio.h>
int main(){
char* str = "hello";
str[2] = "b";
for(int i=0;i<6;i++){
printf("%c", str[i]);
}
printf("\n");
}
자 안 되는 것을 알 수 있다. 반면에, 아까 배열로 선언한 것은 변경이 잘 되었다. 위에서.
배열과 포인터의 차이점
문자열을 사용할 때 둘의 메모리구조와 동작 원리를 알아보자.
일단 문자열이 입력이 되거나 저장이 되면
"hello" 이런 거는 데이터 영역에 저장이 된다. 그 다음, 배열이 선언이 된다면, 해당 배열은 스택 메모리에 생성이 되는데, 저 문자열들을 각 배열에 값을 복사해서 사용된다. 포인터 같은 경우는 저 hello가 저장된 데이터 영역에 바로 주소를 가리키게 된다. 데이터 영역에 저장된 hello같은 경우에는 읽기 전용 공간이기 때문에 중간에있는 값을 하나하나 변경해줄 수가 없는 것이고, 스택메모리에 생성된 배열로 값이 복사된 데이터는 하나하나 값을 변경해줄 수 있는 것이다. 왜냐면 데이터 영역의 문자열에 영향을 미치지 않기 때문.
요약하자면
1. 데이터 영역
- 모든 문자열 리터럴은 프로그램 실행 시 데이터 영역에 저장.
- 이 영역은 읽기 전용이며, 같은 문자열 리터럴은 프로그램 전체에서 중복 없이 재사용.
2. 배열 사용 (char str[])
- 데이터를 배열로 복사하므로 데이터 영역의 리터럴과 완전히 독립적.
- 복사된 데이터는 스택에 저장되므로 수정이 가능.
3. 포인터 사용 (char* str)
- 데이터 영역의 문자열 리터럴을 직접 가리키므로 복사 과정이 없음.
- 문자열 수정은 불가능하지만 포인터는 다른 주소를 가리킬 수 있음.
도전
(1) 두 단어를 입렫받고 두 단어를 합친 결과값 출력하기
#include <stdio.h>
int main(){
char str1[999];
char str2[999];
printf("Input first word : ");
scanf("%s", str1);
printf("Input second world : ");
scanf("%s", str2);
int len1=0, len2 = 0;
int flag1=0, flag2 = 0;
int index = 0;
while(1){
if(!flag1){
if(str1[index] == '\0'){
flag1 = 1;
}
else{
len1++;
}
}
if(!flag2){
if(str2[index] == '\0'){
flag2 = 1;
}
else{
len2++;
}
}
if(flag1 && flag2){
break;
}
index ++;
}
const int total_len = len1+len2+1;
char answer[total_len];
int i=0;
for (i;i<len1;i++){
answer[i] = str1[i];
}
for (int j=0;j<len2;j++){
answer[i] = str2[j];
i++;
}
answer[i] = '\0';
printf("%s\n", answer);
}
(2) 단어를 입력받고, 다른 배열에 단어를 복사한 후에 복사본 출력하기
#include <stdio.h>
int main(){
char str1[999];
printf("Input word : ");
scanf("%s", str1);
int len = 0;
int index = 0;
while(1){
if(str1[index] == '\0'){
break;
}
else{
index++;
len++;
}
}
const int totel_len = len+1;
char str2[totel_len];
index = 0;
for (int i=0;i<len;i++){
str2[i] = str1[i];
index++;
}
str2[index] = '\0';
printf("%s\n", str2);
}
(3) 두 단어를 입력받고, 비교결과 출력하기
#include <stdio.h>
int main(){
char str1[999];
char str2[999];
printf("Input first word : ");
scanf("%s", str1);
printf("Input second word : ");
scanf("%s", str2);
int flag =1;
int index = 0;
while(1){
if (str1[index] == '\0'){
if(str2[index] == '\0'){
break;
}
else{
flag = 0;
break;
}
}
if (str2[index] == '\0'){
if(str1[index] == '\0'){
break;
}
else{
flag = 0;
break;
}
}
if(str1[index] != str2[index]){
flag = 0;
break;
}
else{
index++;
}
}
if(flag){
printf("Yes...\n");
}
else{
printf("Nope...\n");
}
}
에필로그
엄연히 말하면 위 코드들 다 잘못됐다고 볼 수 있다.
왜냐?
char a[b] ; 이런 거 사실 안 된다고 하지 않았는가;;;근데 왜 저렇게 했는가?
C99 이상부터 일부 컴파일러에서는 지원한다. 리눅스 22.04 LTS에서는 지원을 해서 일단 저렇게 한거니까.
나중에 저거는 어떻게 해결해보기로 하고;;;
그리고 저거 문자열 복사, 비교, 합치기 함수들인데, 너무 귀찮지 않은가?
string.h에 대해서 다음에 알아보도로 하자.
'KnockOn' 카테고리의 다른 글
[KnockOn] Linux/Ubuntu C언어 구조체 - 1 (0) | 2024.11.25 |
---|---|
[KnockOn] Linux/Ubuntu C언어 <string.h> (0) | 2024.11.24 |
[KnockOn] Linux/Ubuntu C언어 포인터 (0) | 2024.11.21 |
[KnockOn] Linux/Ubuntu C언어 함수 -2 (0) | 2024.11.20 |
[KnockOn] Linux/Ubuntu C언어 함수 -1 (2) | 2024.11.19 |