잡지식

scanf_s와 동적할당

문정훈 2021. 11. 2. 01:53

1. scanf_s의 사용법

1) scanf_s는 크기를 지정한 byte를 저장시킨다. 

scanf_s("%s", s1, 10);

위 방식과 같이 scanf와 동일하지만 3번 째 매개변수가 있다. 이것은 기존의 scanf의 단점을 보완한 것으로 제한된 크기를 리턴하는 것이 scanf_s이다. 3번째 매개변수는 byte단위의 크기 값인데 10byte의 크기를 무조건 반환한다. 

사용자가 char s1[100]의 값 s1에 문자열을 저장할건데

 

※ 9자리의 문자열 + '\0'를 반환한다. 

 

경우1)

10보다 작은 5byte 문자열을 입력하면 5byte+4개의 null+'\0' 이렇게 총 10자리를 반환한다. 

 

경우2)

9보다 큰 즉 10자리 이상을 입력하면 그 입력은 무시된다. 

 

2) scanf_s는 %s와 %s가 아닌 경우에서 사용 법이 나뉜다.

int main() {
  char s1[10];
  scanf_s("%s", s1, sizeof(10));
  
  int a;
  scanf_s("%d", &a);
}

%s로 사용하는 경우 세 번째 인자의 값이 꼭 있어야한다. 

%s가 아닌 경우는 하나의 단위(int 1개, char 1개 등등)를 입력 받는 경우로 세 번째 인자의 값을 적어도되고 안적어도된다. 

 

 

2. char* s와 char a[10]의 차이점

char* s1 = NULL;
scanf_s("%s", s1, sizeof(10));
/*error*/

s1은 4byte 메모리 공간이다. 이 공간에는 char형의 1byte 문자열의 메모리 공간의 주소를 담을 수 있다.  

char *s1 = "Hello";

위와 같이 배열을 선언해도 된다.

이것은 "Hello"라는 6byte("Hello + '\0')문자열이 메모리 공간에 생기는데 s1은 그 메모리 공간의 처음 주소를 가진다.  

위 예시코드가 실제로 동작안되는 이유는 우선 scanf_s는 사용자가 몇 자리를 입력하던 9byte 이하이기만하면 9byte를 반환하는데, s1은 4byte 메모리 공간인데 이 공간에 문자열을 넣을 수 없다.

아래 코드와 같이 바꾸거나 char[] 공간을 동적할당하여 그 공간을 s1이 가리키도록 한 뒤 그 공간에 사용자로부터 scanf를 하면 된다.  

 

char s1[10];
scanf_s("%s", s1, sizeof(s1));
/*correct*/

이렇게 코드를 수정해야한다. 

 

 

3. 동적 할당

int main() {
    int size;
    printf("크기 입력 >>");
    scanf_s("%d", &size);

    char* s1 = (char*)malloc(sizeof(char) * size);

    printf("%d 자리 문자열 입력>>", size-1);
    scanf_s("%s", s1, sizeof(char) * size);

    printf("%s", s1);
    free(s1);
}
int main() {
    char* s1 = malloc(sizeof(char) * 10);    // char 10개 크기만큼 동적 메모리 할당

    printf("문자열을 입력하세요: ");
    scanf_s("%s", s1, sizeof(char) * 10);      // 표준 입력을 받아서 메모리가 할당된 문자열 포인터에 저장

    printf("%s\n", s1);   // 문자열의 내용을 출력

    free(s1);    // 동적 메모리 해제

}

두 번째 코드를 분석하면

s1은 동적할당 전에 char 1byte 크기의 메모리 공간 주소를 가질 수 있는 변수이다. (그 이상도 그 이하도 아님)

동적할당을 통해 sizeof(char)*10=10byte 크기를 할당받았다. 

따라서 동적할당의 결과로 s1은 10byte의 char 공간을 가졌고 첫 주소를 s1이 가지고 있는 것이 된다. 

 

s1은 힙 영역에 메모리 공간으로 10byte를 가지므로 scanf_s가 가능해진다. 

s1에도 역시 9byte 문자열이 들어가고 끝 1byte는 '\0'이다. 

 

 

4. 의문

int main() {
    char* s1 = (char*)malloc(sizeof(char) * 10);  
    printf("%d\n", sizeof(s1)); // 4
    printf("%d\n", strlen(s1)); // 14
    
    char* s2 = NULL;
    printf("%d\n", sizeof(s2)); // 4
    
    char* s3 = "abcde";
    printf("%d\n", sizeof(s3)); // 4
    printf("%d\n", strlen(s3)); // 5
    
    char s4[5] = "abcd";
    printf("%s\n", s4); //abcd
    printf("%d\n", sizeof(s4)); //5
    printf("%d\n", strlen(s4)); //4
}

파란 글은 모르는걸 해결한 글임.

빨간 글은 왜그런지 모르는 거임

 

 

● s4와 같이 배열로 문자열을 지정할때 크기는 5이지만 실제로 4byte만 작성되야함 끝에 1byte는 '\0'이 들어감

 

● char* 타입으로 지정된 변수를 sizeof하면 값이 있던 말던 무조건 4가나옴 => 왜???

해결=> s1에는 char 타입 하나를 저장할 수 있는 공간의 "메모리 주소"를 가지는데 메모리 주소는 32bit 시스템에서 4byte이기 때문임

    

● strlen(s1)은 왜 10이 아니라 14가 나오지? 왜??

해결=> strlen은 문자열의 길이를 구하는 함수이고 null(''\0")은 포함안함. 

sizeof는 메모리의 크기를 바이트 단위로 계산한다. 

s1이 동적할당은 받았지만 문자열을 가지지 않으므로 애초에 strlen을 사용하면 안됨!

s1에 동적할당을 받았으니 문자열을 입력 한 뒤 strlen을 하면 끝에 null이 들어가긴 하지만 null 빼고 길이를 정상적으로 읽음 아래는 문제점 해결 코드임

//solution코드
int main() {
    char* s1 = (char*)malloc(sizeof(char) * 10);  
    s1 = "abcdefg";
 
    printf("%d\n", strlen(s1)); // 7
    printf("%d\n", sizeof(s1)); // 4
    
    char* s2= NULL;  
    s2 = "abcd";
 
    printf("%d\n", strlen(s2)); // 7
}

 

● s1은 동적 할당으로 10byte의 크기를 할당 받았다=> 저장 가능한 문자열은 9byte이다. 

 

● 실제로 s1은 10byte를 할당 받아 9byte가 들어갈 수 있지만 scanf_s의 3번째 매개변수를 충분히 크게주어 10byte이상을 입력하면 s1에 10byte 이상의 크기 모두가 다 들어감. 

아니 동적할당으로 10byte만 할당받았는데 왜 10byte 이상이 더 들어감? 알아서 크기 늘어나나?? 

int main() {
  char* s1 = (char*) malloc(10);
  s1 = "abcde";
  
  printf("%d\n", strlen(s1)) // 5
  
  
  char* s1 = (char*) malloc(3);
  s1 = "abcde";
  
  printf("%d\n", strlen(s1)) // 5
  
  
}

char *s1으로 동적할당으로 heap영역에 10 byte 길이의 메모리 주소를 할당 받았지만 이후 s1 = "abcde"이렇게 선언한 것은 기존에 s1이 가지고 있던 heap영역에 동적 메모리 주소를 버리고 문자열이 저장된 메모리공간의 시작 주소를 가지는 것임 .  그래서 strlen은 문자열의 길이를 나타내므로 위와 같은 결과가 나온는 것임.

  

 

 

'잡지식' 카테고리의 다른 글

c언어 배열 끝에 '\0'  (0) 2021.12.12