C언어의 정석, C언어의 재 정립
C언어 독학을 하시는 분들께는 처음부터 올바른 개념을 잡아드리고 C언어의 문법과 사용법, 현업에서 실제로 어떻게 쓰이는지 알고 싶은 분들께 제가 알고 있는 C에 관한 지식과 팁을 나눕니다.
잘 이해가지 않거나 아직 다루지 않은 개념에 대해서 카카오톡 오픈 채팅방에서 질문도 받습니다.
들어가기에 앞서 이전 글을 먼저 읽어 주세요.
2021/01/17 - [스터디/C방장] - 1. 컴퓨터 구조의 간단한 이해 [C언어 포인터 어려워도 제대로 이해하기]
1. 컴퓨터 구조의 간단한 이해 [C언어 포인터 어려워도 제대로 이해하기]
C언어의 정석, C언어의 재 정립 C언어 독학을 하시는 분들께는 처음부터 올바른 개념을 잡아드리고 C언어의 문법과 사용법, 현업에서 실제로 어떻게 쓰이는지 알고 싶은 분들께 제가 알고 있는 C
sw-daily.tistory.com
2.1 포인터의 본질, 기본 개념부터
■ 포인터란?
엄밀히 말해 '포인터'는 불완전한 말입니다. '포인터 타입'이라고 해야 완전한 의미를 갖는데 포인터 타입은 다른 변수를 가리키는 타입입니다.(포인터라고만 말하더라도 포인터 타입을 말하는 것이고 의미가 통합니다)
변수의 타입을 두 가지로 나누어 이해할 수 있습니다. 하나는 값 타입(value type)이고 나머지는 참조 타입(reference type)입니다. 값 타입은 int, double처럼 변수 안에 들어있는 값이 그 변수의 값 자체를 나타냅니다. 반면에 참조 타입 변수 안에 들어있는 값은 다른 변수를 참조합니다.
포인터(pointer)는 참조 타입(refence type)으로 다른 변수를 가리키는 변수입니다. 포인터 변수가 가리키는 변수의 타입(referenced type)에 따라서 불리는 이름이 다르게 됩니다. 예를 들어 int를 가리키는 변수 포인터는 int로의 포인터(pointer to int)라고 부릅니다.
그림 1. 을 보면 a, b 두 변수가 있습니다. 변수 a의 타입은 int 이고 10으로 초기화되었습니다. 변수 a 안에 들어있는 값은 10으로 그 값 자체를 나타냅니다. 변수 b의 타입은 int로의 포인터(pointer to int)입니다. 그리고 변수 b 안에 들어있는 값은 a의 주소를 나타냅니다.
※ tip
보통 pointer to int를 int 형 포인터라고 의역 하지만 여기서는 의도적으로 "int로의 포인터" 라고 직역하였습니다. 이것이 포인터를 더 직관적으로 이해하는데 도움을 주기 때문입니다.
예를 들어 int**** p라는 변수가 있다면 이것을 무엇이라고 불러야 할까요? int형 포인터형 포인터형 포인터형 포인터일까요? 맞는 말이지만 직관적이지 않습니다. "int***로의 포인터"라고 이해하면 조금 더 직관적으로 이해할 수 있습니다.
변수를 선언할 때 포인터를 나타내는 *의 위치를 int에 붙여 int*로 표현하였습니다. int* b, int *b, int * b 셋 모두 C언어 문법에 어긋나지 않고 완전히 같은 표현입니다. 그런데 변수의 이름은 b이고 이 변수의 타입은 int* 인 것을 가장 잘 나타내는 것은 int* b이기 때문에 의도적으로 int* b로 표현하였습니다. coding style은 정답이 없고 속해있는 팀에서 하나의 규칙을 갖는 것이 더 중요합니다.
■ 포인터 연산자
포인터 타입에에 사용되는 연산자를 알아보기 전에 연산자가 무엇인지 알아야 합니다. 연산자는 피연산자(operand)의 연산을 도와주고 연산 결과를 얻을 수 있게 해 줍니다. 피연산자의 개수에 따라 단항 연산자(unary operator), 이항 연산자(binary operator) 그리고 삼항 연산자(tenary operator)로 나뉩니다. 또, 피연산자의 종류와 연산 결과에 따라서 산술 연산자(arithmetic operator), 논리 연산자(logical operator), 비트 연산자(bitwise operator) 등으로 구분이 됩니다.
연산자에 대한 자세한 내용은 이번 포스팅에서는 다루지 않고 연산자를 이해하기 위해서는 피연산자와 연산 결과를 알아야 한다는 정도만 알고 넘어가겠습니다.
□ & operator 주소 연산자
& 연산자는 &operand 처럼 쓰이는 단항 연산자이고 operand의 주소(address of operand)라고 읽습니다.
& 연산자의 피 연산자는 변수가 올 수 있습니다.
피연산자의 타입에 따라 &연산자의 결과가 결정이 되는데, 만약 피연산자의 타입이 T라고 하면 &연산자의 결과 타입은 T로의 포인터(pointer to T)입니다.
※ tip
표준 C에서 & operator의 피연산자는 함수 지정자(function designator), [] 연산의 결과, * 연산 결과 또는 bit-field와 register 타입이 아닌 l-value 여야 합니다. 이 내용들을 자세히 다루기에는 내용이 길어지기 때문에 본문에서 & 연산자의 피연산자는 변수라고 하였습니다.
register 타입에 관한 내용은 아래 글을 참조 부탁드립니다.
2020/12/27 - [스터디/C방장] - 정수형 type 이야기 [C언어 파헤치기 1]
□ * operator 간접 참조 연산자
* 연산자는 *operand처럼 쓰이는 단항 연산자입니다. 보통 별 operand 또는 star operand라고 발음하는데 굳이 정확한 이름을 찾으면 inderect to operand 또는 dereference to operand 정도가 될 것 같습니다.
* 연산자의 피연산자는 항상 포인터 타입이어야 합니다.
* 연산자의 결과는 피연산자의 타입에 따라 결정되는데 피연산자가 pointer to T라면 * 연산 결과는 타입 T입니다. 피연산자가 lvalue로 사용되면 역참조 하여 값을 대입하고 그렇지 않으면 역참조 한 값을 반환합니다.
그림 3. 의 왼쪽 예시는 lvalue로 사용된 예입니다. pointer to int 타입의 변수 b에 a의 주소가 들어있습니다. 따라서 *b=20식 수행 이후 변수 a에는 20이 들어가 있게 됩니다.
그림 3. 의 오른쪽 예시는 피연산자가 값을 반환합니다. *b는 a의 주소를 역참조 하여 20을 반환하고 변수 c에 대입합니다.
※ tip
각 연산이 유효할 때 다음과 같은 몇 가지 팁들이 있습니다.
어떤 변수 E에 대해서 *&E는 E와 동치(equivalent)입니다. 그리고 *&E 또한 E와 동치입니다.
이 표현들은 가끔 유용하게 쓰일 때가 있습니다. 예를 들어 임베디드 시스템에서 특정 메모리 주소에 어떤 값을 넣고 싶은 경우라던가 const 변수의 값을 변경할 수 있습니다.
단, 임베디드 시스템처럼 메모리 접근이 자유로운 특수한 경우에만 가능하고 비주얼 스튜디오같은 범용 IDE에서는 운영체제가 메모리를 보호하기 때문에 아래 같은 방법 사용이 불가능할 수 있습니다.
예시 1) 0x20001000 번지에 0x1020을 대입하고 싶을 때
*(&((int)0x20001000))) = 0x1020;
0x20001000이라는 상수를 int로 형 변환하고 이것을 &연산자로 포인터로 형 변환 한 뒤 *연산자로 lvalue가 되고 0x20001000을 역참조하여 메모리 0x20001000 번지에 0x1020을 쓸 수 있습니다.
예시 2) const int a = 10;
어떤 이유로 const로 한정된 변수 a의 값을 바꾸고 싶을 때가 있습니다.
*((int *)&a) = 20;
위 코드를 작성하면 &a의 타입은 const int * 입니다. 그리고 이것을 int *로 강제 형변환시키고 *연산자로 역참조하여 const로 한정된 a의 값을 바꿀 수 있습니다.
그림 4. 는 visual studio에서 수행한 코드입니다. 실행 환경에 따라 runtime error가 발생할 수도 있고 위 결과처럼 20이 출력될 수도 있습니다.
const 관련 설명은 아래 글을 참고해 주세요.
2021/01/07 - [스터디/C방장] - 변수의 scope rule, lifetime / extern, static의 올바른 사용 방법
다음 포스팅에서는 C언어로 작성한 포인터가 실제 시스템에서 어떻게 동작하는지 살펴보겠습니다.
읽어주셔서 감사합니다.
도움이 되셨다면 즐겨찾기 등록하고 또 오세요 ★
함께 읽으면 좋은 글
2020/12/27 - [스터디/C방장] - 문자형/character type 이야기 [C언어 파헤치기 2]
'스터디 > C방장' 카테고리의 다른 글
1. 컴퓨터 구조의 간단한 이해 [C언어 포인터 어려워도 제대로 이해하기] (0) | 2021.01.17 |
---|---|
변수의 scope rule, lifetime / extern, static의 올바른 사용 방법 (0) | 2021.01.07 |
협업을 위한 코드, 읽기 쉬운 코드 작성하는 법. Top Down, 모듈화 (feat. 소수 구하기) (0) | 2021.01.03 |
floating type 사용 시 주의할 점. single/double precision. 메모리 표현 방식. floating type 이야기 [C언어 파헤치기 3] (0) | 2021.01.01 |
문자형/character type 이야기 [C언어 파헤치기 2] (0) | 2020.12.27 |