본문 바로가기
C │ C++ │ C#/C++

C++ Chapter 6.7 : 포인터와 정적 배열

by Pokaa 2023. 9. 28.
728x90
728x90
SMALL
포인터와 배열 이름은 일맥상통하는게 있다.

 

int array[5] = {0, 1, 2, 3, 4}; 
int * ptr = array;

배열의 이름 👉🏻 포인터 상수나 마찬가지. $($변하지 않는 주소 값$)$

  •  array 와  &array[0] 은 같다.
    ο  배열의 이름 👉🏻배열의 첫번째 원소의 주소
  •  *array 와  array[0] 은 같다.
    ο   *array  👉🏻 배열의 첫번째 원소를 간접 참조함.
  •  int * ptr = array; 
    ο  ptr 포인터 변수가 array 배열의 주소를 담게 되었으니 이제 ptr로 array 배열에 간접참조가 가능해짐.
    ο   *ptr  와  array[0] 은 같다.
    ο   *ptr = 100 은  array[0]=100 과 같다.
        ■  배열의 주소를 복사해 담은 포인터로 배열의 원소 값을 이렇게 바꿀 수도 있지만 char * 형 포인터에
               char [] 문자열 배열의 원소를 바꾸려는 시도는 불가능하다. [다다음 포스트 참고]

 

 

배열의 이름과 포인터의 차이

 

int array[5] = {0, 1, 2, 3, 4}; 
int * ptr = array;

sizeof(array) // 👉🏻 20
sizeof(ptr)) // 👉🏻 4
  • sizeof에 배열의 이름을 넣으면 배열의 총 메모리 크기를 알려주지만
  • sizeof에 포인터를 넣으면 포인터가 배열을 가리키고 있더라도 포인터의 크기만 알려준다.
    ο  포인터의 크기는 int * 든 double * 든 데이터 타입에 상관없이 4byte. $($64 bit 환경에선 8byte$)$

 

 

함수 파라미터로 배열을 넘길 때

 

void printArray(int * array)
{
    cout << sizeof(array) << endl;  
    // 지역변수 array에 배열의 이름이 들어오든 포인터가 들어오든 지역변수 array는 포인터이므로 언제나 4가 출력될 것. 
}

int main()
{
    int array[5] = {0, 1, 2, 3, 4}; 
    int * ptr = array;
    
    printArray(array);  // 배열의 주소가 printArray 함수 내부의 array 지역변수에 대입된다. 
    printArray(ptr); // 포인터 ptr 값이 printArray 함수의 지역변수 array에 대입된다.
}
  • 함수의 매개변수로 배열을 전달할 때는 포인터로서 전달 된다. $($주소만 복사되어 넘어감$)$
    ο  성능 상 이유로 매개변수 타입 자체를 배열로 하여 배열을 통째로 복사하여 넘기는 것은 금지되어 있다.
    ο  배열 크기가 클때는 배열 복사하는데 시간이 많이 소요되기 때문.
    ο  매개변수를 포인터로 선언해서 배열의 이름 $($= 배열의 첫번째 원소의 주소$)$만 복사하여 넘겨주면 
        포인터로 배열 원소에 접근할 수 있으므로 효율적이다.
        ■  어차피 배열 원소들은 연속된 메모리 공간으로 위치하므로 첫번째 원소 주소값 $($= 배열의 이름$)$만 
              알면 다른 원소들도 4 $($int 기준$)$ 씩만 더해주어 주소를 알아낼 수 있다.
  • 이처럼 배열의 주소를 포인터 매개변수로 넘길 땐 포인터 매개변수만으로는 배열의 크기를 알 수 없으므로 int len 형태로 배열의 크기를 직접 같이 넘겨주어야 한다.
void printArray(int * array, int len)

 

매개변수가 배열 같아 보여도 사실 포인터다.

 

void printArray(int array [5])
{
    cout << sizeof(array) << endl;   
}

int main()
{
    int array[5] = {0, 1, 2, 3, 4}; 
    int * ptr = array;
    
    printArray(array);  
    printArray(ptr); 
}

 void printArray(int array [5]) 

  • 매개변수를 배열로 받는 것 같아 보여도 사실 포인터다.
    ο  배열을 위한 포인터라고  [ ]  대괄호 기호를 써서 배열의 주소를 넘겨 받을 것임을 강조하는 것 뿐
    ο  오직 매개변수 타입에서만  * 를  [ ] 로 바꾸는 것이 가능하다.
        ■  더블포인터와 같이  * 가 여러개인 경우엔 하나만  [ ] 로 바꿀 수 있다.
void func(int ** pArr) // 가능
void func(int * pArr[]) // 가능
void func(int pArr[][]) // 불가능
  • 따라서  [ ] 기호 안의 숫자도 아무 의미 없다.
    실제로 3 크기의 배열을 넘기는 것이라도 매개변수에 [5]라고 되있다고 해서 오류가 발생하는 것도 아니다.
  • 프로그램에는 영향을 미치지 않고 단순 5 크기의 배열을 받을 것이라는 표시일뿐.

 

 

구조체 혹은 클래스의 멤버로 속한 배열을 매개변수로 넘길 때

 

이 경우엔 포인터로 전환되지 않고 배열 자체가 그대로 넘겨진다.

#include <iostream>
#include <cstring>

using namespace std;

struct Mystruct
{
	int array[5] = { 9, 7, 5, 3, 1 };
};

void doSomething(Mystruct * ms)
{
	cout << sizeof((*ms).array) << endl; // 4가 아닌 20 출력
}

int main()
{
    Mystruct ms;
    cout << ms.array[0] << endl;  // 9 출력
    cout << sizeof(ms.array) << endl; // 20 출력 (멤버가 array 배열 하나 뿐)

    doSomething(ms);

    return 0;
}
  • 배열이 구조체나 클래스의 멤버로 있을 땐
    ο  파라미터가 구조체 타입으로 받든 구조체 포인터로 받든
    ο  배열 그 자체가 넘어간다
        ■  위와 같이 배열의 주소가 포인터에 복사되어 넘어가는 것이 아닌!
728x90
300x250
LIST