변수 선언시 메모리에 배치되는 순서와 구조.

addnull의 이미지

이미 올라온 질문이라 생각되지만,
적당한 검색어를 정하지 못하고 헤매다 질문 올립니다.. ㅠ.ㅠ

#include <stdio.h>
#define     N       5

int main()
{
     char    i, j, k, l;
     char    s[N], t[N];
}

출력문 빼고 최대한 간략하게 표현했습니다. ^^;;

여기서 선언된 변수들의 주소값을 찍어봤더니
제 예상엔 빈틈없이 빽빽하게 나올 줄 알았는데,
변수들 사이에 공백이 존재하더군요.
storage unit size 관련 문제인가 싶어서 여러가지 테스트해봤지만,
그것과 다른 것 같기도 하고..

그리고 메모리에 배치되는 순서 역시 흥미롭네요.
i, j, k, l, s, t 순이 아니라.
t, s, l, k, j, i 순으로 배치됩니다.

변수가 메모리에 저장될때 순서나 배치 위치에 대한 어떤 정책이 있나요?

2005년 12월 28일.

mirr의 이미지

스택.....구조상 그러는거 아닌가요? ㅡ,.ㅡ
그리고 메모리 크기의 다름은 gcc에서 가비지컬렉터라고 해야하나?
그 메모리 크기를 단위별로 짤라서 저장시키기 때문에 그런거 아닌가요?? ㅡ.,ㅡ::

내 마음속의 악마가 자꾸만 나를 부추겨.
늘 해왔던 것에 만족하지 말고 뭔가 불가능해 보이는 것을 하라고 말야.

irondog의 이미지

변수 사이에 생기는 공백은 32비트 마이크로프로세서의 경우 데이터 fetch시에 4바이트 단위로 하기 때문에 생기는 padding아닌가요?

말하자면 마프로 데이터를 가져 갈 때 효율 문제로 내버려둔 공란이죠.

addnull의 이미지

글쎄요..
전체 소스는

      1 #include <stdio.h>
      2
      3 #define     N       4
      4
      5 int main()
      6 {
      7     char    i, j, k, l;
      8     char    s[N], t[N];
      9
     10     for (i=0 ; i<N ; i++)
     11     {
     12         printf("t[%d] = %p\n", i, t+i);
     13     }
     14     printf("\n");
     15     for (i=0 ; i<N ; i++)
     16     {
     17         printf("s[%d] = %p\n", i, s+i);
     18     }
     19     printf("\n");
     20     printf("l = %p\n", &l);
     21     printf("k = %p\n", &k);
     22     printf("j = %p\n", &j);
     23     printf("i = %p\n", &i);
     24     return 0;
     25 }

입니다.. 실행결과는

t[0] = 0xbffff00c
t[1] = 0xbffff00d
t[2] = 0xbffff00e
t[3] = 0xbffff00f

s[0] = 0xbffff010
s[1] = 0xbffff011
s[2] = 0xbffff012
s[3] = 0xbffff013

l = 0xbffff014
k = 0xbffff015
j = 0xbffff016
i = 0xbffff017

이구요.. 여기서 N을 5로 바꾸면,

t[0] = 0xbfffe8e0
t[1] = 0xbfffe8e1
t[2] = 0xbfffe8e2
t[3] = 0xbfffe8e3
t[4] = 0xbfffe8e4

s[0] = 0xbfffe8f0
s[1] = 0xbfffe8f1
s[2] = 0xbfffe8f2
s[3] = 0xbfffe8f3
s[4] = 0xbfffe8f4

l = 0xbfffe90c
k = 0xbfffe90d
j = 0xbfffe90e
i = 0xbfffe90f

이런 결과가 나오더군요.

irondog님의 말씀처럼 4바이트 단위로 묶인다고 보기엔 어려울 것 같습니다.

아참.. gcc 3.3.5 에서 테스트했습니다.

요즘 귀차니즘 때문에 VC를 깔아놓지 않아서.. =_=)a;;
윈도우에선 테스트를 못해봤습니다...

[極 wrote:
미르[眞]"]스택.....구조상 그러는거 아닌가요? ㅡ,.ㅡ
그리고 메모리 크기의 다름은 gcc에서 가비지컬렉터라고 해야하나?
그 메모리 크기를 단위별로 짤라서 저장시키기 때문에 그런거 아닌가요?? ㅡ.,ㅡ::

예전 제 경험상으로
VC에서 call stack으로 function call 과정을 지켜보면,
호출 이전에 f(a, b, c) 였던게
호출 직후에 f(c, b, a) 로 바뀌는것과 비슷한 원리로 이해하고 있습니다.

하지만, 이건 스택 구조 때문이 아니라,
컴파일러의 구현상의 문제가 아닌가요?

2005년 12월 28일.

익명 사용자의 이미지

캐쉬 슬롯의 크기가 8byte이면 저렇게 나올 수도 있을 것 같은데요.
배열이 각 4 bytes일 경우 두개를 한 슬롯에 넣어도 괜잖으니 연속적으로 할당된 것일테고
배열이 각 5 bytes이면 한 슬롯에 배열 두 개를 넣으면 두 번제 배열이 한 캐쉬라인에 들어가지 않아서 두 번째 배열은 따로 캐쉬슬롯에 넣기 위해서 남은 3바이트를 비워둔게 아닐까요.

Bottom<->Top
ijkl<4bytes padding>s[5]<3 bytes padding>t[4]<3 bytes adding>

위와 같이 생겼을 것 같은데... 현대 컴퓨터 아키텍쳐에서는 캐쉬를 어떻게 효율적으로 쓰느냐가 성능에 크게 영향을 미치기 땜시 컴파일러가 저렇게 최적화 했을 가능성도 있을 듯... 어디까지나 추측

익명 사용자의 이미지

흐... 찾아보니 요즘 펜티엄 프로세서는 캐쉬 라인 크기가 64바이트군요....

creativeidler의 이미지

스택 구조의 문제가 맞습니다. 스택 밑에서부터 할당되기 때문에 저렇게 되는 거죠. 배열 사이에 공간이 있는 것은 배열에 대한 정보가 저장되는 것과 word-align 문제 때문입니다. (word-align은 위에서 어느 분이 말씀하신 4byte 문제랑 비슷하죠.)

lovian의 이미지

공백은 alignment 때문에 생길 수도 있고, 또는 canary bit가 들어가서 공백이나 쓰레기 값이 생기기도 하는 것 같습니다.

결국 컴파일러가 몇바이트로 align하느냐 canary bit를 넣느냐.. 뭐 그런거 인듯..

얼마전 본 책에 그런 설명이 나와있었는데.. :)

-----------------
한글을 사랑합니다.

익명 사용자의 이미지

* c의 경우 auto variable(보통 지역변수를 의미, 그외 register, static 등이 있겠지요.)은 스택을 이용해서 runtime시에 가변적으로 만들어 지고/해제됩니다. 이들이 만들어지는 영역이 스택인데, 스택이 거꾸로(번지개념으로는) 자라지요. 하드웨어 스택은 머신의 워드크기 단위로 push와 pop이 일어나게 됩니다. 이점도 참고하시면 문제해결에 보탬이 될듯합니다.

* 참고로 parameter passing방법의 차이(위의 얘기로는 컴파일러의 구현상 문제)로 보는것도 도움이 되겠습니다.
- 단적인 예로, pascal은 앞에서부터 파라메터를 넘기고, c는 뒤에부터 넘기게됩니다.

* 아울러 다음 코드는 ... 사용사례인데 한번 검토하시면 보탬이 될듯합니다.

typedef struct _recode{
   int len;            // 4 bytes
   int id;               // 4 bytes
   char name[20];         // 20 bytes
   float score;         // 4 bytes
   char student;         // 1 bytes
   struct _recode *next;   // 4 bytes
} recode;

typedef struct _arecode{
   int len;            // 4 bytes
   int id;               // 4 bytes
   char name[20];         // 20 bytes
   float score;         // 4 bytes
   char student;         // 1 bytes
   struct _recode *next;   // 4 bytes
} __attribute__ ((__packed__)) xcode;

main()
{
   int size;

   size = sizeof(recode);
   printf("size = %d\n", size);
   size = sizeof(xcode);
   printf("size = %d\n", size);
   return 0;
}
addnull의 이미지

Anonymous wrote:
* 참고로 parameter passing방법의 차이(위의 얘기로는 컴파일러의 구현상 문제)로 보는것도 도움이 되겠습니다.
- 단적인 예로, pascal은 앞에서부터 파라메터를 넘기고, c는 뒤에부터 넘기게됩니다.

나중에 선언된 변수부터 스택에 배치되는게
C언어로 보장된 거였군요(?)

이렇게 보장된 특별한 이유라도 있는건가요?

2005년 12월 30일

doldori의 이미지

어니스트 wrote:
Anonymous wrote:
* 참고로 parameter passing방법의 차이(위의 얘기로는 컴파일러의 구현상 문제)로 보는것도 도움이 되겠습니다.
- 단적인 예로, pascal은 앞에서부터 파라메터를 넘기고, c는 뒤에부터 넘기게됩니다.

나중에 선언된 변수부터 스택에 배치되는게
C언어로 보장된 거였군요(?)


아닙니다. 이에 대해서는 표준에서 전혀 보장하는 바가 없습니다.
lovewar의 이미지

어니스트 wrote:

나중에 선언된 변수부터 스택에 배치되는게
C언어로 보장된 거였군요(?)

이렇게 보장된 특별한 이유라도 있는건가요?

C언어에서 보장은 못할 것 같습니다.
일례로, IA-32체계의 경우도 파라미터 패싱방법이 3가지로 나와 있습니다.
http://www.intel.com/design/pentium4/manuals/index_new.htm#sdm_vol3
IA-32 Intel(r) Architecture Software Developer's Manual, Volume 1: Basic Architecture
에서 참조하였습니다.

Quote:

6.3.3 Parameter Passing
Parameters can be passed between procedures in any of three ways: through general-purpose
registers, in an argument list, or on the stack.

다른 시스템들(플랫폼 아키텍쳐)은 어떤가요?

addnull의 이미지

Anonymous wrote:
* 아울러 다음 코드는 ... 사용사례인데 한번 검토하시면 보탬이 될듯합니다.
typedef struct _recode{
   int len;            // 4 bytes
   int id;               // 4 bytes
   char name[20];         // 20 bytes
   float score;         // 4 bytes
   char student;         // 1 bytes
   struct _recode *next;   // 4 bytes
} recode;

typedef struct _arecode{
   int len;            // 4 bytes
   int id;               // 4 bytes
   char name[20];         // 20 bytes
   float score;         // 4 bytes
   char student;         // 1 bytes
   struct _recode *next;   // 4 bytes
} __attribute__ ((__packed__)) xcode;

main()
{
   int size;

   size = sizeof(recode);
   printf("size = %d\n", size);
   size = sizeof(xcode);
   printf("size = %d\n", size);
   return 0;
}

storage unit에 대해서 말씀하신듯 싶네요.
실행결과가

size = 40
size = 37

이더군요.

그런데 제 코드를 수정에서 테스트 해봤더니 그래도 이상한 점이..
우선 코드는

      1 #include <stdio.h>
      2
      3 #define     N       5
      4
      5 int main()
      6 {
      7     char    e[N];
      8     char    i, j, k, l, m;
      9     char    s[N], t[N];
     10
     11     for (i=0 ; i<N ; i++)
     12     {
     13         printf("t[%d] = %p\n", i, t+i);
     14     }
     15     printf("\n");
     16     for (i=0 ; i<N ; i++)
     17     {
     18         printf("s[%d] = %p\n", i, s+i);
     19     }
     20     printf("\n");
     21     printf("m = %p\n", &m);
     22     printf("l = %p\n", &l);
     23     printf("k = %p\n", &k);
     24     printf("j = %p\n", &j);
     25     printf("i = %p\n", &i);
     26     printf("\n");
     27     for (i=0 ; i<N ; i++)
     28     {
     29         printf("e[%d] = %p\n", i, e+i);
     30     }
     31     return 0;
     32 }

이고..
실행결과는

t[0] = 0xbfffe650
t[1] = 0xbfffe651
t[2] = 0xbfffe652
t[3] = 0xbfffe653
t[4] = 0xbfffe654

s[0] = 0xbfffe660
s[1] = 0xbfffe661
s[2] = 0xbfffe662
s[3] = 0xbfffe663
s[4] = 0xbfffe664

m = 0xbfffe67b
l = 0xbfffe67c
k = 0xbfffe67d
j = 0xbfffe67e
i = 0xbfffe67f

e[0] = 0xbfffe680
e[1] = 0xbfffe681
e[2] = 0xbfffe682
e[3] = 0xbfffe683
e[4] = 0xbfffe684

입니다.
여기서 보면, t[0]와 s[0]의 차이는 16byte이고,
그런데 s[0]와 m은 27byte 차이가 납니다.. ??
게다가 i 다음에는 빈공간 없이 바로 e가 배치되는군요..

몇가지 다른 방식으로 테스트 해봤는데,
아직까지 무슨 규칙을 찾지 못하고 있습니다..

점점 더 미궁으로. ㅜ.ㅜ

2005년 12월 30일.

익명 사용자의 이미지

doldori wrote:
어니스트 wrote:
Anonymous wrote:
* 참고로 parameter passing방법의 차이(위의 얘기로는 컴파일러의 구현상 문제)로 보는것도 도움이 되겠습니다.
- 단적인 예로, pascal은 앞에서부터 파라메터를 넘기고, c는 뒤에부터 넘기게됩니다.

나중에 선언된 변수부터 스택에 배치되는게
C언어로 보장된 거였군요(?)


아닙니다. 이에 대해서는 표준에서 전혀 보장하는 바가 없습니다.

표준에 있을 이유는 없지만, 이는 관습에 가깝다고 봅니다.
그리고, 이 부분은 컴파일러 구성에서 인터미디어트 코드 생성을 어떻게 구현할 것인가에 많이 의존할 것으로 보입니다. 즉, 컴파일러 구성시 전반부(고수준언어)가 아닌 후반부(저수준언어)에 가까운것같다는...
실제하드웨어/가상하드웨어의 서브루틴처리절차에 의존할 가능성이 높다고 봅니다.
call, ret형태의 명령들 말입니다. 이때 파라메터 패싱을 어떻게 할것인가?(calling sequence) ret를 만나 리턴할때, 어떻게 되돌아 올것인가 및 결과값의 전달은?(return sequence)
이런 것을 구현하다보면, 역시 스택을 이용하는게 좋다고 봅니다.
스택을 이용하는 것으로 일단 결정되었다면, 앞에서부터 넘길까? 뒤에서부터넘길까? 또는 맘대로 할까? ... ㅎㅎ
하여간 대부분이 이런 형태로 구성되지 않았을까요?

익명 사용자의 이미지

지역변수는 스택에 저장되는데 스택은 LIFO방식입니다.

즉, 스택은 후입선출방식입니다. 마지막에 들어온 것이

먼저 나오는 방식이죠... 그리고 스택의 관리 방식은

운영체제마다... 그리고 컴파일러마다 틀려질 수가 있습니다.

윈도우상의 비쥬얼스튜디오를 이용해서 컴파일을 해서

실행을 하게 되면 기준점을 기준으로...

만약 지역변수를 아래와 같이 선언을 하셨다면...

int i = 1;
int j = 2;
int k = 3;

스택에 저장될때는 kji이런순으로 들어가게됩니다.....

즉 이것은 기준점을 두고 위쪽부분의 아래부분부터 채워지기

때문입니다...

리눅스의 gcc로 컴파일을 하게 되면 이것과는 조금 다른

결과가 나오지요... 리눅스상의 gcc는 기준점을 두고 아래로

저장을 하기 때문입니다... 주소값을 출력해보면 서로

다른것이 나올 것입니다. 그리고 리눅스상의 스택의

관리방식에서는 약간의 공백이 존재할 것입니다...

이것또한 윈도우상의 비쥬얼 스튜디오랑 비교해 보았을때

다른 점 입니다... 이런 점들은 자료구조부분을 참고하면

될 듯 싶네요...^^

제가 아는 한도내에서 설명 해드렸는데 도움이 되었는지

모르겠네요...^^

그럼 수고하세요...^^

kuaaan의 이미지

어니스트 wrote:

하지만, 이건 스택 구조 때문이 아니라,
컴파일러의 구현상의 문제가 아닌가요?

제 기억으로는... 변수의 순서가 거꾸로 되는건 x86 Chip의 특성이었던 것 같습니다.
x86 계열은 스택을 높은 주소에서부터 낮은 주소로 쌓습니다.
따라서 나중에 선언된 변수가 낮은 주소명을 갖는 거죠.

아마... HP 계열은 똑바로 쌓일걸요?

----------------------------------------------
한번뿐인 인생....
미친듯이 살아보자!
----------------------------------------------

익명 사용자의 이미지

kuaaan wrote:
어니스트 wrote:

하지만, 이건 스택 구조 때문이 아니라,
컴파일러의 구현상의 문제가 아닌가요?

제 기억으로는... 변수의 순서가 거꾸로 되는건 x86 Chip의 특성이었던 것 같습니다.
x86 계열은 스택을 높은 주소에서부터 낮은 주소로 쌓습니다.
따라서 나중에 선언된 변수가 낮은 주소명을 갖는 거죠.

아마... HP 계열은 똑바로 쌓일걸요?

CPU내의 레지스터에 (시스템별로 다양, 개수 및 용도등) Stack Pointer(SP)레지스터와 같이 general purpose register가 아닌 special purpose register를 둔것은 x86이 가지는 특성입니다.
"x86이 곧 법이다."라는 논리는 문제가 있으므로, 이는(여기서는, 스택관련 전용레지스터를 CPU내에 두는것) 일반적인(대부분의) 프로세서의 특성이라고 보기 어렵습니다. 그러나, CPU의 범용레지스터중 관습적으로 특정 범용레지스터(예를들어 14번레지스터등)를 사용하는 경우가 많습니다(비x86인경우). 스택의 구현도 그 CPU별/OS별로 틀려질 수 있습니다.

HP-PA가 그런지는 모르겠으며 이왕이면 HP-PA의 어셈....등.. 코드좀 올려주시면 감사하겠습니다.

익명 사용자의 이미지

아참 그리고, 컴파일러 특성으로 보는게 맞는것 같습니다.
컴파일러가 하는일이 고수준컴파일도 있지만, 각 머신에 맞는 코드로 배치시키는 일을 하기도 하니까요. 해당 컴퓨터의 특성에 맞게 이를 수행하겠지요.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.