C 포인터 질문좀욧

loopback.kr의 이미지

마땅히 물어볼데가 여기밖에 없네요;;;;;^^

혼자 연구하는 C/C++ 공부하면서 안풀리는게 생겼는데...

x86 CPU 환경에서

int main()
{
int ar[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};

printf("%d\n", sizeof(ar));
printf("%d\n", sizeof(ar+0));
printf("%d\n", sizeof(ar+2));

// 위 세 문장의 차이가 무엇인지 궁금합니다.
//나름대로 생각해서 적어봤는데 틀린 부분이 무엇인지 여쭤봅니다.~
//배열이 sizeof연산자의 피연산자일때 배열 그 자체로 평가된다고 책에 적혀있길래
//첫번째 문장: ar은 배열 그 자체의 크기니까 모든 요소들의 크기 48이 나와야겠구나~!
//두번째 문장: 수학적으로 ar+0 == ar 이지 않나? 그럼 똑같잖아? 그럼 48 나와야지?
//세번째 문장: ar+2는 2번째 요소 즉 세번째 배열명을 가리키므로 세번째 배열의 크기 16이 나와야겠지?
//
printf("%d\n", sizeof(*(ar+2)));
//네번째 문장: 이것도 ar[2]랑 같은 의미니까 세번째 배열명(==이 배열의 시작 번지)을 가리키고 배열명 == 포인터 상수 이지.
// sizeof연산자의 피연산자이니까 배열 그 자체의 크기 16이 나오지.

return 0;
}

이렇게 판단했는데
몇개는 맞는데 몇개는 틀리네요...

누가 이 돌머리 이해시켜주실 분 없나요?~

익명 사용자의 이미지

자, 답글을 달기에 앞서, 한 가지 좀 되묻도록 합시다.

대체 어떻게, 무슨 수로 말이죠,
표준에 의거 비직관적인 동작을 할 여지가 조금이라도 있으면, 꼭 누군가가 그 코드를 정말로 작성하는 거죠?
sizeof(ar+0)라니, 상상도 못 했습니다.

표준에 의거 설명을 드릴 수는 있습니다만, 이렇게 교묘하게 표준 해킹을 하는 코드라니요.
혹시 알고 그러신 건가요? 그게 아니면 ioccc 같은 대회에서 요구하는 종류의 능력을 타고나신 걸지도...

흠, 우선, 이 답변글에서 C언어 표준 설명은 C99 표준을 기반으로 하고 있다는 것을 밝혀 둡니다. 물론 다른 버전의 표준이라고 해도 큰 틀은 달라지지 않습니다.

이 모든 일의 단초는, C언어 표현식(expression)의 문법이 애초에 배열을 직접적으로 다루는 경우가 극히 드물다는 겁니다.
아무래도 표준을 제정한 사람들이, 그렇게 함으로써 표준을 좀 더 단순하게 만들 수 있을 거라고 생각한 것 같아요.
아무튼, 덕분에 C언어에서 초보 프로그래머들이 배열을 사용하고 있다고 믿고 있을 때, 사실은 그렇지 않은 경우가 종종 있어요.
심지어 가장 "배열"스러운 연산자인 배열 첨자 연산자(Array subscripting operator, [])를 사용할 때조차도 그렇습니다.

"타입 T에 대한 배열"은, 다음과 같은 예외 상황을 제외하고, 그 배열의 첫 원소를 가리키는 rvalue "타입 T에 대한 포인터"로 암시적 변환됩니다.
쉽게 말해서, 배열 arr가 마치 &arr[0]과 같이 쓰인 것처럼 변환된다는 거죠.

예외는 C99 기준으로 딱 셋입니다.
1) sizeof 연산자의 operand로 사용될 때
2) unary & 연산자의 operand로 사용될 때
3) 문자열 상수(string literal)로 배열을 초기화할 때

더 쉽게, 예를 들어서 설명해 드리죠.

int arr[3] = {1, 2, 3};
 
sizeof(arr); // 예외 1 : sizeof 연산자의 operand로 사용됨. sizeof(int[3])이므로 3 * sizeof(int)와 같음.
&arr; // 예외 2 : unary & 연산자의 operand로 사용됨. 결과 타입은 int (*)[3]
char str[] = "abc"; // 예외 3 : "abc"의 타입은 char[4], 문자열 상수로 배열을 초기화하고 있으므로 char *로 변환되지 않고 길이 정보를 유지하여, str은 char[4] 타입이 됨.

자, 이제 질문자님의 코드를 풀어 봅시다.

sizeof(ar) : 예외 1에 의해서 ar은 타입 int[3][4]를 고스란히 유지하고, 따라서 int[3][4]가 되어 답은 12 * sizeof(int) 입니다. sizeof(int)는 x86에서 보통 4니까 아마 48이 나오겠죠.

sizeof(ar+0) : +0은 아무 효과 없는 것처럼 보이지만, 어쨌건 있기는 있는 거고, arsizeof의 operand가 아니게 만드는 결과를 초래합니다! 결국 아무 예외 조항에도 걸리지 않으니 ar는 속절없이 int (*)[4]라는 포인터 타입으로 변하고 맙니다. 그러고 나서 +0이 적용되는거고, 이제 와서 아무 효과 없어봤자 변환된 타입이 원래대로 돌아가지는 않지요. 결국 답은 sizeof(int (*)[4])이고, 뭐 포인터 하나 크기가 반환되겠죠. x86이면 4, x86_64면 8...

sizeof(ar+2) : 위와 같습니다. 위는 {1, 2, 3, 4}를 가리키는 포인터이고, 이건 {9, 10, 11, 12}를 가리키는 포인터겠죠. 어쨌든 타입은 int (*)[4]으로 같으니까요. 그러니 역시 답은 x86이면 4, x86_64면 8...

sizeof(*(ar+2)) : 뭐 별 거 있습니까. ar+2{9, 10, 11, 12}를 가리키는 int (*)[4] 타입 포인터였으니, *(ar+2){9, 10, 11, 12}int [4] 타입 배열인 거죠. 배열이 sizeof 연산자의 operand가 되고 있으므로 예외 1에 걸리고, 배열 타입을 고스란히 유지하여 sizeof(int [4])가 됩니다. 그러므로 답은 4 * sizeof(int). sizeof(int)는 x86에서 보통 4니까 아마 16이 나오겠죠.

어때요, 맞았나요? 가서 한 번 확인해봅시다.

#include <stdio.h>
 
int main(void){
	int ar[3][4] = {
		{1, 2, 3, 4},
		{5, 6, 7, 8}, 
		{9, 10, 11, 12}
	};
	printf("%d\n\n", sizeof(int (*)[4])); // 포인터 타입 크기 확인용
 
	printf("%d\n", sizeof(ar));
	printf("%d\n", sizeof(ar+0));
	printf("%d\n", sizeof(ar+2));
	printf("%d\n", sizeof(*(ar+2)));
	return 0;
}

실행 결과 : http://ideone.com/VimNlY

예상하셨던 것과 달리 비수학적(!)이게도 arar+0과 다르게 동작해서 많이 당황하셨나요?
어쩔 수 없습니다. 이건 대수학이 아니거든요. C언어의 semantic이라는 거죠.
그리고, 아무리 그렇다고 해도 ioccc 출품작 말고는 sizeof(ar+0) 같은 코드 쓰지 마세요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.