목차:
*포인터의 개념
*변수의 주소
* 포인터의 선언
* 간접 참조 연산자
* 포인터 연산
* 포인터와 배열
* 포인터와 함수
1. 포인터의 개념
정의) 주소를 저장하는 변수 (데이터를 저장하는 변수 X)
tip) 변수는 메모리에 저장되며, 메모리는 바이트 단위이고, 각각의 바이트 마다 주소가 부여가 됨.
ex) int = 4byte , char = 1byte , float = 4byte
2. 변수의 주소
정의) 해당하는 변수의 주소를 계산하는 연산자 : &(앤퍼센트)
- referencing연산자,대상의 주소를 얻어오는 연산자
ex) 변수 i의 주소 -> &i
변수 i의 값 -> i=10
#include <stdio.h>
int main(void)
{
int i = 10;
char c = 69;
float f = 12.3;
printf("i의 주소: %u\n", &i); // &(앤퍼센트) 기호로 주소값 호출중
printf("c의 주소: %u\n", &c);
printf("f의 주소: %u\n", &f);
return 0;
}
i의 주소: 1769731892 //시스템에 임의로 부여한 주소값임.
c의 주소: 1769731924
f의 주소: 1769731956
3. 포인터의 선언
정의) 변수의 주소를 가지고(저장) 있는 변수
syntax) int * p ;
= 정수를(원하는 type)/ 가리키는(포인터 입니다)/ 포인터p(변수명)
주의) (1) 실제 가르키는 변수의 type에 따라 알맞게 포인터의 type을 선언해야함
(2) 포인터의 초기화는 꼭 하자 NULL 사용.
// 포인터와 변수의 연결
int i = 10; // 정수형 변수 i 선언
int*p; // 포인터 변수 p 선언
p=&i // 변수 i의 주소가 포인터p로 할당됨
//위와 동일함
int i = 10; // 정수형 변수 i 선언
int*p=&i; // 포인터 변수 p 선언과 동시에 변수i의 주소가 할당됨
<주의 해야할 점(1)>
char c = 'A' // 문자형 변수 c 선언
flaot f = 36.5; // 실수형 변수 f 선언
double d = 3.141592; // 실수형 변수 d 선언
char*pc = &c; // 문자형을 가리키는 포인터pc
float*pf = &f; // 실수형을 가리키는 포인터pf
double *pd = &d; // 실수형을 가리키는 포인터pd
<주의 해야할 점(2)>
int i = 10;
int*pi=NULL; // 포인터의 초기화는 필수!!
pi=&i;
print("%u,%u",pi,&i);
>>>1768820 1768820 // 동일한 주소값이 출력이 됨
4. 간접(역) 참조 연산자 *
정의) 포인터가 가리키는 값을 가져오는 연산자
-deferencing 연산자, referencing의 역과정으로 주소로부터 대상변수를 얻어오는 것
int i=10; // i에 10을 할당함
int *p=&i; // 포인터p에 i의 주소를 할당함.
printf("%d",*p)
>>> 10 // 10 이 출력됨
#include <stdio.h>
int main(void)
{
int x=3;
int *ptr_x=&x
print("주소=%5d\n",ptr_x); // 주소 값 출력
print("값= %5d\n",*ptr_x); // 주소가 지칭하는 값 출력
return 0;
}
0x0061FF08
3
정리) & 연산자
* 연산자
의미 | 읽는법 | |
&연산자 | 변수의 주소를 반환 함 | &x -> x의 주소 |
*연산자 | 포인터가 가리키는 곳의 내용을 반환 함 | *x -> x의 포인터 |
포인터 예제#1
#include <stdio.h>
int main(void)
{
int i = 3000;
int* p = NULL;
p = &i; //이렇게 해야 연결이 됨
printf("i=%d\n", i); // 변수의 i 의값 출력
printf("&i= %u\n", &i); // 변수 i 의 주소 출력
printf("p=%u\n",p); // 포인터의 값 출력
printf("*p=%d\n", *p); // 포인터를 통한 간접 참조 값 출력
return 0;
}
>>>
i=3000
&i=1245024
p=1245025
*p=3000
포인터 예제#2
#include <stdio.h>
int main(void)
{
int i = 10;
int* p = NULL;
p = &i; //이렇게 해야 연결이 됨
printf("i=%d\n", i);
*p = 20; // 포인터르 통하여 변수의 값을 변경한다, i=*p=20 과 동일한 의미
printf("i=%d\n", i);
return 0;
}
>>>
i=10
i=20
5. 포인터 연산
- 가능한 연산: 증가, 감소, 덧셈, 뺄셈 연산
- 증가 연산의 경우 증가되는 값은 포인터가 가리키는 객체의 크기
포인터 타입 | ++연산후 증가되는 값 |
char | 1 |
short | 2 |
int | 4 |
float | 4 |
double | 8 |
<증가연산 예제 (바이트만큼 커진다는것을 알 수 있다.)(감소도 동일함)>
#include <stdio.h>
int main(void)
{
char* pc;
int* pi;
double* pd;
pc = (char*)10000;
pi = (int*)10000;
pd = (double*)10000;
printf("증가 전 pc=%d,pi=%d,pd=%d\n", pc, pi, pd);
pc++; //증가된 후에 저장이 되어서 증가된값들이 pc,pi,pd가 됨.
pi++;
pd++;
printf("증가 후 pc=%d,pi=%d,pd=%d\n", pc, pi, pd );
printf("pc+2=%d, pi+2=%d, pd+2=%d\n", pc + 2, pi + 2, pd + 2);
return 0;
}
>>>
증가 전 pc=10000,pi=10000,pd=10000
증가 후 pc=10001,pi=10004,pd=10008
pc+2=10003, pi+2=10012, pd+2=10024
<간접 참조 연산자와 증감 연산자>
● *p++ : 주소를 증가
-> p(주소)가 가리키는 위치에서 값을 가져온 후에 p(주소)를 증가함.
●(*p)++ : 값을 증가
-> p가 가리키는 위치의 값을 증가한다.
#include <stdio.h>
int main(void)
{
int i = 10;
int* pi = &i;
printf("i=%d, pi=%p\n", i, pi);
(*pi)++; // pi가 가리키는 위치의 값을 증가한다.
printf("i=%d, pi=%p\n", i, pi);
printf("i=%d, pi=%p\n", i, pi);
*pi++; // pi가 가리키는 위치에서 값을 가져온 후에 pi를 증가한다.
printf("i=%d, pi=%p\n", i, pi);
return 0;
}
>>>
i=10, pi=00000015E119FB44
i=11, pi=00000015E119FB44 // i 의 값이 증가됨을 알 수 있다.
i=11, pi=00000015E119FB44
i=11, pi=00000015E119FB48 // 주소값이 4byte(int임으로)증가함을 알수있다.
<포인터의 형변환>
syntax: double*pd=&f;
int *pi;
pi=(*int)pd;
<인수 전달 방법>
->함수 호출 시에 인수 전달 방법
(1) 값에 의한 호출(call by value)
-> 함수로 값의 복사본이 전달됨
-> 기본적인 방법
(2) 참조에 의한 호출(call by reference)
-> 함수로 주소가(원본이) 전달됨.
-> C에서는 포인터를 이용하여 흉내 낼 수 있다.
<swap함수 #1 값에 의한 호출>
#include <stdio.h>
void swap(int x, int y)
{
int tmp;
printf("x=%d y=%d\n", x, y);
tmp = x;
x = y;
y = tmp;
printf("x=%d y=%d\n", x, y);
}
int main(void)
{
int a = 100, b=200;
printf("a=%d b=%d\n", a, b);
swap(a, b);
printf("a=%d b=%d\n", a, b);
return 0;
}
>>>
a=100 b=200
x=100 y=200
x=200 y=100 // 로컬 변수 이기 때문에 함수 내부에서만 유지됨.
a=100 b=200
<swap함수 #2 참조에 의한 호출>
#include <stdio.h>
void swap(int *px, int *py) //주소에 있는 값 전달
{
int tmp;
tmp = *px;
*px = *py;
*py = tmp;
}
int main(void)
{
int a = 100, b=200;
printf("a=%d b=%d\n", a, b);
swap(&a, &b);
printf("a=%d b=%d\n", a, b);
return 0;
}
>>>
a=100 b=200
a=200 b=100
6. 포인터와 배열
-> 포인터는 배열처럼 사용할 수 있다.
-> 인덱스 표기법을 포인터에 사용할 수 있다.
// 포인터와 배열의 관계 예시
#include<stdio.h>
int main(void)
{
int a[] = { 10,20,30,40,50 }; //각각의 배열이 4바이트씩 차지함.
printf("&a[0]=%u\n", &a[0]);
printf("&a[1]=%u\n", &a[1]);
printf("&a[2]=%u\n", &a[2]);
printf("a=%u\n", a);
return 0;
}
>>>
&a[0]=2503932216
&a[1]=2503932220 // 다음 int 공간에서 가장작은 값 나옴
&a[2]=2503932224 // 다음 int 공간에서 가장작은 값 나옴
a=2503932216 // a는 가장 작은 주소를 가리킨다
//포인터를 배열처럼 사용 예시
#include<stdio.h>
int main(void)
{
int a[] = { 10,20,30,40,50 }; //각각의 배열이 4바이트씩 차지함.
int* p;
p = a; // p에다가 a의 주소 가져옴
printf("a[0]=%d a[1]=%d a[2]=%d \n", a[0],a[1],a[2]); // line 10,11을 보고 배열은 결국
printf("p[0]=%d p[1]=%d p[2]=%d \n", p[0],p[1],p[2]); // 포인터로 구현 된다는것을 알수있다.
p[0] = 60; //포인터를 통해 배열원소를 변경할수 있다.
p[1] = 70;
p[2] = 80;
printf("a[0]=%d a[1]=%d a[2]=%d \n", a[0], a[1], a[2]);
printf("p[0]=%d p[1]=%d p[2]=%d \n", p[0], p[1], p[2]);
return 0;
}
>>>
a[0]=10 a[1]=20 a[2]=30
p[0]=10 p[1]=20 p[2]=30
a[0]=60 a[1]=70 a[2]=80
p[0]=60 p[1]=70 p[2]=80
7. 포인터와 함수
//포인터와 함수의 관계
#include <stdio.h>
void sub(int b[], int n[]);
int main(void)
{
int a[3] = { 1,2,3 };
printf("%d%d%d\n", a[0], a[1], a[2]);
sub(a, 3);
printf("%d%d%d\n", a[0], a[1], a[2]);
return 0;
}
void sub(int b[], int n) // ( 배열을 받은 포인터, 개수)
{
b[0] = 4;
b[1] = 5;
b[2] = 6;
}
>>>
123
456
// 다음 두가지 방법은 완전히 동일함
//방법1_ 배열 매개변수
void sub(int b[], int size[]) //(b[] 배열의 이름과 포인터는 근복적으로 같다.
{
b[0] = 4;
b[1] = 5;
b[2] = 6;
}
//방법 2_ 포인터 매개변수
void sub(int* b, int size)
{
b[0] = 4; //line18,19,20 배열 표기법을 사용하여 배열에 접근
b[1] = 5;
b[2] = 6;
}