C언어에서 문자열을 입력받을 때는 아래 코드와 같이 문자열의 길이가 선언되어야 하며
scanf
을 통해 저장할 변수에&
를 붙일 필요가 없습니다.
숫자를 입력 받는 경우는&
를 통해 변수의 주소에 접근해서 값을 넣어주기 위한 것이고, 문자열은 그 자체가 배열의 주소값이기 때문에&
를 붙일 필요가 없습니다.
#include <stdio.h>
int main ()
{
char a[9];
scanf("%s", a);
printf("%s", a);
return 0;
}
C언어에서
scanf("%s", str);
를 사용하면"Hello, World!"
와 같은 공백이후의 문자가 들어올 경우 해당 문자열 전체를 입력받을 수 없습니다.
scanf
함수는 버퍼에 문자열을 가져와 배열에 저장하는데 중간에 공백 문자, 탭문자, 개행문자가 있으면 그 이전까지만 저장하기 때문입니다.
fgets(str, 31, stdin);
을 사용하면 공백 포함 30자리 이내의 문자열을 입력 받을 수 있게 된다.
#include <stdio.h>
int main(){
char a[31]; /* 문자(배)열의 크기는 저장될 문자열 +1로 정의한다.
(C에서 문자열의 끝은 항상 널로 끝나야 하기 때문
- 문자열의 특성을 유지하려면 '\0'으로 끝나야 함!)
*/
fgets(a,31,stdin);
printf("%s",a);
return 0;
}
https://www.onlinegdb.com/online_c_compiler 에서
아래 코드와 같이 길이 3의 문자열 3개를 선언한 뒤 3개의 문자를 각각 인풋으로 넣고 출력을 하면NULL
을 고려하지 않은 결과, 의도치 않은 결과를 출력하게 된다
#include <stdio.h>
int main()
{
char arr1[3], arr2[3], arr3[3];
scanf("%s %s %s",arr1, arr2, arr3);
printf("%s %s %s\n",arr1, arr2, arr3);
printf("%x %x %x", &arr1[0], &arr2[0], &arr3[0]);
return 0;
}
입력 > 123 456 789
출력 > 123456789 456789 789
d171d1cf d171d1d2 d171d1d5
주소값을 확인해 보면
arr1, arr2, arr3
이 각 3byte씩(각 배열의 크기) 차이를 두고 있다. 즉,arr1
배열의 시작 주소부터 차례대로 '123'을 집어 넣었고 바로 다음arr2
배열에 '456', 마지막arr3
배열에 '789'를 집어 넣은 것이다.
그 결과 당연히printf
함수는\0, 즉 NULL
값이 나올 때까지를%s
로 출력하기 때문에arr1
을 출력할 경우arr2, arr3
까지의 값이 나온 것이다.
이는 main()함수 내 지역변수인
arr1, arr2, arr3
가 스택(stack) 영역에 할당 되며,Stack 영역은 메모리의 가장 큰 주소값부터 거꾸로 할당 된다(32비트 시스템의 경우만 해당, 64비트 시스템의 경우 지역변수 할당 시 할당 순서대로 메모리 주소값도 커짐). arr1이 먼저 정의되어 맨 아래 쌓이고, 그 다음 arr2, 다음 arr3로 할당되었기 때문이다.
만약 아래의 코드와 같이,
arr1, arr2
순서로 선언하여 해당 변수의 메모리 주소가 차례대로 할당(arr1의 주소 = 0x7ffde44546e1, arr2의 주소 = 0x7ffde44546e4
)되지만,scanf
를 통해 입력 데이터를 받을 때 buffer overflow로 인해 제대로 변수에 값이 할당 되지 않는 것을 볼 수 있다.
#include <stdio.h>
int main()
{
char arr1[3], arr2[3];
scanf("%s %s",arr2, arr1);
printf("arr1: %s and arr2: %s\n",arr1, arr2);
printf("%p %p", &arr1[0], &arr2[0]);
return 0;
}
input : 456 123
output : arr1: 123 and arr2:
0x7ffde44546e1 0x7ffde44546e4