오래된 Computer Language인 c언어는 다른 여러 언어들에 비해 그 쓰임새가 부족하다라고 느끼는 경우가 많다.
그런 이유는 기본적으로 지원이 안되는 것이 많기 때문이다.
하지만 이러한 c언어에서 상상한 것을 구현하게 도와주는 것이 있었으니 Pointer라는 개념이다. Pointer는 프로그램에서 또다른 variable(memory location)으로 참조한다. 그 과정에서 아래의 여러 기능들을 수행할 수 있다.
- function(reference parameters)으로 들어오는 값들을 수정할 수 있다.
- group (such as an array)의 특정 member를 기억하는데 사용할 수 있다.
- 동적(dynamic) 메모리 할당(특히 어레이)에 사용할 수 있다.
- 복잡한 data structure를 구축하는 데에 사용할 수 있다. (linked lists, stacks, queues, trees, etc.)
Pointers - 목차
Basics
- Variable declaration, initialization, NULL pointer
- & (address) operator, * (indirection) operator
- Pointer parameters, return values
- Casting points, void *
Arrays and pointers
- 1D array and simple pointer
- Passing as parameter
Dynamic memory allocation
- calloc, free, malloc, realloc
- Dynamic 2D array allocation (and non-square arrays)
위와 같은 목차로 설명을 이어나가 보도록 하겠습니다.
Pointer Basics
- Variables는 Computer Memory의 addresses에 할당됩니다. (address depends on computer/operating system)
- Variable의 이름은 memory address에 대한 참조입니다.
- A pointer variable는 또다른 variable의 address가 표시됩니다. (P는 아래의 사진에서 pointer variable임.):
Pointer Variable Definition
Basic syntax: Type *Name
Examples:
int *P; /* P는 int variable로 point할 수 있는 variable입니다. */
float *Q; /* Q is a float pointer */
char *R; /* R is a char pointer */
Complex example:
int *AP[5]; /* AP는 int 타입의 5개의 pointers로 구성된 배열입니다. */
- c언어는 타언어에 비해서 좋게 받아들여진 이유가 뭐냐면, 일관된 syntax를 가졌다고 하기 때문이다. 하지만 그 중에서도 비판받는 부분이 무엇이나면 Array Pointer부분이다. 규칙성이 없기 때문에 외우는 수밖에 없다.
Address (&) Operator
The address (&) operator는 C언어에서 어느 variable 객체의 앞에서 사용될 수 있습니다. name으로부터 memory를 찾아가는 과정인데 , 주소값을 가져오는 것을 일컫는다.
-- 연산의 결과는 variable의 memory에 위치합니다.
Syntax: &VariableReference
Examples:
int V;
int *P;
int A[5]; // 만들어낸 변수는 A[0], A[1], ..., A[4]이지만, 사실은 int *A라는 int pointer로 선언된 것이다.
- &V - integer variable V의 memory location
- &(A[2]) - array A의 2번째 array element(A[2])의 memory location ≒ 「A + 2」 와 같이 표현할 수 있다.
- &P - pointer variable P의 memory location
A = &(A[0])이 표현은 맞지만, A[0]은 int 타입이다. A라는 *(pointer)는 제일 처음 주소값을 갖는다.
Pointer Variable Initialization/Assignment
NULL
- 개발자들이 c언어를 구현할 때 존재하지 않는 주소에 대한 표현이 필요했고 그에 따라 invalid한 값임을 나타내기 위하여 NULL pointer를 사용하기 시작했다. 아무것도 아닌 값(주로 0)을 가르키며, function을 사용하는 가운데 NULL pointer가 parameter로 도착하면 error를 표출한다.
NULL에 pointer variable를 초기화/할당하거나 Address(&)operator를 사용하여 variable의 address를 가져올 수 있습니다.
- address operator의 variable은 pointer에 대해 올바른 type이어야 합니다.
- integer pointer는 integer variable에서만 참조(point)된다.
Examples:
int V;
int *P = &V; //pointer variable P에 integer variable V의 address를 할당
int A[5];
P = &(A[2]); //pointer variable P에 array A의 2번째 element(A[2])의 address로 update
Indirection (*) Operator
- pointer variable에 memory address가 포함되어 있습니다.
- pointer가 가르키는(point) variable의 내용을 참조하기 위해, indirection operator를 사용한다.
Syntax: *PointerVariable
Example:
int V = 101;
int *P = &V;
/* Then *P would refer to the contents of the variable V (in this case, the integer 101) */
printf(“%d”,*P); /* Prints 101 */
여기서 indirection operator는 printf("%d", *P); 부분을 말하는 것이며, poiner variable P가 참조하는 내용, V 값을 참조할 수 있다.
Pointer Sample
int A = 3;
int B;
int *P = &A;
int *Q = P;
int *R = &B;
printf(“Enter value:“);
scanf(“%d”,R);
printf(“A:%d \t B:%d\n”, A, B);
printf(“*P:%d \t *Q:%d \t *R:%d\n”, *P, *Q, *R);
Q = &B;
if (P == Q)
printf(“1\n”);
if (Q == R)
printf(“2\n”);
if (*P == *Q)
printf(“3\n”);
if (*Q == *R)
printf(“4\n”);
if (*P == *R)
printf(“5\n”);
해당 예제를 실행하면 다음과 같은 결과를 얻을 수 있다.
Reference Parameters
- function이 끝난 후 존재하는 variable을 변경하기 위해, function으로 variable(pointer)의 address를 전달한다.
- 그런 다음 function 내부의 indirection operator를 사용하여 parameter가 가리키는 값을 변경한다:
void changeVar(float *cvar) {
*cvar = *cvar + 10.0;
}
float X = 5.0;
changeVar(&X);
printf(“%.1f\n”,X);
보통 function의 parameter로 들어온 경우(call by value) 해당 variable의 값을 사용하는 것이기 때문에 값을 변경하더라도 function 안에서만 값이 변경되고 function을 end하고 나왔을 경우 parameter로 들어갔던 variable의 값은 들어가기 전과 동일하다.
하지만 float *cvar와 같이 Call by reference로 parameter를 받게되면 function 밖에서도 변경된 값을 그대로 이어갈 수 있다. 해당 pointer variable이 가르키는(pointer) variable의 값을 reference를 하기 때문이다. 이러한 방법을 이용하여 return type이 아닌 parameter로 들어온 variable의 값을 유동적으로 변경할 수 있다.
Pointer Return Values
A function can also return a pointer value:
float *findMax(float A[], int N) {
int I;
float *theMax = &(A[0]);
for (I = 1; I < N; I++)
if (A[I] > *theMax) theMax = &(A[I]);
return theMax;
}
int main() {
float A[5] = {0.0, 3.0, 1.5, 2.0, 4.1};
float *maxA;
maxA = findMax(A,5);
*maxA = *maxA + 1.0;
printf("%.1f %.1f\n",*maxA,A[4]);
return 0 ;
}
위와 같이 function의 return value를 pointer로 선언하여 사용할 수도 있으며, 결과값은 다음과 같다:
Pointers to Pointers
pointer는 또한 pointer variable을 가르키게(point) 만들 수 있다.(하지만 pointer는 pointer variablre을 가르키게 허락되는 type이어야한다. - double pointer이어야 한다.)
int V = 101;
int *P = &V; /* P points to int V */
int **Q = &P; /* Q points to int pointer P */
printf(“%d %d %d\n”,V,*P,**Q); /* prints 101 3 times */
위와 같이 다 같은 101을 point하는 것을 볼 수 있었다.
Casting Pointers
어느 type의 variable의 memory address를 또 다른 type을 가리키는 포인터에 할당하는 경우 cast operator를 사용하여 cast가 의도적이었다는 것을 표시하는 것이 가장 좋습니다 (이렇게 하면 warning이 제거됨).
Example:
int V = 101;
float *P = (float *) &V; /* Casts int address to float * */
warning을 제거하였더라도 여전히 안전하지 않은 작업이긴 합니다. 위와 같은 작업은 원하는 출력값을 얻지 못할 수도 있습니다.
(float *)와 같이 원래의 변수가 아닌 값을 지정해주는 것을 cast해준다고 합니다.
The General (void) Pointer
- A void * 는 general pointer로 간주됩니다.
- (또 다른 pointer type으로)void * 에서 혹은 void *으로 address를 할당하는 데 cast가 필요하지 않습니다.
Example:
int V = 101;
void *G = &V; /* No warning */
float *P = G; /* No warning, still not safe */
특정 library function가 void * 결과를 반환함(더 나중에)
오늘은 Pointer에 대해서 조금 알아보았습니다.
예정에 비해서 길어진 관계로 part를 나누어 작성하도록 하겠습니다.
part 2 링크