티스토리 뷰
c언어를 공부하는 사람들이 제일 어려워 하는 부분이 바로 포인터입니다. 저 또한 포인터를 공부하는게 제일 힘들었습니다. 오늘은 이렇게 어려운 c언어 포인터에 대해 알아보겠습니다.
포인터를 얼마나 확실하게 이해하고 효율적으로 사용할 수 있는가로 초보와 전문 c언어 프로그래머를 구분할 수 있습니다. 포인터는 c언어 전반에 많은 영향을 미치며, 언어 자체에 많은 유연함을 제공합니다. 포인터는 동적 메모리 할당의 매우 중요한 부분이기도 하며, 배열의 표기법과 밀접한 관련이 있습니다. 그리고 포인터가 함수와 함게 사용될 때 프로그램의 흐름을 제어하는 또 다른 차원의 기능을 제공합니다.
오랫동안 포인터는 c언어 학습에 걸림돌이 되어 왔습니다. 포인터의 기본적인 개념은 매우 간단합니다. 포인터는 메모리 위치의 주소를 정장하는 변수입니다. 하지만 포인터 연산자를 사용하고, 암호와도 같은 난해한 표기법을 식별하려고 할 때부터 이 개념이 급격하게 복잡해집니다. 하지만 그렇게 복잡하게 생각할 필요는 없습니다. 간단한 것부터 시작하여 기초를 확실하게 다져 놓으면, 포인터의 고급 사용 기법 또한 어렵지 않게 따라 하고 적용할 수 있습니다.
포인터를 이해하는 열쇠는 c언어 프로그램에서 메모리가 어떻게 관리되는지 이해하는 데 있습니다. 결국, 포인터는 메모리의 주소를 담고 있기 때문입니다. 메모리가 구성되고 관리되는 방법을 이해하지 못한다면 포인터의 동작 방식을 이해하기 쉽지 않습니다. 이런 우려를 해소하기 위해, 포인터의 개념을 설명하는 데 필요하다면 어디서든 메모리의 구성에 대해 설명할 것입니다. 일단, 메모리와 그 구성 방법을 확실하게 이해하고 나면 포인터를 이해하기가 훨씬 쉽습니다.
컴파일된 c언어 프로그램은 세 종류의 메모리를 사용합니다.
첫번째로 정적/전역 영역입니다. 정적으로 선언된 변수들은 정적/전역 메모리에 할당됩니다. 전역 변수를 또한 같은 메모리 공간에 할당됩니다. 정적/전역 변수들은 프로그램이 시작될 때 할당되며, 프로그램이 종료될 때까지 메모리 공간에 남아 있습니다. 모든 함수에서 접근할 수 있는 전역 변수와는 달리 정적 변수의 접근 범위는 해당 변수를 선언한 함수로 제한됩니다
두번째로 자동/로컬 영역입니다. 자동 변수는 함수 안에서 선언되고 함수가 호출될 때 생성됩니다. 자동 변수의 접근 범위는 선언된 함수로 제한되며, 함수가 호출되는 동안에만 존재합니다.
세번째로 동적 영역입니다. 동적 메모리는 힙 메모리 영역에 할당되고 필요한 경우 해제됩니다. 포인터를 사용하여 할당된 메모리 영역을 참조하며, 포인터에 의해 접근이 제한됩니다. 메모리를 해제하지 않는 한 메모리에 존재합니다.
메모리의 종류를 이해하는 것이 포인터의 동작을 이해하는 데 도움이 되며, 대부분 포인터는 이러한 메모리의 데이터를 조작하는 데 사용됩니다. 메모리가 분할되고 구성되는 방법을 이해하는 것은 포인터가 메모리를 다루는 방법을 정확히 이해하는 데 도움이 됩니다.
포인터 변수는 다른 변수 및 객체 또는 함수의 메모리상 주소를 포함하고 있습니다. 여기서 객체란, malloc 함수 같은 메모리 할당 함수를 사용하여 할당된 메모리를 말합니다. 포인터는 일반적으로 "문자 타입 포인터"와 같이 포인터가 가리킬 대상에 따라 특정 타입도 될 수 있습니다. 그러나 포인터 자체는 포인터가 참조하는 데이터 타입의 속성을 가지지 않습니다. 포인터는 단지 주소만을 가지고 있습니다.
그렇다면 포인터를 잘 알아야 하는 이유는 무엇일까요? 포인터는 보통 다음과 같은 용도로 사용됩니다.
먼저 빠르고 효율적인 코드를 작성할 수 있게 해줍니다.
다양한 문제에 대한 효과적인 해결 방법을 제공합니다.
동적 메모리 할당을 지원합니다.
작고 간결한 표현의 사용을 가능하게 해줍니다.
큰 오버헤드 없이 데이터 구조를 포인터로 전달할 수 있게 해줍니다.
함수의 매개변수로 전달된 데이터를 보호해줍니다.
포인터는 하드웨어의 구조에 좀 더 가까우므로 빠르고 효율적인 코드의 작성이 가능합니다. 다른 말로 하면, 컴팡일러는 좀 더 쉽게 포인터의 동작을 머신 코드로 변환할 수 있습니다. 포인터는 다른 연산자에 비해 발생하는 오버헤드가 적습니다.
많은 데이터 구조들은 포인터를 사용하면 더욱 쉽게 구현됩니다. 예를 들어, 연결 리스트는 배열 또는 포인터를 사용하여 구현될 수 있지만, 포인터를 사용하여 구현하기가 좀 더 쉽고 다음 연결 또는 이전 연결을 포인터로 직접 지정할 수 있습니다. 배열을 사용하여 데이터 구조를 구현할 경우 포인터의 역할을 배열의 인덱스로 대체할 수 있지만, 포인터만큼 직관적이거나 유연하지 못합니다.
포인터의 표현 방식은 배열보다 더 명확하고 유연합니다. 배열의 크기는 일반적으로 배열이 생성될 때 결정되어 배열이 저장할 수 있는 요소의 수에 제약이 발생합니다. 포인터를 사용하면 이러한 제약이 생기지 않으며, 새로운 노드가 필요할 때 동적으로 할당할 수 있습니다.
c언어에서 동적 메모리 할당은 포인터를 사용하여 좀 더 효과적으로 사용할 수 있습니다. 동적 메모리를 할당하고 해제하는 데 malloc과 free 함수가 각각 사용됩니다. 동적 메모리 할당은 크기 조절이 가능한 배열이나 연결 리스트와 큐 같은 데이터 구조의 구현에 사용됩니다. 하지만 최신 c 표준인 c11에서 이미 가변 배열을 지원하고 있습니다.
포인터를 사용한 간결한 표현은 매우 기술적인 표현이면서도 많은 프로그래머가 제대로 이해할 수 없는 매우 난해한 표현이 되기도 합니다. 목적과 상황에 맞는 포인터를 사용해야 하며, 단지 어렵기만 한 코드를 짜기 위해 포인터를 쓰지 않도록 해야 합니다. 예를 들어, 다음 코드는 names 배열의 두 번째 요소의 세 번째 문자를 printf 함수를 사용하여 두 가지 방법으로 출력합니다. 두 가지 접근 방식은 결국 같은 결과를 출력하지만, 배열 표기법을 사용한 접근 방식이더 단순하고 이해하기 쉽습니다.
char *name[] = {"Miller", "Jones", "Anderson"};
printf("%c\n", *(*(names+1)+2));
printf("%c\n", names[1][2]);
포인터는 애플리케이션을 만들고 개선하는 강력한 도구이지만, 포인터를 이용할 때 다음과 같은 다양한 문제가 발생할 수 있습니다.
배열이나 데이터 구조의 경계를 넘는 접근을 할 수 있습니다.
소멸한 자동/로컬 변수를 참조 할 수 있습니다.
할당 해제된 힙 메모리를 참조 할 수 있습니다.
아직 할당되지 않은 포인터에 대해 역참조 할 수 있습니다.
포인터 사용법의 구문과 의미는 c언어 표준 문서에 매우 자세히 설명되어 있습니다. 그러나 표준 문서가 포인터의 동작을 명확히 정의하지 못하는 경우가 있습니다. 이럴 때 표준 문서는 포인터의 동작을 아래와 같이 정의합니다.
첫번째로 구현 방법에 따라 정의된 행동입니다. 이것은 동작에 대한 문서화된 구현을 제공합니다. 구현 방법에 따라 정의된 행동의 예로, 정수에 대한 오른쪽 시프트 연산에서 상위 비트의 확장 방법이 있습니다.
두번째로 명시되지 않은 행동입니다. 이것은 동작에 대한 구현을 제공하지만 문서화하지는 않는 것입니다. malloc 함수에 인자로 0을 주고, 실행할 때 메모리가 얼마나 할당되는가 하는 것이 명시되지 않은 행동의 예가 될 수 있습니다. 명시되지 않은 행동의 목록을 CERT Secure Coding Appendix DD에서 볼 수 있습니다.
세번째로 정의되지 않은 행동입니다. 이것은 포인터의 동작에 대한 어떠한 것도 강요하지 않으므로 어떠한 동작도 발생할 수 있습니다. 이 경우의 예로, free 함수에 의해 해제된 포인터의 값이 있습니다. 정의되지 않은 행동의 목록은 CERT Secure Coding Appendix CC에서 찾을 수 있습니다.
로케일에 따라 다른 동작도 있습니다. 이런 동작은 일반적으로 컴파일러 제조사가 문서화합니다. 로케일에 따라 정의된 동작은 컴파일러 제작자가 더 효율적인 코드를 생성할 수 있는 자율성을 부여합니다.
여기까지 c언어 포인터에 대해 알아보았습니다. c언어 포인터에 대한 깊이 있는 내용은 아니었지만 c언어 포인터가 어떠한 개념인지만 잡으셔도 c언어 포인터를 공부하시는데 도움이 되실거라 생각합니다. 이 글을 읽으시는 분들도 이 어려운 c언어 포인터의 난관을 넘어서 c언어를 정복했으면 좋겠습니다.
'IT' 카테고리의 다른 글
리니지 서큐버스 서버가 오픈됩니다. (0) | 2016.09.30 |
---|---|
삼성 960 pro 발표 (0) | 2016.09.24 |
데이터통신 개요 (0) | 2016.09.22 |
오픈소스 소프트웨어 코드읽기 (0) | 2016.09.22 |
삼성 SM961 SSD 구매후기 및 사용후기 (0) | 2016.09.19 |