본문 바로가기

프로그램언어/C언어

c언어 포인터

1. 포인터

포인터란 메모리의 주소를 저장하는 변수이다. 포인터 변수는 변수명 앞에 애스터리스크(*) 입력하여 표현한다. *를 간접 연산자라 합니다.

포인터 선언 

 자료형 *변수명 또는 자료형* 변수명

왜 포인터변수를 쓰는 것인가?
 1. 동적으로 메모리 할당을 하기 위해서이다.
 2.  함수에서 인수를 받아 다른 내용으로 갱신하기 위해서이다. 

int *p;
p : p에는 메모리가 참조된 주소가 저장 
*p : p가 가리키고 있는 주소 안의 값

예]
포인터 변수를 잘못 사용한 경우
char *ptr1                     // 전역 변수로 0으로 초기화됨
int main()
{
    double *ptr2;          // 지역 변수 0으로 초기화되지 않음
    *ptr2 = 3.14;          // 에러발생


    printf("%d\n", ptr1);
    printf("%f\n", *ptr2);

    return 0;
}
전역변수는 컴파일러가 0으로 초기화한다. 여기서 *ptr2에 어떤 값을 쓰면 시스템의 중요 자료인 인터럽트 벡터가 깔려있는 0번지에 자료를 쓰므로 운영체제는 자신을 보호하기 위해서 해당 프로세스를 강제 종료시킨다.

 

#include 

int main()
{
    int value = 10;
    int *ptr;
    ptr = &value;

    printf("value 값 = %d\n", value);         // 10
    printf("*ptr 값 = %d\n", *ptr);             // 10
    printf("value 주소 = %p\n", &value);    // 0033F9A4
    printf("ptr 값 = %p\n", ptr);               // 0033F9A4
    printf("&ptr 주소 = %p\n", &ptr);       // 0033F998

    *ptr = *ptr + 3;
    printf("*ptr 값 = %d\n", *ptr);          // 13
    printf("value 값 = %d\n", value);       // 13

    return 0;
}

 

증감 연산자를 사용한 여러가지 표현 방법

표현 방법 설명
*ptr + 1 ptr이 가리키고 있는 주소에 저장된 값을 1증가
*(ptr + 1) ptr이 갖고 있는 주소를 1증가 시킨 후 주소의 값을 구함
*ptr++ ptr이 가리키는 주소의 저장된 값을 구한 후 ptr를 1증가 시킴 ( ptr의 값은 변화가 없음 )
(*ptr)++ ptr이 가리키고 있는 주소의 저장된 값을 구한 다음 그 값에 1 중가시킴 (ptr의 값에 변화가 있음 )
*++ptr ptr를 1 중가시킨 다음 해당 주소에 있는 값을 구함
++*ptr ptr이 가리키고 있는 값을 구한 다음 그 값에 1을 더함

 

 

2. 포인터와 배열

배열은 연속된 메모리 공간을 차지하는 같은 자료형을 갖는 자료들의 집합이다. 반면 포인터는 주소를 내용으로 갖는다.

 

소스코드 소스코드

 char array[] = "Hello";

 char *ptr;

 ptr = array;

 

 for(int i=0; i<5; i++) {

     printf("*(ptr+%d) = %c\n", i, *(ptr+i));

 }

 int arr[5] = { 2,4,6,8,10 };
 int *ptr;

 ptr = arr;

 for (int i = 0; i < 5; i++) {
     printf("*(ptr+%d) = %d\n", i, *(ptr + i));
 }

실행 결과]

*(ptr+0) = H

*(ptr+1) = e

*(ptr+2) = l

*(ptr+3) = l

*(ptr+4) = o

실행 결과]

*(ptr+0) = 2

*(ptr+1) = 4

*(ptr+2) = 6

*(ptr+3) = 8

*(ptr+4) = 10

 

소스코드 실행 결과
 const char *ptr = "Hello";

 for (int i = 0; i < 5; i++) {
     printf("*(ptr+%d) = %c\n", i, *(ptr + i));
 }

 *(ptr+0) = H

 *(ptr+1) = e

 *(ptr+2) = l

 *(ptr+3) = l

 *(ptr+4) = o

 

소스코드 실행 결과
 char arr[] = "Hello";
 const char *ptr = "Pointer";

 for (int i = 0; i < 5; i++) {
     printf("*(arr+%d) = %c\n", i, *(arr + i));
 }

 for (int i = 0; i < 7; i++) {
     printf("ptr[%d] = %c\n", i, ptr[i]);
 }

 *(arr+0) = H

 *(arr+1) = e

 *(arr+2) = l

 *(arr+3) = l

 *(arr+4) = o

 ptr[0] = P

 ptr[1] = o

 ptr[2] = i

 ptr[3] = n

 ptr[4] = t

 ptr[5] = e

 ptr[6] = r

 

 

char ch='A';
char *p1 = &ch;
char **p2 = &p1;  // 이중 포인터 메모리 주소 2번 받음
**p2 = 'A';
printf("%c %c\n", *p1, **p2);   //결과 : A, A


int i = 100;
int *p1, **p2, ***p3;  // 삼중 포인터 : 메모리 주소 3번 받음
p1 = &i;
p2 = &p1;
p3 = &p2;
printf("%d, %d, %d\n", *p1, **p2, ***p3);   //결과 : 100, 100, 100

 

 

메모리를 할당해 주는 함수를 이용한 포인터 변수의 사용예을 알아보자.

소스코드
 #include <stdio.h>
 #include <malloc.h>

 int main()
 {
     char *p1;
     short *p2;
     int *p3;
     long *p4;
     float *p5;
     double *p6;

     p1 = (char *)malloc(sizeof(char));
     p2 = (short *)malloc(sizeof(short));
     p3 = (int *)malloc(sizeof(int));
     p4 = (long *)malloc(sizeof(long));
     p5 = (float *)malloc(sizeof(float));
     p6 = (double *)malloc(sizeof(double));

     printf("char *p1의 메모리에 할당된 바이트 : %d\n", sizeof(*p1));
     printf("short *p2의 메모리에 할당된 바이트 : %d\n", sizeof(*p2));
     printf("int *p3의 메모리에 할당된 바이트 : %d\n", sizeof(*p3));
     printf("long *p4의 메모리에 할당된 바이트 : %d\n", sizeof(*p4));
     printf("float *p5의 메모리에 할당된 바이트 : %d\n", sizeof(*p5));
     printf("double *p6의 메모리에 할당된 바이트 : %d\n\n", sizeof(*p6));

     printf("char p1의 메모리에 할당된 바이트 : %d\n", sizeof(p1));
     printf("short p2의 메모리에 할당된 바이트 : %d\n", sizeof(p2));
     printf("int p3의 메모리에 할당된 바이트 : %d\n", sizeof(p3));
     printf("long p4의 메모리에 할당된 바이트 : %d\n", sizeof(p4));
     printf("float p5의 메모리에 할당된 바이트 : %d\n", sizeof(p5));
     printf("double p6의 메모리에 할당된 바이트 : %d\n", sizeof(p6));

     free(p1); 
     free(p2); 
     free(p3); 
     free(p4); 
     free(p5); 
     free(p6);

     return 0;
 }

실행 결과]

 char *p1의 메모리에 할당된 바이트 : 1
 short *p2의 메모리에 할당된 바이트 : 2
 int *p3의 메모리에 할당된 바이트 : 4
 long *p4의 메모리에 할당된 바이트 : 4
 float *p5의 메모리에 할당된 바이트 : 4
 double *p6의 메모리에 할당된 바이트 : 8

 char p1의 메모리에 할당된 바이트 : 4
 short p2의 메모리에 할당된 바이트 : 4
 int p3의 메모리에 할당된 바이트 : 4
 long p4의 메모리에 할당된 바이트 : 4
 float p5의 메모리에 할당된 바이트 : 4
 double p6의 메모리에 할당된 바이트 : 4

 

포인터 자료형에 따른 메모리 증가확인하는 예제프로그램을 작성해보자.

소스코드 실행 결과
 char *p1;
 short *p2;
 int *p3;
 long *p4;
 float *p5;
 double *p6;

 int i;

 p1 = (char *)100; //p1에 100번지를 수록
 printf("p1 : %d\n", p1);
 for (i = 0; i < 5; i++) {
     printf("p1+%d = %d번지\n", i, p1 + i);
 }  

 p2 = (short *)200; //p2에 200번지를 수록
 printf("\np2 : %d\n", p2);
 for (i = 0; i < 5; i++) {
     printf("p2+%d = %d번지\n", i, p2 + i);
 }

 p3 = (int *)300; //p3에 300번지를 수록
 printf("\np3 : %d\n", p3);
 for (i = 0; i < 5; i++) {
     printf("p3+%d = %d번지\n", i, p3 + i);
 }

 p4 = (long *)400; //p4에 400번지를 수록
 printf("\np4 : %d\n", p4);
 for (i = 0; i < 5; i++) {
     printf("p4+%d = %d번지\n", i, p4 + i);
 }

 p5 = (float *)500; //p5에 500번지를 수록
 printf("\np5 : %d\n", p5);
 for (i = 0; i < 5; i++) {
     printf("p5+%d = %d번지\n", i, p5 + i);
 }

 p6 = (double *)600; //p6에 600번지를 수록
 printf("\np6 : %d\n", p6);
 for (i = 0; i < 5; i++) {
     printf("p6+%d = %d번지\n", i, p6 + i);
 }

p1 : 100
p1+0 = 100번지
p1+1 = 101번지
p1+2 = 102번지
p1+3 = 103번지
p1+4 = 104번지

p2 : 200
p2+0 = 200번지
p2+1 = 202번지
p2+2 = 204번지
p2+3 = 206번지
p2+4 = 208번지

p3 : 300
p3+0 = 300번지
p3+1 = 304번지
p3+2 = 308번지
p3+3 = 312번지
p3+4 = 316번지

p4 : 400
p4+0 = 400번지
p4+1 = 404번지
p4+2 = 408번지
p4+3 = 412번지
p4+4 = 416번지

p5 : 500
p5+0 = 500번지
p5+1 = 504번지
p5+2 = 508번지
p5+3 = 512번지
p5+4 = 516번지

p6 : 600
p6+0 = 600번지
p6+1 = 608번지
p6+2 = 616번지
p6+3 = 624번지
p6+4 = 632번지

 

포인터 배열예제 프로그램을 작성해보자.

소스코드 실행 결과
 const char *name[3] = { "홍길동", "일지매", "임꺽정" };

 for (int i = 0; i < 3; i++) {
     printf("name[%d] = %s\n", i, name[i]);
 }
name[0] = 홍길동
name[1] = 일지매
name[2] = 임꺽정

 

포인터 2차원 배열 프로그램을 작성해 보자.

소스코드 실행 결과
 int arr[2][2] = { 1,2, 4,5 };
 int(*ptr)[2];
 int i, j;

 ptr = arr;

 //포인터를 배열 형식으로 출력
 for (i = 0; i < 2; i++)
 {
     for (j = 0; j < 2; j++)
     {  
         printf("ptr[%d][%d] = %d\n", i, j, ptr[i][j]);
     }
 }

 for (i = 0; i < 2; i++)
 {
     for (j = 0; j < 2; j++)
     {
         printf("*(*(ptr+%d)+%d) = %d\n", i, j, *(*(ptr+i)+j));
     }
 }
ptr[0][0] = 1
ptr[0][1] = 2
ptr[1][0] = 4
ptr[1][1] = 5
*(*(ptr+0)+0) = 1
*(*(ptr+0)+1) = 2
*(*(ptr+1)+0) = 4
*(*(ptr+1)+1) = 5

 

 

3. void 형 포인터

void형 포인터는 임의의 데이터 타입을 처리하기 위한 데이터 타입이므로, 캐스트 연산자를 사용하여 형변환후 사용 할수 있습니다. 그러므로 void형 포인터는 데이터 타입에 상관없이 모든 데이터 타입의 데이터를 처리할 수 있습니다.

 

int a = 123;
char b = 'A';
void *ptr;

ptr = &a;
printf("void형 포인터가 int형 변수의 주소을 받음 출력 : %d\n", *(int*)ptr);

ptr = &b;
printf("void형 포인터가 char형 변수의 주소을 받음 출력 : %c\n", *(char*)ptr);

 

출력 결과]

void형 포인터가 int형 변수의 주소을 받음 출력 : 123
void형 포인터가 char형 변수의 주소을 받음 출력 : A

 

 

문제 풀어보기]

1. 정수형 자료인 한 개의 포인터 변수와 일반 변수를 선언하여 10을 대입하고 출력하시오.

출력 예]
*p = 10;

 


2. 정수형 자료인 두 개의 포인터 변수와 일반 변수를 선언하여 10, 20을 대입하고 출력하시오.

출력 예]
*p1 = 10
*p2 = 20

 


3. 정수형 자료인 한 개의 포인터 변수와 일반 변수를 선언하여 입력하고 출력하시오.
입력 예]
30

출력 예]
*p = 30

 


4. 정수형 자료인 두 개의 포인터 변수와 일반 변수를 선언하여 입력하고 출력하시오.
입력 예]
30 50

출력 예]
30 + 50 = 80

 


5. 포인터 변수를 선언하고 “computer”문자열 상수를 대입하여 출력하시오. (char *p="computer")

출력 예]
d u n c o m

 


6. 배열 변수와 포인터 변수를 선언하고 문자를 입력하여 출력하시오.
입력 예]
c o m p u t e r 

출력 예]
r e t u p m o c

 


7. 배열 변수와 포인터 변수를 선언하고 5개의 정수를 입력 받아 합계를 출력하시오.
입력 예]
10 20 30 40 50

출력 예]
150

 


8. 포인터 배열을 선언하고 배열요소를 대입하고 출력하시오.( 포인터 변수에 각각 "홍길동", "일지매", "임꺽정"로 대입)

출력 예]
name[0] = 홍길동
name[1] = 일지매
name[2] = 임꺽정


9. 배열 포인터를 이용하여 2차원 배열과 연결하고 6개의 정수형 자료를 포인터 변수에 입력하여 아래와 같이 출력하시오.
입력 예]
7 8
4 5
2 3

출력 예]
ptr[0][0] = 7
ptr[0][1] = 8
ptr[1][0] = 4
ptr[1][1] = 5
ptr[2][0] = 2
ptr[2][1] = 3
합계 : 29

'프로그램언어 > C언어' 카테고리의 다른 글

c언어 구조체와 공용체  (0) 2019.10.07
c언어 사용자 정의 함수  (0) 2019.10.04
c언어 배열  (0) 2019.10.02
c언어 반복문(for, while, do ~ while)  (0) 2019.10.02
c언어 선택문 (if, switch)  (0) 2019.10.01