[Syntax, C++] Array에서의 Pointer

2023. 3. 21. 17:38·Computer Science and Engineering/OOP (Object Oriented Programming)
728x90

Array와 Pointer의 관계

 

메모리 주소에 직접적으로 접근할 수 있다는 장점이 있는 Pointer라는 개념은 Array와 밀접한 관련이 있다. 왜냐하면 Array는 메모리에 연속적으로 값을 저장하고, Array를 초기화한 변수 자체는 Array의 첫 번째 요소의 주소를 담고 있기 때문이다. 다음 예시를 통해 Array와 Pointer의 밀접한 관계를 이해할 수 있겠다.

#include <iostream>

using namespace std;

int main()
{
	int x[] = { 2, 3, 4, 5 };
    cout << x << endl // 0x7ffca35d1f10
    	<< &x[0] << endl // 0x7ffca35d1f10
        << &x[1] << endl // 0x7ffca35d1f14
        << &x[2] << endl // 0x7ffca35d1f18
        << &x[3] << endl // 0x7ffca35d1f1c
    return 0;
}

x 자체로는 x의 첫 번째 요소의 메모리 주소를 의미한다. 따라서 x와 &x[0]을 출력하면 같은 값이 나오는 것을 알 수 있다.

또 바로 옆 요소는 배열이 연속적으로 메모리에 저장되므로, int의 크기인 4 bytes만큼 차이가 나는 것을 알 수 있다.

 

x[3] = 4;
*(x+3) = 4;

이어서 해당 코드를 살펴보자. x는 그 자체로 x의 첫 번째 요소의 메모리 주소를 담고 있고, 거기에 +3을 하게 되면 해당 값만큼 메모리 주소를 옆으로 이동한다. 이어서 그 값을 참조한 *(x+3)은 역시 4를 나타낸다. 따라서 해당 두 코드는 같은 의미를 담고 있다.

 

int x[] = { 3, 4, 5, 6, 7 };
int* y = x + 2;
int* z = y - 1;
cout << z[3] << endl;

한 단계 더 나아가보자. 이번에는 좀 더 생각해 볼게 많아 보인다. 먼저 y는 x의 첫 번째 요소의 메모리 주소보다 두 칸 더 이동한 녀석, 즉 x[2]의 메모리 주소를 담고 있다. 다음으로 z는 x[2]에서 왼쪽으로 한 칸 이동한 녀석, 즉 x[1]의 메모리 주소를 담고 있다. 그렇다면 z[3]은 뭘까? z의 입장에서는 x의 2번째 요소가 자신에게는 첫 번째 요소이므로, z[3]은 x[1]에서 3칸 더 이동한 x[4]를 가리키고, 따라서 7을 출력하게 된다.

 

Arrays cannot be passed to arrays

 

배열은 다른 배열로 보낼 수 없다라는 말은 무슨 말일까? 말 그대로다. 배열을 어떤 함수의 매개변수로 전달할 수도 없고, 다른 배열에 할당하는 것이 불가능하다는 것. 이때 필요한 것이 Pointer다. 앞서 말했듯 배열의 변수명은 그 자체로 배열의 첫 번째 요소의 메모리 주소를 담고 있기에, 포인터 변수를 매개변수로 하여 배열을 할당하거나 함수를 사용할 수 있다. 해당 예시를 살펴보자.

 

int foo(int* x) {
	return x[0] + x[1];
}

int main() {
	int z[] = { 3, 4, 5 };
    cout << foo(z) << endl;
    return 0;
}

foo라는 함수는 int형의 포인터 변수가 매개변수이다. 따라서 해당 위치에는 어떤 메모리 주소가 들어가야 한다. main함수에서 foo(z)을 출력하였는데, z는 z배열의 첫 번째 요소의 메모리 주소가 해당된다. 따라서 x[0]는 3, x[1]은 4이므로 출력값은 7이 나오게 된다.

 

Passing in the size of the array

 

배열의 값을 전달할 때 배열의 첫번째 요소의 메모리 주소를 보내는 것만으로는 부족하다. 위와 같은 예시에서 만약 z의 요소가 하나밖에 없다면, foo함수에서 x[1]에 해당하는 값이 없으므로 에러가 발생할 것이다. 따라서 우리는 배열을 보내줄 때에 배열의 크기도 같이 보내주는 방식을 이용해야 한다. 해당 예시를 살펴보자.

 

double sum(double* vals, size_t n) {
	double s = 0;
    for (size_t i = 0; i < n; i ++) {
    	s += vals[i];
    }
    return s;
}

int main() {
	double x[] = { 7e2, 3.1e-5, -6 };
    cout << sum(x, sizeof x / sizeof x[0]) << endl;
    return 0;
}

sizeof는 해당 변수의 메모리 크기를 의미한다. 요소가 3개인 x의 크기는 24bytes이고, 요소 하나의 크기는 8 bytes이므로 배열의 요소 개수는 3이 나오게 된다. 따라서 sum함수는 x의 첫 번째 요소의 메모리 주소와 크기를 받아 오버플로우 에러를 내지 않고 안전하게 연산을 수행할 수 있게 된다.

 

Changing Value vs Memory Address

 

어떤 배열이 있고, 이 배열의 최솟값과 총합을 구하는 문제가 있다고 생각해보자. 총합을 먼저 구해야 하는 경우에는 상관없겠지만, 최솟값을 먼저 구해야 하는 상황이라면 조심해야 할 것이다. 해당 예시를 살펴보자.

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    long prices[] = {5000, 3500, 15000, 30000, 8000};
    long *smallest = &prices[0];
    for (size_t i = 0; i < sizeof prices / sizeof prices[0]; i++)
    {
        if (prices[i] < *smallest)
            *smallest = prices[i];
    }

    cout << "Your cheapest item had a price of " << *smallest << endl;

    long total = 0;
    for (size_t i = 0; i < sizeof prices / sizeof prices[0]; i++)
    {
        cout << setw(10) << prices[i] << '\n';
        total += prices[i];
    }

    cout << "Total: " << total << endl;
    return 0;
}

smallest는 prices배열 첫 번째 요소의 메모리 주소를 담는다. 반복문을 돌리면서 smallest가 참조하는 값을 더 작은 값으로 할당시켜주며 최솟값을 찾아내는 방식이다. 해당 방식을 이용한다면 최솟값을 구하는 데에는 문제가 없겠지만 해당 메모리 주소의 값이 실질적으로 변경이 되므로, 총합을 구할 때에는 prices = { 3500, 3500, 15000, 30000, 8000 }으로 첫 번째 요소의 값이 변경되어 다른 값을 출력하게 될 것이다. 따라서 우리는 smallest가 참조하는 값을 변경시키는 것이 아니라, smallest의 메모리 주소를 prices 최솟값의 메모리 주소로 바꿔주고, 참조를 통해 출력해줘야 한다.

 

long prices[] = {5000, 3500, 15000, 30000, 8000};
long *smallest = &prices[0];
for (size_t i = 0; i < sizeof prices / sizeof prices[0]; i++)
    {
        if (prices[i] < *smallest)
            smallest = &prices[i];
    }
cout << "Your cheapest item had a price of " << *smallest << endl;
728x90
저작자표시 (새창열림)

'Computer Science and Engineering > OOP (Object Oriented Programming)' 카테고리의 다른 글

[Syntax, C++] struct, enum, union을 활용하여 계산기 구현하기  (2) 2023.05.02
[Syntax, C++] struct를 활용하여 다각형의 둘레의 길이 구하기  (1) 2023.04.27
[Syntax, C++] struct, enum class를 활용하여 날짜(D - day) 계산하기  (0) 2023.04.27
[Syntax, C++] 입력값과 선언한 자료형이 다를 때는 어떻게 처리해야 할까? (with simple I/O, ignore and clea  (0) 2023.03.14
[Syntax, C] - 포인터(Pointer)란?  (1) 2023.03.08
'Computer Science and Engineering/OOP (Object Oriented Programming)' 카테고리의 다른 글
  • [Syntax, C++] struct를 활용하여 다각형의 둘레의 길이 구하기
  • [Syntax, C++] struct, enum class를 활용하여 날짜(D - day) 계산하기
  • [Syntax, C++] 입력값과 선언한 자료형이 다를 때는 어떻게 처리해야 할까? (with simple I/O, ignore and clea
  • [Syntax, C] - 포인터(Pointer)란?
100두산
100두산
출발하게 만드는 힘이 동기라면, 계속 나아가게 만드는 힘은 습관이다.
  • 100두산
    정상에서 보자 ✈️
    100두산
  • 전체
    오늘
    어제
    • 분류 전체보기 (127)
      • Life (6)
        • living (1)
      • Research (6)
      • AI (20)
      • Dev (45)
        • iOS (28)
        • Web (4)
        • flutter (9)
        • etc (4)
      • PS (Problem Solving) (23)
      • Computer Science and Engine.. (21)
        • Data Structures and Algorit.. (13)
        • OOP (Object Oriented Progra.. (8)
      • etc (5)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글쓰기
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    AI
    Python
    c++
    오블완
    SKT
    TIP
    파이썬
    티스토리챌린지
    알고리즘
    자료구조
    Challenger
    백트래킹
    SKTelecom
    BOJ
    백준
    ios
    swift
    xcode
    PS
    D3
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
100두산
[Syntax, C++] Array에서의 Pointer
상단으로

티스토리툴바