(1)
#include <limits.h>
int main(void)
{
unsigned long long n1 = LLONG_MAX;
long n2 = -5;
n1+=100;
n1+n2; // 연산 결과값의 자료형은?
return 0;
}
(2)
#include <limits.h>
int main(void)
{
unsigned long n1 = LONG_MAX;
int n2 = -5;
n1+=100;
n1+n2; // 결과값의 자료형은? (int의 크기보다 long의 크기가 더 큰 시스템이라 가정)
return 0;
}
(3)
int main(void)
{
printf("%d %d %d\n"); // 서식문자와 대응되는 데이타의 개수 불일치
printf("%d\n", 300U); // 대응되는 데이타의 자료형 불일치
printf("%u\n", -300); // 대응되는 데이타의 자료형 불일치
printf("%d\n", 0.12); // 대응되는 데이타의 자료형 불일치
return 0;
}
서식문자와 대응되는 데이타의 개수 불일치 혹은 대응되는 데이타의 자료형이 불일치 할 경우 C 표준에서는 정의되지 않은 동작이라고 명시해둔게 맞나요?
특히 2, 3, 4번째 코드의 결과가 Visual C++ 에서도 Dev C++ 에서도 같다보니 조금 혼란스럽습니다.
결과가 달랐으면 정의되지 않은 동작이라 서로 다르구나 이해할 텐데 말이죠.
물론 printf 함수 내부적으로는 서식문자와 대응되는 데이타의 자료형이 일치한다고 가정하여 '무조건' 서식문자를 기준으로 데이타를 해석하니 두 컴파일러 모두 정의되지 않은 동작(자료형 불일치) 상황을 따로 검사하는 코드 없이 위의 '무조건' 서식문자를 기준으로 데이타를 해석하는 코드를 정의되지 않은 동작으로 채택할 순 있겠지만요.
각설하고 서식문자의 불일치는 정의되지 않은 동작이 맞나요?
함께 공부하는 곳이니 질문도 올리고 서로 토론하면 좋겠습니다.
관리자라서 본문의 내용을 벗어나면 잘 몰라서 답변하기가 어렵습니다. ㅜㅜ
잘 하는 다른 분이 알려주시면 좋겠어요.
환경이 다르다는 건 VC++하고 리눅스 64비트 환경에서 GCC가 동작이 많이 다릅니다.
https://dojang.io/mod/page/view.php?id=5
마지막 참고에 보면 온라인 컴파일러가 있습니다. 64비트 리눅스 환경에서 GCC를 사용하니 여기서 테스트해서 비교해보면 어떨까요?
감사합니다.
C 언어 전문가 선생님에게 문의해서 들은 설명을 정리해서 답변으로 올려드립니다.
-. 랭크가 같은 타입에서 signed와 unsigned를 섞어 쓰면 unsigned가 이긴다. 계산도 unsigned 타입으로 되고, 결과도 unsigned 타입이 된다.
-. 랭크가 다른 타입끼리 섞으면 상위 랭크가 이긴다. 상위 랭크가 하위 랭크 타입을 모두 표현할 수 있기 때문. 따라서 계산도 상위 랭크 타입으로 되고 결과도 상위 랭크 타입이 된다.
-. 상위 랭크 타입이 하위 랭크 타입의 값을 모두 표현할 수 없으면 상위 랭크의 unsigned 타입이 사용된다. 따라서 결과도 unsigned 타입이 된다.
이렇다고 합니다.
https://dojang.io/mod/page/view.php?id=113
그림 16-3을 보면 형 확장과 축소에서 랭크가 숫자로 표시되어 있습니다.
따라서 문의하신 내용을 보면
1. signed와 unsigned를 섞은 것이므로 unsigned가 됩니다. 따라서 unsigned long long(ull)이 된다고 합니다.
2. 64비트 환경의 int, long의 크기가 중요한 게 아니라고 합니다. signed와 unsigned를 섞으면 unsigned가 이깁니다.
(int의 크기보다 long의 크기가 더 큰 시스템이라 가정)
int, long의 크기가 아니라 자료 타입의 랭크가 높은 쪽이 이긴다고 합니다. 즉, int와 long의 크기와 무관한 문제라고 합니다.
3. 3번에서 문의하신 3, 4는 정의되지 않은 동작(undefined behavior, UB)이 맞다고 합니다. 다만, 대부분의 환경이 2의 보수로 숫자를 표현하고, IEEE 754를 사용하는 특성상 우연히 같은 결과를 확인할 뿐이라고 합니다.
마지막으로 전문가 선생님의 조언은 signed와 unsigned는 섞어 쓰지 않는 것이 좋다. 만약 섞어 쓴다면 그 의도를 명확히 해야 한다.
signed와 unsigned를 섞어 쓰는 것은 GCC에서는 -Wall 옵션을 주면 경고로 다 잡아준다. C에 익숙해지면 -Wno-sign-compare 옵션을 사용하게 된다.
4. 가변 인자 개수는 남는 건 괜찮지만 부족하면 안 된다고 합니다. 질문한 코드는 초기화하지 않은 자동 변수마냥 쓰레기 값이 쓰이게 된다고 합니다.
감사합니다.
여담이지만 문의 내용중 1,2번은 signed와 unsigned를 섞어써서 unsigned가 이기는게 아니라, 상위 랭크 타입이 하위 랭크 타입의 값을 모두 표현할 수 없으므로 상위 랭크의 unsigned로 결정되는 것이 옳다고 생각되요.