[c] 다차원 배열은 메모리에서 어떻게 포맷됩니까?

C에서는 다음 코드를 사용하여 힙에 2 차원 배열을 동적으로 할당 할 수 있다는 것을 알고 있습니다.

int** someNumbers = malloc(arrayRows*sizeof(int*));

for (i = 0; i < arrayRows; i++) {
    someNumbers[i] = malloc(arrayColumns*sizeof(int));
}

분명히 이것은 실제로 정수로 구성된 여러 개의 1 차원 배열에 대한 포인터의 1 차원 배열을 만들고 “시스템”은 내가 요청할 때 의미하는 바를 알아낼 수 있습니다.

someNumbers[4][2];

그러나 다음 줄과 같이 2D 배열을 정적으로 선언하면 … :

int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];

… 스택에서 유사한 구조가 생성됩니까? 아니면 완전히 다른 형태입니까? (즉, 포인터의 1D 배열입니까? 그렇지 않은 경우, 무엇이며, 이에 대한 참조는 어떻게 파악됩니까?)

또한 “시스템”이라고 말했을 때 실제로 그것을 알아내는 데 책임이있는 것은 무엇입니까? 커널? 아니면 C 컴파일러가 컴파일하는 동안 정렬합니까?



답변

정적 2 차원 배열은 배열 배열처럼 보입니다. 메모리에 연속적으로 배치되어 있습니다. 배열은 포인터와 같지 않지만, 종종 그것들을 거의 상호 교환 적으로 사용할 수 있기 때문에 때때로 혼란 스러울 수 있습니다. 그러나 컴파일러는 올바르게 추적하므로 모든 것이 잘 정렬됩니다. 언급 한 것처럼 정적 2D 배열에주의해야합니다. int **매개 변수 를받는 함수에 1을 전달하려고하면 나쁜 일이 발생할 수 있기 때문입니다. 다음은 간단한 예입니다.

int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}};

메모리에서 다음과 같습니다.

0 1 2 3 4 5

정확히 다음과 같습니다.

int array2[6] = { 0, 1, 2, 3, 4, 5 };

그러나이 array1기능 에 전달하려고하면 :

void function1(int **a);

경고 메시지가 표시되고 앱이 배열에 올바르게 액세스하지 못합니다.

warning: passing argument 1 of function1 from incompatible pointer type

2D 배열이와 같지 않기 때문 int **입니다. 배열에서 포인터로의 자동 소멸은 말하기를 “한 수준 깊이”만 진행합니다. 함수를 다음과 같이 선언해야합니다.

void function2(int a[][2]);

또는

void function2(int a[3][2]);

모든 것을 행복하게 만듭니다.

이 같은 개념은 n 차원 배열로 확장됩니다 . 응용 프로그램에서 이러한 종류의 재미있는 비즈니스를 활용하면 일반적으로 이해하기가 더 어려워집니다. 그러니 조심하세요


답변

답은 C가 정말하지 않는 생각을 기반으로 2 차원 배열을 – 그것은 배열 – 중 – 배열을 가지고있다. 이것을 선언하면 :

int someNumbers[4][2];

someNumbers4 개의 요소로 구성된 배열을 요구합니다 . 여기서 해당 배열의 각 요소는 유형 int [2](2 자체의 배열 임)입니다.int 의 ).

퍼즐의 다른 부분은 배열이 항상 메모리에 연속적으로 배치된다는 것입니다. 요청하는 경우 :

sometype_t array[4];

그러면 항상 다음과 같이 보일 것입니다.

| sometype_t | sometype_t | sometype_t | sometype_t |

(4 개의 sometype_t물체가 서로 간격을두고 배치되지 않음). 따라서 someNumbers배열 배열에서 다음과 같이 나타납니다.

| int [2]    | int [2]    | int [2]    | int [2]    |

그리고 각 int [2]요소 자체는 다음과 같은 배열입니다.

| int        | int        |

전체적으로 다음과 같은 결과를 얻습니다.

| int | int  | int | int  | int | int  | int | int  |


답변

unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}};

메모리에서 다음과 같습니다.

unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9};


답변

컴파일러는 많은 노력을 기울이고 있지만 둘 다 대답합니다.

정적으로 할당 된 배열의 경우 “시스템”이 컴파일러가됩니다. 스택 변수처럼 메모리를 예약합니다.

malloc의 배열의 경우, “시스템”은 malloc (일반적으로 커널)의 구현자가됩니다. 컴파일러가 할당 할 모든 것은 기본 포인터입니다.

컴파일러는 Carl이 상호 교환 가능한 사용법을 파악할 수있는 위치에서 제공 한 예를 제외하고는 선언 된대로 항상 형식을 처리합니다. 따라서 [] []를 함수에 전달하면 정적으로 할당 된 플랫이라고 가정해야합니다. 여기서 **는 포인터를 가리키는 포인터입니다.


답변

우리가, 가정 a1a2아래 (C99) 같이 정의 및 초기화 :

int a1[2][2] = {{142,143}, {144,145}};
int **a2 = (int* []){ (int []){242,243}, (int []){244,245} };

a1메모리에 평범한 연속 레이아웃을 가진 동종 2D 배열이며 표현식 (int*)a1은 첫 번째 요소에 대한 포인터로 평가됩니다.

a1 --> 142 143 144 145

a2이기종 2D 배열에서 초기화되고 type 값에 대한 포인터입니다 int*. 즉, dereference 표현식 *a2이 type 값으로 평가되므로 int*메모리 레이아웃이 연속적 일 필요는 없습니다.

a2 --> p1 p2
       ...
p1 --> 242 243
       ...
p2 --> 244 245

완전히 다른 메모리 레이아웃과 액세스 시맨틱에도 불구하고 배열 액세스 표현식에 대한 C 언어 문법은 동종 및 이종 2D 어레이에서 똑같이 보입니다.

  • 표현은 a1[1][0]값을 가져옵니다 144에서a1어레이
  • 표현식 a2[1][0]의 값을 페치한다 244밖으로 a2어레이

컴파일러에 대한 액세스-expression이 있음을 알고 a1유형에 운영 int[2][2]에 대한 액세스-expression이 때, a2종류에 작동합니다 int**. 생성 된 어셈블리 코드는 동종 또는 이종 액세스 시맨틱을 따릅니다.

코드는 일반적으로 유형의 배열이 유형 int[N][M]캐스트되고 유형으로 액세스 되면 런타임에 충돌 int**합니다. 예를 들면 다음과 같습니다.

((int**)a1)[1][0]   //crash on dereference of a value of type 'int'


답변

특정 2D 배열에 액세스하려면 아래 코드와 같이 배열 선언에 대한 메모리 맵을 고려하십시오.

    0  1
a[0]0  1
a[1]2  3

각 요소에 액세스하려면 매개 변수로 관심있는 배열을 함수에 전달하기에 충분합니다. 그런 다음 열에 오프셋을 사용하여 각 요소에 개별적으로 액세스하십시오.

int a[2][2] ={{0,1},{2,3}};

void f1(int *ptr);

void f1(int *ptr)
{
    int a=0;
    int b=0;
    a=ptr[0];
    b=ptr[1];
    printf("%d\n",a);
    printf("%d\n",b);
}

int main()
{
   f1(a[0]);
   f1(a[1]);
    return 0;
}


답변