우선 \0은 아스키 코드로 0 즉 (int)0이고 \n은 아스키 코드로 10 즉 (int)10입니다.
의례 말하는 널, NULL은 \0을 뜻하고 \0은 문자열의 끝을 알려주는 문자입니다. 대부분의 함수가 문자열을 처리할때 \0를 참조합니다.
그에 반해 \n는 개행문자 줄바꿈문자라고도 부르며 쉽게 이해하자면 우리가 엔터를 누르면 입력되는 문자입니다.
\0와 \n를 이해했다면 이제 fgets, gets, scanf에서 \n과 \0이 어떻게 기능하는지 알 수 있습니다.
먼저 scanf는 엔터(\n)전까지 읽어들입니다. 그리고 자동으로 문자열 제일 끝에 \0을 붙여준다. (엔터 전!!까지 읽는다는게 매우 중요한 개념입니다.)
gets는 엔터(\n)를 저장합니다. 그런데 자동으로 엔터(\n)를 \0로 바꿉니다.
scanf 와 gets의 동작원리는 다르나 결과적으로 문자열 뒤에 엔터(\n)는 붙지 않고 \0은 붙는다고 이해하면 됩니다. (하지만 출력함수 puts, fputs와 함께 쓰게 되면 원리가 굉장히 중요하게 되니 끝까지 읽어주세요!!)
fgets는 특이합니다. 엔터(\n)까지 저장하고 \0도 붙여줍니다.
abcd 라고 치고 엔터!까지 친다고 가정해봅시다. ((중요))
scanf함수는 abcd까지 배열에 저장되고 버퍼에 \n가 남습니다. 그리고 abcd로 저장된 배열 뒤에 \0를 붙여줍니다.
gets 함수도 abcd\n(엔터) 로 배열에 저장되기에 버퍼에 \n가 남지 않습니다! 그런데 gets는 \n를 \0로 변환해줍니다. 그래서 결과적으로 배열에 abcd\0가 저장됩니다.
fgets는 abcd\n(엔터)로 배열에 저장되기에 버퍼에 \n가 남지 않습니다. 그리고 fgets가 제일 뒤에 \0를 붙여줍니다. 그렇기에 abcd\n\0가 배열에 저장됩니다!
버퍼에 대해서 간략하게 설명을 해보자면 내가 키보드로 치는 모든건 버퍼로 들어간다고 생각하시면 됩니다.
내가 'abcd엔터'를 치면 abcd\n가 버퍼로 들어갑니다.
그런데 scanf는 \n을 읽지 않습니다. 버퍼에 있는 \n를 외롭게 내버려두고 abcd까지만 홀랑 데려갑니다ㅠ. 이게 바로 scanf의 특이한 점입니다.
###실험을 위해 배열 ch1을 문자0(아스키코드 48) 로 초기화 한 상태임을 미리 말씀드립니다.
자세히 알아보기 위한 실험1 <gets>
fputs("실험 문자열 123을 입력해주세요 : ",stdout);
gets(ch1);
// 예상 123\n 읽은 후 123\0됨 버퍼에 남는건 없음
fputs("버퍼테스트를 위해 .를 입력해주세요 : ",stdout);
gets(buff1);
//한번더 입력을 받아서 버퍼에 뭐가 있는지 확인해본다.
결과 : 실제로 123\0로 배열에 저장되고 버퍼엔 \n이 아닌 .인 46이 저장됨
실험2 <scanf>
fputs("실험 문자열 123을 입력해주세요 : ",stdout);
scanf("%s", ch1);
//예상 scanf는 123까지 읽고 \n는 버퍼에 남겨둘 것이기에 buff에 \n가 자동저장
fputs("버퍼테스트를 위해 .를 입력해주세요 : ",stdout);
gets(buff1);
//한번더 입력을 받아서 버퍼에 뭐가 있는지 확인해본다.
scanf는 \n이전까지 저장한 후 \0을 붙여서 저장하고 fputs는 실행조차 되지 않았음 내가 입력할 수 없게 \n을 받고 바로 배열을 출력함
실험 3-1 <fgets에 설정한 배열 길이가 넉넉할때>
fputs("실험 문자열 123을 입력해주세요 : ",stdout);
fgets(ch1,sizeof(ch1),stdin);
// 예상 123\n까지 저장 후 \0 붙여줌 그러므로 123\n\0됨
fputs("버퍼테스트를 위해 .를 입력해주세요 : ",stdout);
gets(buff1);
//한번더 입력을 받아서 버퍼에 뭐가 있는지 확인해본다.
실제로123\n\0로 출력됨!!! (\n는 아스키 코드 10임)
실험 3-2 <fgets에 설정한 배열 길이가 문자열 길이랑 같을때>
실험 3-2의 1)버퍼에 있는 문자열 확인을 위해 gets 함수를 사용했을때
fputs("실험 문자열 123을 입력해주세요 : ",stdout);
fgets(ch1,3,stdin);
// 12까지 저장후 \0붙여서 12\0됨 남은 3\n는 버퍼로 감
fputs("버퍼테스트를 위해 .를 입력해주세요 : ",stdout);
gets(buff1);
//한번더 입력을 받아서 버퍼에 뭐가 있는지 확인해본다.
for(int i=0;i<6;i++)
printf("%d, ", ch1[i]);
printf("\nbuff1에 남은 문자 : %d, %d",buff1[0],buff1[1]);
buff1을 위한 fputs에서 사용자의 입력을 받지 않고 바로 넘어갑니다. 그리고 buff1에서 gets를 사용했기에 버퍼에는 3\n이 남았을테지만 gets함수가 \n를 \0로 바꾸어서 배열에는 3\0로 저장됨 더 정확한 확인을 위해 fgets로 다시 실험해보았습니다.
실험 3-2의 2)버퍼에 있는 문자열 확인을 위해 fgets 함수를 사용했을때
fputs("실험 문자열 123을 입력해주세요 : ",stdout);
fgets(ch1,3,stdin);
// 12까지 저장후 \0붙여서 12\0됨 남은 3\n는 버퍼로 감
fputs("버퍼테스트를 위해 .를 입력해주세요 : ",stdout);
fgets(buff1,sizeof(buff1),stdin);
//한번더 입력을 받아서 버퍼에 뭐가 있는지 확인해본다.
fgets를 사용하니 3\n\0형태로 저장하는 것을 볼 수 있습니다. 엔터까지 저장한 후 \0를 붙여주는 것
실험 3-3 <fgets에 설정한 배열 길이가 문자열보다 +1클때>
fputs("실험 문자열 123을 입력해주세요 : ",stdout);
fgets(ch1,4,stdin);//123까지 저장후 \0붙여서 123\0됨
fputs("버퍼테스트를 위해 .를 입력해주세요 : ",stdout); //버퍼에는 \n가 남음
fgets(buff1,sizeof(buff1),stdin);
//한번더 입력을 받아서 버퍼에 뭐가 있는지 확인해본다.
ch1배열에는 예상대로 123\0이 되고 버퍼에는 \n이 남아서 사용자의 입력을 받지 않고 buff1에는 \n\0가 저장됩니다
fgets함수를 사용하지 않고 gets함수를 사용해서 버퍼를 확인하면 \n을 \0로 바꾸어서 0, 48, 48... 이 됨
실험 3-4 <fgets에 설정한 배열 길이가 문자열보다 +2클때>
fputs("실험 문자열 123을 입력해주세요 : ",stdout);
fgets(ch1,5,stdin) ;// 123\n까지 저장후 \0붙여서 123\n\0됨
fputs("버퍼테스트를 위해 .를 입력해주세요 : ",stdout);
fgets(buff1,sizeof(buff1),stdin);
// 한번더 입력을 받아서 버퍼에 뭐가 있는지 확인해본다.
버퍼 입력을 fgets로 받았습니다. ch1배열에는 123\n가 온전히 저장된 후 \0이 붙고 buff1에도 .\n이 저장된 후 \0이 붙어서 .\n\0이 됩니다.
실험끝!
'개발 공부 > C언어' 카테고리의 다른 글
[실험] #22 C언어 atoi함수를 &이랑 같이 쓰는 법 (0) | 2022.08.07 |
---|---|
#21 C언어 그냥 써보는 잡담... 변수이름에 comp라는 단어가 들어가는데 이건 무슨 뜻일까 (0) | 2022.08.07 |
[실험] #19 C언어 strncpy, strncat와 \0, \n 대체 어디부터 어디까지 복사하는거야! (0) | 2022.08.07 |
#18 [열혈 C 프로그래밍] 도전! 프로그래밍3 - 1번, 2번, 3번, 4번, 5번, 6번 코드와 해설 (0) | 2022.08.04 |
#17 [열혈 C 프로그래밍] 도전! 프로그래밍2 - 1번, 2번, 3번, 4번 (0) | 2022.08.04 |
댓글