ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Pointer] 배열과 포인터
    개발/C++ 2021. 4. 14. 21:36

    타입과 Decay

    배열의 이름은 상수 포인터처럼 사용할 수 있지만 엄밀히 말해 그 둘이 같다고 보기는 어렵습니다. 애초에 타입부터 다르기 때문입니다. C++에서는 typeid라는 연산자를 이용해서 변수의 타입을 확인할 수 있습니다.

    #include <iostream>
    using namespace std;
    
    int main()
    {
    	int nums[4] = { 1,2,3,4 };
    	int num0 = 0;
    	int* pNum = &num0;
    	cout << typeid(nums).name() << endl;
    	cout << typeid(pNum).name() << endl;
    }

    배열을 함수의 인자로 넘길 때 매개변수를 포인터로 정의하지 않고 배열로 선언해도 마찬가지입니다. 배열일 때는 size를 구할 수 있지만 포인터로 변환되면 배열의 크기 정보를 알 수 없습니다. 이를 decay라고 합니다. 

    #include <iostream>
    using namespace std;
    
    void func(int arr[])
    {
    	cout << typeid(arr).name() << endl;
    	cout << sizeof arr << endl;
    }
    
    int main(int c, char* v[])
    {
    	int nums[4] = { 1,2,3,4 };
    	cout << typeid(nums).name() << endl;
    	cout << sizeof nums << endl;
    	cout << endl;
    	func(nums);
    }

    정적인 배열을 사용할 때는 std::array 클래스를 사용하면 decay를 막을 수 있고 안전하게 배열을 사용할 수 있습니다. 정적으로 선언한 배열에서 배열의 크기보다 큰 인덱스로 접근해 출력을 해보면 쓰레기 값이 출력됩니다. 정의되지 않은 동작이지만 오류로 판단하지 않는 것입니다. std::array 클래스의 at() 함수를 사용하면 오류를 발생시켜 줍니다. Release 모드에서는 아예 아무 것도 출력해주지 않습니다. 해당 인덱스에 접근하지 않는 것입니다.

    #include <iostream>
    #include <array>
    using namespace std;
    
    int main()
    {
    	array<int, 4> arr{ 1,2,3,4 };
    	int nums[4] = { 1,2,3,4 };
    	cout << nums[4] << endl;
    	cout << arr.at(4) << endl;
    }

    Debug 모드
    Release 모드

    이차원 배열과 이중 포인터

    모든 이차원 배열이 이중 포인터일까요? 다음 코드를 봅시다.

    #include <iostream>
    using namespace std;
    
    
    void funcChar(int c, const char **arr)
    // void funcChar(int c, const char *arr[]) // ok
    {
    	for (int i = 0; i < c; ++i)
    	{
    		cout << arr[i] << endl;
    	}
    }
    
    int main()
    {
    	const char *arr[2] = { "abc", "def" };
    	funcChar(2, arr);
    }

    main()에서 선언한 arr 변수는 배열의 포인터입니다([]가 *보다 우선 순위가 높습니다. ). 각 요소에 문자열의 시작 주소를 넣습니다. 이것이 이중 포인터입니다. 다음은 비슷한 듯 조금 다릅니다.

    #include <iostream>
    using namespace std;
    
    void funcChar2(int c, char (*charPtr)[5])
    // void funcChar2(int c, char **charPtr) // error
    {
    	for (int i = 0; i < c; ++i)
    	{
    		cout << charPtr[i] << endl;
    	}
    }
    
    int main()
    {
    	char strs[][5] = { "abcd", "efg" };
    	funcChar2(2, strs);
    }

    이차원 배열로 선언한 strs 변수는 각 요소에 문자열 자체를 갖고 있습니다 "abcd" | "efg" 이렇게요. 문자열의 배열입니다. 일부러 컴파일 오류를 내서 확인해보면 strs 변수가 (*)[5] 형식이라고 뜹니다. funcChar2()에서 이중 포인터로 받을 수 없고 (*charPtr)[5] 이렇게 포인터의 배열로 받아야 합니다.

     

    다음 코드를 디버깅해서 조사식으로 살펴보면 str1은 시작 주소를 담기에 H만 표현하고 str2에는 모든 인덱스에 Hello가 들어가있는 모습을 확인할 수 있습니다. 이 차이에서 비롯됐습니다.

    #include <iostream>
    using namespace std;
    
    int main()
    {
    	const char* str1 = "Hello";
    	char str2[] = "Hello";
    }

    댓글

Designed by Tistory.