C++/Syntax 2021. 3. 11. 16:48

배열 (Array)

상수 포인터, 포인터 상수는 무슨 의미인지는 알겠는데 사용하면 더 헷갈린다...


변수란 뭘까

#include <iostream>

int     main()
{
  using namespace std;

  int a = 1;

  cout << &a << endl;
}

/* stdout
00FAF990
*/
  • int a = 1을 정의하면 스택에 sizeof(int) 만큼 공간이 확보된다.

  • 여기서의 변수명 a는 단지 프로그래머가 메모리 공간에 접근할 때 필요한 것이다.

  • a라는 변수는 컴파일러가 기계어로 번역할 때 메모리 주소로 변환된다.

  • 즉 사람의 입장에서 메모리에 편하게 접근하려고 만든게 변수이고, 컴파일러가 처리하는 것이다.


배열 예제

#include <iostream>

int     main()
{
  using namespace std;

  int        arr[10]{ 0, 1, 2, 3, 4, 5, };

  cout << typeid(arr).name() << endl;     // int [10]
  cout << typeid(&arr).name() << endl;    // int (*)[10]
  cout << typeid(&arr[0]).name() << endl; // int *

  cout << sizeof(arr) << endl;            // 40
  cout << sizeof(&arr) << endl;           // 4
  cout << sizeof(&arr[0]) << endl;        // 4

  // 이 3개는 같은 주소를 가리킴
  cout << arr << endl;
  cout << &arr << endl;
  cout << &arr[0] << endl;
  // 배열 두 번째 요소의 주소
  cout << &arr[1] << endl;
  // 아래는 배열 전체의 다음 주소 (이렇게 사용하면 안됨)
  cout << (&arr)[1] << endl;
}

/* stdout
int [10]
int (*)[10]
int *
40
4
4
0118F840
0118F840
0118F840
0118F844
0118F868
*/
  • int arr[10]을 정의하면 스택에 sizeof(int) * 10 만큼 공간이 확보된다.

  • 배열의 이름인 arr은 사람이 편하게 접근할 수 있도록 컴파일러가 처리하는 것이다.

  • typeid().name()

    • 자료형을 표현

    • arr : int [10]

      • 배열 자료형이라고 생각하면 될 듯

      • 배열 자료형이라는 것은 들어본 적이 없으나, 내부적으로 이렇게 처리한다고 생각하는게 맞는 것 같다.

    • &arr : int (*)[10]

      • int [10] 의 배열을 가리키는 포인터

      • 이 주소에 1을 더하면 배열의 크기만큼 주소가 이동된다.

    • &arr[0] : int *, arr[0]를 찾아가서 주소를 찍은 것이므로 일반적인 포인터와 같음

  • javascript 의 경우 배열을 선언한 뒤 typeof 로 살펴보면 Object 라고 뜨는 것을 볼 수 있다.


에러 예제

int     main()
{
  int* p;
  int(*pa)[3];
  int arr[2][3] = { 3, };

  p = arr;        // cannot convert from 'int [2][3]' to 'int *'
  p = (int *)arr; // 이건 가능
  p = &arr;       // cannot convert from 'int (*)[2][3]' to 'int *'
  pa = arr;       // 가능
  pa = &arr;      // cannot convert from 'int (*)[2][3]' to 'int (*)[3]'

  int(*ppa)[2][3];

  ppa = arr;      // cannot convert from 'int [2][3]' to 'int (*)[2][3]'
  ppa = &arr;     // 가능

  int arr2[2];
  pa = arr2;      // cannot convert from 'int [2]' to 'int (*)[3]'

  int arr3[2][3][4];
  int*** ppp;
  ppp = arr3;     // cannot convert from 'int [2][3][4]' to 'int ***'
}
  • 7번째 줄 (p = arr;)
    • int [2][3] 이라는 2차원 배열 자료형을 int * 에 대입하려해서 오류 발생
  • 8번째 줄
    • 직접 형변환을 한 뒤 역참조를 해보면 값이 제대로 나오는 것을 확인할 수 있다.
    • int [2][3] 도 주소를 나타내지만, 묵시적 형변환이 작동하지 않는 경우임을 알 수 있다.
      • 비슷한 예로 int * 변수에 double * 변수 값을 대입하면, cannot convert from 'double *' to 'int *' 에러가 발생하는 것을 볼 수 있다.
  • 9번째 줄
    • &arrint (*)[2][3] 타입이라는 것은 int [2][3]의 배열을 가리키는 포인터임을 의미한다.
  • 10번째 줄
    • arrint [2][3] 타입이고, 묵시적 형변환을 통해 int (*)[3]으로 변환되었음을 볼 수 있다.

배열을 레퍼런스로 전달하는 예제

#include <iostream>

void    printElements(const int(&arr)[5])
{
  std::cout << arr << std::endl;
  for (int i = 0; i < 5; ++i)
    std::cout << arr[i] << " ";
  std::cout << std::endl;
}

int     main()
{
  using namespace std;

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

  std::cout << arr << std::endl;
  printElements(arr);    
}

/* stdout
00CFFC08
00CFFC08
1 2 3 4 5
*/

참고


std::array 컨테이너

'C++ > Syntax' 카테고리의 다른 글

C++ 문자열 (string)  (0) 2021.03.11
C++ nullptr_t  (0) 2021.03.11
C++ 반복문  (0) 2021.03.11
C++ switch  (0) 2021.03.11
C++ 제어 흐름 (Control Flow)  (0) 2021.03.11