본문 바로가기
SW/C,C++

[C/C++] 함수인자로써 이중포인터와 가변길이배열

by FastBench 2024. 5. 13.

지난 게시글 : https://microelectronics.tistory.com/50

 

[C/C++] 동적 2차원 배열과 가변길이배열(VLA)

C언어의 표준에서는 고정된 크기의 배열을 선언할 때 배열의 크기를 컴파일 시간에 알 수 있어야 했다. 그렇기 때문에 C99 표준 이전에는 배열의 크기로 변수를 사용하는 것이 허용되지 않았다.

microelectronics.tistory.com

 

이중포인터와 가변길이 2차원배열

2중포인터로 동적 2차원 배열을 만들었다고 하더라도, 여전히 변수의 타입은 이중포인터다.

하지만 배열과 동작원리(주소가 가리키는 메커니즘)는 같으므로, 배열을 조종하듯 값을 넣고 뺄 수 있다.

 

(물론 2중 포인터로 동적할당 시 각 행의 첫번째 요소들이 연속된 값을 가지도록 해야 한다. 아래 코드 참고)

당연히 포인터로 접근해도된다.

애초의 변수 타입을 포인터가 아닌 배열로 해도 접근 방식은 똑같다.

하지만 둘의 타입은 명확하게 다르다.

사용법이 같고, 접근방법이 같다고 해도 타입이 다르다.

 

대표적으로 sizeof 를 해보면 알 수 있다.

이중포인터로 arr 를 선언한 경우 크기는 포인터 변수이므로 8바이트이고,

2차원 배열로 arr 를 선언하는 경우 크기는 36바이트이다. (3x3 배열일때)

 

그리고 배열의 경우 연속된 공간의 stack 에 저장될테지만

포인터 변수로 동적할당할 경우 비연속적인 공간의 heap 에 저장된다.
(위 구현에 따르면, 열은 연속적인 공간, 행은 비연속적인 공간이다. 물론 구현방법에 따라 연속적인 공간에 할당도 가능하다. 게시글 맨위 링크 참고)

 

함수 인자

타입이 다르므로 함수의 인자로 넘길때도 방법이 다르다.

 

만약 2차원 배열로 선언했다면, 아래와 같이 함수의 인자의 타입은 int (*arr)[n] 로 해야한다.

 

 

만약 이중포인터로 선언했다면, 함수는 아래와 같이 선언해야 한다.

 

 

타입에 대한 분석

조금 더 생각을 해보자. 배열과 포인터. 둘 모두 포인터로써 동작가능하다.

 

C언어에서는 타입을 출력하는 기능이 없기에 printf 의 경고 를 이용해서, 해당 함수의 인자가 어떤 타입으로 설정되어있는지 알아보자.

 

2차원 배열의 특정 행인  arr[1] 의 경우  int* 타입으로 인자가 설정되어있다고 컴파일러가 말한다.

 

arr[0] 을 인자로써 함수에 넘겨줄때, int* 타입으로 전달되지만,,,,  이미 내부적으로(어셈블리) arr[0] 가 3의 길이를 가짐을 알고있다.

그러니까 포인터로 전달되더라도, 길이를 명시할 필요가 전혀 없다.

 

같은 맥락으로 이차원 배열인 arr 의 경우 길이 정보를 표현된 int (*)[3] 타입으로 인자가 설정되어 있다고 컴파일러가 말한다.. 

arr 만 전달되면 그 열의 길이가 몇인지 내부적(어셈블리적으로)으로 알 수 없다. 

(행의 길이는 알 수 있다. arr[0] 이 전달될 때와 마찬가지로..)
따라서 열의 길이를 반드시 함수 정의시 인자에 명시해주어야 한다.

 

그러므로 arr 이 인자로써 함수에 전달될 때는, arr[0] 일 때와 경우가 다르게,

int** 로 전달되는 것이 아니라 반드시 열의 길이를 명시하여 int (*)[3] 으로 전달되어야 한다.

C++ 에서의 VLA

그런데 C++ 의 표준은 가변길이 배열을 지원하지 않는다.

따라서 가변길이배열을 함수의 인자로 사용할시 컴파일 에러가 발생할 수 있다.

 

파훼하려고 많은 노력을 하였으나, 컴파일러를 만들 때 에러가 발생하도록 한 이유가 있다고 생각하고 포기했다.

애초에 이중포인터로 동적2차원 배열을 만들던가, 아니면 vector 를 사용해야 한다.

 

아래는 vector 의 예시이다.

 

댓글