C extern로 다른파일 변수의 값 변경에 대해

글쓴이: 익명 사용자 / 작성시간: 금, 2020/03/13 - 7:07오후
C언어를 공부중 다른 파일의 전역변수를 가져와서 값을 변경하는 것에 대해 순수하게 의문점이 들어 질문합니다.
sub.c
t x=10; int ShowX(void){ return x; }
main.c
#include <stdio.h> #include <stdlib.h> extern int ShowX(void); //외부파일에 있는 x의 변함을 확인을 위함 extern int x; //x=100; //ERROR! int main(int argc, char *argv[]) { x=100; //가능 printf("result : %d\n",x); printf("result2 : %d\n",ShowX()); }
Dev C++ / GCC 4.9.2 64bit 로 공부중입니다.
외부에 존재한다고 알린 x의 값을 변경하는게 왜 main에서나 혹은 함수 안에서 가능한 것인지 고수님들의 도움이 필요합니다.
Forums:
컴파일러가 main.c를 컴파일할 때, 대충 이런
컴파일러가 main.c를 컴파일할 때, 대충 이런 식으로 컴파일합니다.
- [FILLME(x의 주소)] 위치에 100 저장
- [FILLME(x의 주소)] 에서 값을 읽어서 출력
- [FILLME(ShowX의 주소)] 에 있는 함수 호출
나중에 링커가 sub.o와 main.o를 연결하면서, sub.o에서 정의된 x와 ShowX의 주소를 main.o의 빈칸에 채워 넣습니다.
그 결과 실행 파일이 완성되는 거죠.
main함수 먼저 읽는다는 말씀이신가요?
main함수 먼저 읽는다는 말씀이신가요?
전역변수는 나중에 처리합니까?
정확히 어떤 순서를 말씀하시는 건지 모르겠군요.
정확히 어떤 순서를 말씀하시는 건지 모르겠군요.
1. sub.c를 컴파일할 때, 컴파일러는 전역 변수 x와 함수 ShowX를 정의하며, 이들의 이름이 외부 연결(external linkage)을 가진다는 사실을 파악합니다. 이 모든 정보는 sub.c를 컴파일한 결과인 object file(보통 sub.o)에 담기게 됩니다.
2. main.c를 컴파일할 때, 컴파일러는 외부 연결을 가진 두 대상체(변수 x 및 함수 ShowX)의 선언을 봅니다. 이들 대상체의 정의를 찾을 수는 없었지만, 외부 연결을 가진 이름들인 만큼 다른 소스 파일 어딘가에 정의되었을 거라고 믿고, 일단 이들을 참조하는 코드(main 함수 내부)에 빈 칸을 만들어 둡니다. 컴파일 결과는 마찬가지로 main.o에 담깁니다.
3. 최종적으로 링커가 sub.o와 main.o를 합칩니다. 그 결과 전역 변수 x와 함수 ShowX, main이 정의된 하나의 바이나리가 생성됩니다. 그 과정에서, 링커는 main.o에서 참조하는 이름 x 및 ShowX가 프로그램 전체에 걸쳐 단 한 번 정의되었는지 확인합니다. 실제로 sub.o 안에 정의되었으므로, 링커는 해당 객체의 정보를 main.o 안에 채워 넣습니다.
여기서 1과 2는 순서가 상관이 없고 종종 동시에 진행되기도 합니다. 하지만 3은 항상 마지막입니다.
IDE를 비롯한 편리한 개발 도구들은 "Build" 버튼 한 방에 이 모든 걸 알아서 해 줍니다. 하지만 gcc에 적절한 옵션을 줘서 직접 실행한다면, 프로그램 빌드의 각 단계들을 수동으로 하나씩 실행해 보고 결과물을 확인해 볼 수 있지요.
답변 감사드립니다.
상세한 답변에 감탄합니다. 그런데 말씀하신대로 2번에서 main.c를 컴파일하면서 변수 x에 대한 선언을 보는 것까지도 이해하고 3번도 이해했습니다만
아직도
위 예제에서 main함수나 다른 함수내가 아닌 전역변수가 선언되는 곳에 x의 값을 변경할 수 없는지는 이해가 되지 않습니다. 말씀하신대로 x를 참조하는 코드에만 빈칸을 만들고 내부가 아닌 일반적인 전역변수를 선언하는 곳에선 빈칸을 만들지 못하는 것인가요?
아애 처음부터 그렇게 설계되있는건가요?
정확히 뭘 못 한다는 거죠?
정확히 뭘 못 한다는 거죠?
질문이 어떤 의미인지 잘 이해가 안 되는데, 정확히 1)뭘 하고 싶고, 2)어떻게 시도했는데 안 되었는지 설명해보세요. 코드로 보여주시면 더 좋습니다.
제 질문이 좋지못했군요 다시한번 봐주시면 감사하겠습니다.
1.
main.c에서 위 그림에서
x=100; 이 안되는 이유를 알고싶습니다. main()함수 내에선 잘되는데도 불구하고요.
2.extern int x;으로써 먼저 외부에 x가 있다고 선언했음에도 x=100;에서 오류를 띄우는데
오류 메세지는
[Warning] type defaults to 'int' in declaration of 'x'
[Error] ld returned 1 exit status
이렇게 나오는데 오류메세지 자체가 이해가 안됩니다.
이거 원, 제가 완전히 헛다리 짚었군요.
이거 원, 제가 완전히 헛다리 짚었군요.
귀하께서 원하는
x=100;
, 즉 x에 100을 대입하겠다는 statement는 절대 main.c에서 그 위치에 올 수 없습니다.C언어에서 statement는 반드시 함수 안에서만 있을 수 있기 때문입니다.
x라는 이름을 볼 수 있는지, 접근할 수 있는지와는 다른 문제입니다.
파이썬이나 bash, 기타 스크립트 언어들처럼 인터프리터가 스크립트 파일 하나를 읽어나가며 함수가 있든 없든 위에서부터 쭉 실행해 나가는 그런 언어들과는 다릅니다.
C언어에서 실행 시간에 실행되는 모든 statement는 어떤 함수 정의 안에 들어있어야 하며, 어떤 이유로든 실행 흐름이 그 함수 안으로 들어와야 실행될 기회가 있다는 뜻입니다.
반면, sub.c에서
int x=10;
와 같이 할 수 있었던 건, 이게 statement가 아니라 declaration, 즉 선언이었기 때문에 가능했던 겁니다. 이 때 초기값을 주는=10
역시 선언의 일부입니다.x=100;
이 statement가 아니라 declaration이 있어야 할 자리에 있기 때문에, 컴파일러는 이 구문을 declaration으로 해석합니다. 그리고 옛날 옛적 구시대 C언어 문법에 의해, 선언에 타입이 지정되지 않을 경우 int가 주어졌다고 가정합니다.이 옛 문법은 현대적인 C 프로그램을 컴파일할 경우엔 프로그래머의 의도에 어긋날 가능성이 99.9%이므로, 보시다시피 Warning이 뜹니다.
그리고 main.c에서 외부 이름을 가진 변수 x를 초기치와 함께 선언했으므로 이를 정의(define)했다고 가정합니다.
그리고 링커는 똑같은 이름의 객체 x를 sub.o와 main.o에서 둘 다 정의했다는 것을 확인하고 에러를 발생시킵니다.
정확한답변 감사합니다
분명하게 알려주셔서 감사합니다.
책에 이런 내용이 없었고 인터넷으로 찾기도 어려워서 막막했는데
고수님의 내공에 탄복하고 갑니다
댓글 달기