Array and Struct: 두 판 사이의 차이

youngwiki
79번째 줄: 79번째 줄:
위의 배열은 row-major order 기준으로 각 행에 연속적으로 20바이트를 할당한다. 배열이 메모리에 할당된 모습은 figure 3에 잘 나타나있다.  
위의 배열은 row-major order 기준으로 각 행에 연속적으로 20바이트를 할당한다. 배열이 메모리에 할당된 모습은 figure 3에 잘 나타나있다.  
i
i
2차원 배열은 배열의 배열이라고 할 수 있다. 이에 따라, A[i]는 i번째 행(배열)의 시작 주소를 의미한다. 따라서 A[i]가 가리키는 주소는 <code>A[i] = A + i * (M * sizeof(T))</code>와 같이 계산된다.  
2차원 배열은 배열의 배열이라고 할 수 있다. 이에 따라, A[i]는 i번째 행(배열)의 시작 주소를 의미한다. 따라서 A[i]가 가리키는 주소는 <code>A[i] = A + i * (M * sizeof(T))</code>와 같이 계산된다.


==Access in 2D Array==
아래는 2차원 배열의 행에 접근하는 C언어, 어셈블리 코드이다:
<syntaxhighlight lang="c">
int* get_row(int arr[][5], int row_idx) {
    return arr[row_idx];
}
</syntaxhighlight>
<syntaxhighlight lang="asm">
movslq %esi, %rsi          ; row_idx를 64비트로 확장
lea (%rsi, %rsi, 4), %rax  ; %rsi * 5 → 5 * row_idx
lea (%rdi, %rax, 4), %rax  ; 최종 주소 = arr + 20 * row_idx
ret
</syntaxhighlight>
위 어셈블리 코드의 핵심 구현 사항은 <code>get_row()</code> 함수가 <code>arr + 20 * row_idx</code>에 해당하는 주솟값을 리턴해야 한다는 것이다.


==각주==
==각주==
[[분류:컴퓨터 시스템]]
[[분류:컴퓨터 시스템]]

2025년 5월 13일 (화) 04:14 판

상위 문서: Assembly

개요

해당 문서에서는 1차원 배열(array)와, 다차원 배열, 그리고 포인터 배열의 특성에 대해 다룬다. 또한 구조체(struct)를 선언할 때, 구조체가 메모리에 어떻게 배치되는지와 정렬 및 패딩(padding) 규칙이 구조체에 어떤 영향을 미치는지에 대해 다룬다.

Array

C에서는 기본적으로 아래와 같이 배열을 선언한다:

T A[N];
Figure 1. Array allocation example
Figure 1. Array allocation example

T는 배열의 원소(element)의 자료형, N는 배열에 최대로 저장될 수 있는 원소의 수에 해당한다. 위 명령어를 통해 총 N * sizeof(T) 바이트의 연속적인 메모리 할당이 이뤄진다. 예를 들어, char *p[3];와 같은 명령어는 포인터 자료형의 크기가 8바이트이므로, 총 24바이트의 공간이 할당된다. 이는 figure 1이 잘 보여준다.

Array Access

기본적으로 배열 이름 A는 배열의 첫 원소의 시작 주소를 지정하는 포인터처럼 작동한다. 예를 들어, 배열 val이 figure 2와 같이 선언되었을 때, 배열 이름 val에 대한 산술 연산은 아래와 같이 작동한다.

Figure 2. Array access example
Figure 2. Array access example
Expression Type Value
val int* x
&val[1] or val + 1 int* x + 1 * 4
val[1] or *(val + 1) int 5
&val[i] or val + i int* x + i * 4

아래는 배열의 원소에 접근하는 C언어 기반의 함수와, 동일한 작업을 수행하는 어셈블리 코드이다.

int get_elem(int* arr, long idx) {
  return arr[idx];
}
get_elem:
  mov (%rdi,%rsi,4), %eax
  ret

위 어셈블리 코드에서 %rdi는 배열의 시작 주소를 의미하며, %rsi는 인덱스를 의미한다. 따라서, arr[idx]%rdi + 4 * %rsi에 위치한 원소이다.

Array vs. Pointer in C

배열과 포인터는 밀접한 관계가 있지만, 동일하지 않다. char str1[32];과 같이 배열 이름은 포인터처럼 사용될 수 있지만, 진짜 포인터 변수는 아니다. 이는 아래의 예시 코드를 통해서 알아볼 수 있다.

char str1[32];
char str2[64];
char *p = str1;
printf("%p vs. %p\n", str1, p); 
printf("sizeof(str1) = %ld\n", sizeof(str1)); // 배열의 크기 = 32 
printf("sizeof(p) = %ld\n", sizeof(p)); //포인터 크기 = 8
p = str2; //char* 포인터에 배열 이름 할당은 괜찮음
str2 = p; //이는 컴파일 오류를 야기

위의 코드에서는 마지막 줄이 컴파일 오류를 야기한다. 이는 배열 이름은 사실상 상수와 같이 동작하므로, 재할당이 불가하기 때문이다. 하지만 그 외에는 사실상 포인터와 같이 동작하므로, 서로 호환되어 사용되는 것을 보여준다.

Multi-dimensional (2D) Array

C에서는 다음과 같이 2차원 배열을 선언한다:

T A[N][M];

이는 N행 M열의 2차원 배열을 선언하고, 선언된 배열의 메모리 크기는 N * M * sizeof(T)이다. 이때 C언어는 row-major order를 사용하여 이차원 배열의 각 원소들을 저장한다. Row-major order란 같은 행의 원소들이 메모리 상에서 연속적으로 저장되는 것을 의미한다. 예를 들어, 아래와 같이 배열이 주어져있다고 가정하자.

Figure 3. 2D Array: Array of Array
Figure 3. 2D Array: Array of Array
int val[4][5] = {
  {9, 8, 1, 9, 5},
  {9, 8, 1, 0, 5},
  {9, 8, 1, 0, 3},
  {9, 8, 1, 1, 5}
};

위의 배열은 row-major order 기준으로 각 행에 연속적으로 20바이트를 할당한다. 배열이 메모리에 할당된 모습은 figure 3에 잘 나타나있다. i 2차원 배열은 배열의 배열이라고 할 수 있다. 이에 따라, A[i]는 i번째 행(배열)의 시작 주소를 의미한다. 따라서 A[i]가 가리키는 주소는 A[i] = A + i * (M * sizeof(T))와 같이 계산된다.

Access in 2D Array

아래는 2차원 배열의 행에 접근하는 C언어, 어셈블리 코드이다:

int* get_row(int arr[][5], int row_idx) {
    return arr[row_idx];
}
movslq %esi, %rsi          ; row_idx를 64비트로 확장
lea (%rsi, %rsi, 4), %rax  ; %rsi * 5 → 5 * row_idx
lea (%rdi, %rax, 4), %rax  ; 최종 주소 = arr + 20 * row_idx
ret

위 어셈블리 코드의 핵심 구현 사항은 get_row() 함수가 arr + 20 * row_idx에 해당하는 주솟값을 리턴해야 한다는 것이다.

각주