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];
someNumbers
4 개의 요소로 구성된 배열을 요구합니다 . 여기서 해당 배열의 각 요소는 유형 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이 상호 교환 가능한 사용법을 파악할 수있는 위치에서 제공 한 예를 제외하고는 선언 된대로 항상 형식을 처리합니다. 따라서 [] []를 함수에 전달하면 정적으로 할당 된 플랫이라고 가정해야합니다. 여기서 **는 포인터를 가리키는 포인터입니다.
답변
우리가, 가정 a1
및 a2
아래 (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;
}