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 |
포인터 자료형에 따른 메모리 증가확인하는 예제프로그램을 작성해보자.
소스코드 | 실행 결과 |
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 |