c언어에서 printf함수를 배웠다면 다음은 scanf 함수이다. 여기서 c언어와 다르게 변수 앞에 문자 &를 추가로 붙이는데 그 이유는 무엇일까?
1. printf 함수의 경우
우선 변수에 대해 간단히 이해하고 넘어가 보자
int a;
a = 10;
이러한 코드가 있다고 생각해 보자. 정수 변수 a를 선언하고, a에 10을 대입한다.
a에 직접 대입 연산자 =와 대입할 정수를 입력해 주면 된다.
그러면 a라는 이름으로 명명된 정수 크기 메모리 공간에 10이라는 정수가 들어간다.
printf("%d", a);
우리는 printf문을 통해 a를 불러 'a라는 변수에(a라는 이름으로 명명된 특정 주소 정수 크기 메모리 공간에) 들어있는 값'을 이용하게 된다.
2. scanf의 경우
scanf("%d", &a);
사용자에게서 값을 입력받아 변수 a에 값을 저장하는 scanf 함수의 경우, 함수의 인자로 a를 넘기지 않고 &를 붙이게 된다. 여기서 앞에 붙는 &기호는 주소를 의미하는데, 왜 변수의 이름 대신, 변수 주소를 넘기게 될까?
결론부터 말하자면 scanf함수에서 a라는 변수에 값을 집어넣으려는데, a라는 변수의 주소가 필요하기 때문이다.
printf문에서는 'a의 값'을 원했기 때문에 그냥 a를 써서 값을 이용했지만, 지금은 값이 아니라 저장할 주소가 필요하기 때문이다.
여기까지가 내가 c언어 처음 배울 때 아~ 그렇구나 하고 넘어간 내용이다. 하지만 갑자기 궁금증이 생겼다.
3. 변수 a에 값을 대입할 때는 주소가 필요하지 않나?
우리는 앞선 예로 a에 값 10을 대입할 때 a = 10;이라는 문장을 통해 "a변수의 값을 10으로 해 주세요" 라고 요청했다.
이는 바꾸어 말하면 "a라는 이름이 붙은 변수의 주소로 가서(크기는 정수) 거기에 있던 값을 10으로 덮어씌워 주세요" 라는 뜻이다.
아니 a라는 변수의 주소가 필요한데 여기서는 왜 &를 쓰지 않는거야? chatGPT를 통해 알아보았다.
정리하자면 scanf 함수는 포인터를 사용하는데, 값을 단순 대입할 때는 포인터가 필요하지 않다는 것이다. 둘 다 주소가 필요한데 대입할 때는 왜 포인터가 필요하지 않는거야? 다시 물어보자.
변수를 선언하여 컴파일러가 이미 변수와 관련된 메모리 위치를 알고 있으므로 값을 변수에 직접 할당할 때 포인터를 따로 전달할 필요가 없다고 한다. scanf함수에서도 이미 선언한 변수를 사용하는데, 이건 왜 포인터가 필요하다는거야?
컴파일러가 scanf도 컴파일 하는데 왜 scanf랑 별개인 것처럼 하고 scanf는 메모리 위치를 모를까?
컴파일러가 선언하면 가지고 있는 변수에 대한 정보를 scanf는 이용하지 못한다는 말이 되시겠다. 왜 그럴까?
이제 이해했다. 값을 대입하는 것은 우리가 컴파일러에게 요청한다고 볼 수 있고, 함수를 이용하는 것은 함수에게 요청하는 것이다. 함수는 컴파일러와 동일한 정보를 모두 가지고 있지 않으며, 따라서 직접 변수를 선언한 컴파일러와는 다르게 a라는 변수를 넘겨준다고 a의 주소를 알지 못한다는 것!
하지만 궁금한 점이 남았다. 함수에게 요청하는 것은 왜 컴파일러에게 요청하는 것과 다르지?
라이브러리 함수를 포함한 함수의 컴파일의 경우 기본 프로그램 코드와 별도로 컴파일된다.
성능, 코드 재사용, 최적화, 모듈성의 측면에서 여러 이득이 있으므로 함수를 기본 프로그램과는 별개로 컴파일한다.
결론
scanf함수 인자에 &를 붙이는 이유는 scanf함수는 변수의 이름을 넘겨줬을 때, 해당 변수의 주소값을 모르기 때문이다. 변수의 주소의 의미인 &를 변수명 앞에 붙여줘야 한다.
반면 변수 a에 직접 값을 대입할 때(예시 : a = 10;) 주소값이 필요 없는 이유는, 직접 값을 대입할 때는 컴파일러가 a를 선언한 뒤로, 컴파일러는 a의 주소값에 대한 정보가 이미 있기 때문이다.
하지만 라이브러리 함수인 scanf는 이 정보에 접근할 수 없어 따로 주소값이 필요하다.
왜 함수인 scanf는 이 정보에 접근할 수 없을까?
이는 성능, 코드 재사용, 최적화, 모듈성의 측면에서 여러 이점을 챙길 목적으로 함수를 기본 프로그램과는 별도로 컴파일 후 다시 링크하기 때문이다.
번외. scanf함수는 어떻게 생겼을까?
이쯤 되면 scanf함수가 어떻게 생겼는지가 궁금하다. scanf는 c 표준 라이브러리 함수로, c언어에서 기본으로 쓰라고 제공하는 함수이다. 우리가 코딩할 때 맨 위에 쓰는
#include <stdio.h>
이 문구는 STandarD Input Output(해석하면 표준 입출력)의 Header 파일을 포함하라는 뜻이고 해당 헤더 파일 안에 scanf가 선언되어 있다.
포함하라는 의미는 그냥 헤더 파일을 복붙한다는 것일까? 아니다. 이는 따로 글을 쓰기로 하겠다.
어쨌든 stdio.h라는 헤더 파일을 들여다보면 그 안에 scanf함수를 찾을 수 있다는 뜻.
https://blog.naver.com/tipsware/221275585536 여기에서 c언어 표준 함수의 헤더 파일 위치에 접근하는 법을 확인해 보자.
C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt
필자의 경우 이 폴더 안에 있었다.
헤더 파일을 비주얼 스튜디오로 열어주면
scanf를 검색하면 나오는데...겁나 많이 나온다. 이를 직접 뜯어보는 것은 다른 글에서 하도록 하겠다.