원글: https://arca.live/b/programmer/86258136?p=1
1. a = "ddd"
우선 좌변은 char 타입(정수), 우변은 char[4]인 배열 타입
배열은 값으로 평가될때 포인터로 붕괴(decay)함
따라서 포인터를 정수에 대입하려는 식
그러면 (x86_64의 경우) 포인터가 크기가 8인 정수로 변환되고, 이거를 크기가 1인 정수에 대입하므로 "일반적으로" LSB쪽의 1바이트만 잘려서 대입 됨
즉 a의 값은 "ddd" 문자열이 저장된 "주소"의 하위 1바이트 ('d'가 아니라 """주소""")
2. printf("%s", a)
우선 a가 가변 인자 함수로 넘어갈 때, 기본적으로 int보다 작은 타입들은 int로 캐스팅되어 들어감
따라서 a 값과 같은 값을 가지는 int형 정수로 넘어감
%s는 char *를 받아서 문자열을 출력해주는데, 이 때 전달된 포인터가 NULL일 경우 (null)을 출력해주는 C Library가 있음. 대표적으로 glibc
즉 우연인지 필연인지 "ddd"가 저장된 주소의 하위 1바이트가 0x00이라서 a의 값이 0이었고, 이를 NULL로 인식한 printf가 (null)을 출력해주는 것
과연 정말인지 확인해보자
환경은 mingw-w64-ucrt-x86_64, 컴파일러는 gcc

위 코드는 "ddd" 문자열이 저장되는 주소와 a에 대입된 값을 알아보기 위해 ptr과 a의 값을 각각 포인터와 정수형으로 출력하였다
얘를 컴파일&실행 해보면

보시다시피 "ddd"가 저장된 주소는 0x0000_7FF7_E25D_40'00'이고 따옴표 친 최하위 바이트 0x00이 a에 저장된 것을 볼 수 있다.
그렇다면 만약 문자열의 주소가 0x00으로 끝나지 않는다면 어떤 일이 벌어질까? 그리고 최상위 바이트도 00인데 정말 최하위 바이트가 저장되는걸까?

문자열의 시작주소 대신 시작주소 + 1을 대입해보자

그러면 보다시피 주소가 0x0000_7FF7_4802_40'01'이 되고 최하위 바이트인 0x01이 a에 저장된다.
그런데 주소값 0x0000_0000_0000_0001은 NULL도 아니고(즉 "(null)"을 출력하려 하지 않고) 읽기 가능한 메모리 영역도 아니기 때문에 읽으려는 시도가 실패하고 segmentaion fault가 발생한다.
호기심 해결!
지적환영 태클환영 오타 및 맞춤법 지적도 대환영
*) long -> char 같은 더 작은 타입으로의 캐스팅은 unsigned냐 signed냐에 따라 표준에 써있는 내용이 다르다.
unsigned일 경우에는 직관적으로 하위 바이트를 잘라 넣고(== 2의 거듭제곱으로 modular 연산한 결과), signed의 경우에는 implementation defined, 즉 구현에 따라 동작이 바뀔 수 있다. 이 경우에는 그냥 unsigned랑 동일하게 하위 바이트를 잘라 넣는 것 처럼 보이지만 이것에 의존하는 코드는 좋지 않다.
undefined behavior와 implementation defined는 비슷해 보이지만 다르다. UB는 동작이 표준에 정의되어있지 않고 구현에서 문서화 될 필요가 없으며, impl def는 동작을 표준에서 정하지는 않지만 구현에 따라 일관된 동작을 하며 문서화되어야한다.
즉 사용이 금기시되는 UB와 달리 long -> char같은 구현 정의 동작은 구현마다 다를 수는 있지만 문서화는 되어있으며, 주의깊게 사용하여야한다.
**) int와 signed int는 완전히 동일한 타입이지만 char는 signed char 또는 unsigned char과 동등한 타입으로 정의되어있으며, 둘 중 어느 하나와 동등하더라도 서로 같은 타입은 아니다.
즉 int는 signedness에 따라 int == signed int, unsigned int의 두가지 타입이 있지만 char는 char, signed char, unsigned char의 세가지 타입이 별개로 존재한다. short, long, long long은 int와 마찬가지
***) clang에서는 1번을 경고가 아닌 에러로 처리하므로 -Wno-int-conversion같은 옵션을 줘서 에러를 끄거나, 경고로 바꿔야 컴파일에 성공함.
링크: https://reviews.llvm.org/D129881
수정) a가 함수로 넘어갈 때 -> a가 '가변 인자' 함수로 넘어갈 때