1. 포인터의 기본
포인터는 메모리주소를 값으로갖는 변수를 말한다.
포인터변수가 가지는 데이터형은 포인터변수가 참조할 데이터형과 일치해야한다.
int a=10;
int b=10;
int *p; //int형 메모리주소를 참조하는 포인터변수 p의 생성
p=&a; 포인터변수 p가 변수a의 메모리주소를 참조 (int *p=&a 로 선언과동시에 참조할수도 있음.)
*p=20; //변수a의 값을 20으로 변경
p=&b; //포인터변수 p가 변수b의 메모리주소를 참조하도록 변경
*p=20; //변수b의 값을 20으로 변경
printf("%d\n", *p); //포인터변수 p가 참조하는 변수b의 값 20 출력
printf("%d\n", p); //포인터변수 p가 참조하는 변수b의 주소출력
printf("%d\n", &p); //포인터변수 p 자신의 주소출력
2. 다차원 포인터
다차원 포인터란, 포인터주소를 참조하는 포인터변수를 말한다.
다차원 포인터도 일반포인터와 마찬가지로 참조하는 데이터와 데이터형이 동일해야 하며,
다차원포인터를 거슬러 올라가면 실제 데이터가 위치한다. (또는 쓰레기값이 위치한다...)
int a=10;//일반변수 a
int *x=&a; //싱글포인터 변수 x (변수a의 주소값을 참조)
int **y=&x; //더블포인터 변수 y (변수x의 주소값을 참조)
int ***z=&y; //트리플포인터 변수 z (변수y의 주소값을 참조)
x는 a의 주소값을, y는 x의 주소값을, z는 y의 주소값을 참조했다.
a-x-y-z 의 꼬리를무는 포인팅관계를 잘 이해해야한다.
printf("%d\n", *x); //x가 참조하는 a의 값(10) 출력
printf("%d\n", *y); //y가 참조하는 x의 값(a의 주소정보) 출력
printf("%d\n", **y); //y가 참조하는 x가 참조하는 a의 값(10) 출력
printf("%d\n", &z); //z의 주소정보 출력
printf("%d\n", z); //z의 값(y의 주소정보) 출력
printf("%d\n", *z); //z가 참조하는 y의 값(x의 주소정보) 출력
printf("%d\n", **z); //z가 참조하는 y가 참조하는 x의값(a의 주소정보) 출력
printf("%d\n", ***z); //z가 참조하는 y가 참조하는 x가 참조하는 a값(10) 출력
3. 포인터와 배열의 관계
배열은 연속된 데이터의 집합이며, 배열주소는 데이터의 크기만큼 일정하게 증감한다.
포인터변수에 증감연산을 하면 배열의 번지수를 증감한것과 동일한효과가 나타난다.
따라서, int arr[0] ~ arr[99] 의 주소값은 int *(arr+0) ~ *(arr+99) 와 동일하다.
int arr[100];
int i;
for(int i=0; i<100; i++){
arr[i]=i; //배열 a에 데이터설정.
}
for(int i=0; i<100; i++){
printf("%d\n", *(arr+i)); //배열 a의 값들이 출력된다.
}
- 다차원배열은 다중포인터로 표현할수있다.
int arr[10][10][10]; //3차원배열 arr
int i,j,k;
int count=0;
for(i=0; i<10; i++){
for(j=0; j<10; j++){
for(k=0; k<10; k++){
arr[i][j][k]=count++; //다차원배열에 순차적으로 데이터설정.
}
}
}
for(i=0; i<10; i++){
for(j=0; j<10; j++){
for(k=0; k<10; k++){
printf("%d\n", *(*(*(arr+i)+j)+k) ); //다차원배열의 값을 순차적으로 출력.
}
}
}
간단히 요약하자면 arr[0][0][0] 의 포인터식 표현은 *(*(*(arr+0)+0)+0) 으로 표현할수 있으며,
이것을 간단히 표현하자면 *(*(*(p))) 이고, 더 간단히 표현하면 ***p 가 된다.
arr[1][2][3] 은 *(*(*(arr+1)+2)+3) 과 같은표현이다.
당연한 얘기지만, arr[11][22][33] 은 *(*(*(p+11)+22)+33) 과 같은표현이다.
4. 다차원배열 포인터
포인터를 통해 다차원배열에 접근하는 또다른 방법은 다차원배열 포인터를 사용하는것이다.
다차원배열 포인터의 특징은 최상위 배열의 크기는 지정하지 않는것이 핵심이다.
int arr[5][10]; //2차원배열 arr
int (*p)[10]=arr; //int*10씩 건너뛰는 2차원배열 포인터 p에 arr을 대입
int i,j;
int count=0;
for(i=0; i<5; i++){
for(j=0; j<10; j++){
p[i][j]=count++; //포인터 p를 통해 arr에 값을 대입
}
}
for(i=0; i<5; i++){
for(j=0; j<10; j++){
printf("%d\n", p[i][j]); //포인터 p를 통해 arr의 데이터 출력.
}
}
int *p[10] 과 int (*p)[10] 의 차이점을 잘 알아야한다.
전자는 int형 포인터변수 10개, 후자는 int형 다차원배열을 가리키는 포인터이다.
- 3차원 이상의 배열에서 다차원배열 포인터 사용방법.
int arr[5][10][10]; //3차원배열 arr
int (*p)[10][10]=arr; //int*10*10씩 건너뛰는 3차원배열 포인터 p에 3차원배열 arr을 대입
int i,j,k;
int count=0;
for(i=0; i<5; i++){
for(j=0; j<10; j++){
for(k=0; k<10; k++){
*(*(*(p+i)+j)+k)=count++; //포인터 p를 통해 arr에 값을 대입
}
}
}
for(i=0; i<5; i++){
for(j=0; j<10; j++){
for(k=0; k<10; k++){
printf("%d\ ",p[i][j][k]); //포인터 p를 통해 arr의 데이터 출력.
}printf("\n");
}printf("\n");
}
printf("%d\n", ***p); //arr[0][0][0]의 값 출력
printf("%d\n", **(*(p+4))+1); //arr[4][0][1]의 값 출력
printf("%d\n", *(*(*(p+4)+9)+9)); //arr[4][9][9]의 값 출력
- p[1][2][3] 과 *(*(*(p+1)+2)+3) 은 문법적으로 동일한 표현임을 알수있다.
5. 함수와 포인터 (콜-바이-레퍼런스)
콜-바이-레퍼런스는 함수호출시 데이터의 주소를 인자값으로 전달, 데이터의 조작을 말한다.
이것을 사용하는 경우는 대표적으로 2가지 경우이다.
- 2개 이상의 인자값을 리턴받을 필요성이 있을때.
- 인자값으로 넘기기엔 너무 큰 데이터가 존재할때. (배열이나 구조체 등)
void function1(int *a, int *b){ //큰 데이터의 처리
int i;
for(i=0; i<1000; i++){
printf("%d %d\n",a[i], b[i]);
}
}
void function2(int *a, int *b){ //2개 이상의 데이터를 조작
*a=10;
*a=20;
}
void main(){
int a[1000];
int b[1000];
int num1;
int num2;
int i;
for(i=0; i<1000; i++){
a[i]=i;
b[i]=i*10;
}
function1(a, b); //큰 데이터를 데이터 복사없이 처리한다.
function2(&num1, &num2); //한번의 함수호출로 2개이상의 변수값을 변경한다.
}
6. 구조체와 포인터
구조체는 하나 이상의 변수를 그룹지어 하나의 단위로 관리하는 사용자정의 자료형이다.
이것은 특성상 동적으로 메모리를 할당하거나, 포인터변수를 통해 제어하는경우가 빈번하다.
구조체에서 포인터가 필요한경우는 크게 2가지이다.
-구조체를 포인터로 선언하여 동적메모리 할당으로 사용하는경우
-구조체의 멤버변수에 포인터변수가 존재하는 경우
그 2가지경우를 모두 만족하는 예제가 바로 여기에 있다.
#include<stdio.h>
#include<malloc.h>
#include<string.h>
typedef struct _person{
char *name;//구조체의 멤버 포인터변수
int age;
}person;
void main(){
person *p=(person*)malloc(sizeof(person)); //구조체 포인터변수 p의 메모리 동적할당
p->name=(char*)malloc(strlen("KKM")+1); //멤버변수 name의 메모리 동적할당
strcpy(p->name, "KKM"); //멤버변수 name에 데이터 "KKM" 을 대입
p->age=27; //멤버변수 a에 데이터 27을 대입
printf("%s\n", p->name); //멤버변수 name의 데이터를 출력
printf("%d\n", p->age); //멤버변수 age의 데이터를 출력
free(p->name); //멤버변수 name의 동적메모리 해제
free(p); //구조체 포인터변수 p의 동적메모리 해제
}
7. 함수 포인터
우리가 사용하는 함수의 기본구조를 해석하면 다음과같은 구조이다.
- 여기서 함수명 이것이 중요하다.
리턴타입 함수명(데이터형 인수명, 데이터형 인수명){
//구현부
return 데이터;
}
프로그램이 실행되면, 모든 상수와 명령은 text 세그먼트로 이동하며,
우리가 사용하는 함수명은 메모리에서 명령을 호출하는 상수형 포인터이다.
헌데, 포인터는 반드시 데이터형이 존재해야한다.
함수포인터는 리턴타입과 인수의 데이터형이 함수포인터의 데이터형이 된다.
예를들면, int func(int a, int b){} 은 int (*)(int, int) 형의 함수라고 볼수있다.
void func1(int a){ //void (*)(int)형 함수 func1
printf("func1 : %d\n", a);
}
void func2(int a){ //void (*)(int)형 함수 func2
printf("func2 : %d\n", a);
}
void func3(int a, int b){ //void (*)(int, int)형 함수 func3
printf("func3 : %d %d\n", a, b);
}
void main(){
void (*p1)(int)=func1; //함수 func1(int a) 를 참조하는 함수포인터 p1
void (*p3)(int, int)=func3; //함수 func3(int a, int b) 를 참조하는 함수포인터 p3
p1(10); //함수 func1 호출됨.
p1=func2; //함수포인터 p1에 함수 func2를 대입.
p1(1); //함수 func2 호출됨.
p3(10, 20); //함수 func3 호출됨.
}
8. void 포인터
void형의 데이터타입은 없는, 아무것도아닌 의 의미이기도 하지만,
무엇이든 될수있는 의 의미로서 해석할수도 있다.
실제로 void형 포인터는 어떤 자료형의 데이터도 포인팅가능한 호환성 100%의 포인터이다.
일반변수, 배열, 구조체, 함수, 그에대한 포인터의 주소까지 어떤것도 포인팅 가능하다.
하지만, 포인팅한 데이터를 제어하기 위해서는 같은형태로 캐스팅해야 한다.
포인터는 반드시 참조하는 데이터와 동일한 자료형으로 선언되어야 하기 때문이다.
int a=10;
char b='A';
void *p; //void 포인터변수 p의 선언
p=&a; //포인터변수 p에 int형 변수 a의 주소값을 대입
*((int*)p)=20; //포인터변수 p가 참조하는 주소값을 int형으로 캐스팅하여 변수a의 값을 변경.
p=&b;//포인터변수 p에 char형 변수 b의 주소값을 대입
*((char*)p)='B'; //포인터변수 p가 참조하는 주소값을 char형으로 캐스팅하여 변수b의 값을 변경.
이러한 방법으로 캐스팅한다면, 메모리주소에 직접 접근하여 제어하는것도 가능하다.
마이크로프로세서 응용제어 분야에서 많이 사용되는 메모리 직접제어 방식이다.
*(unsigned char*)0x1000 = 0xFF; //메모리주소 0x1000 의 데이터를 16진수 0xFF (2진수 11111111) 로 변경.
9. 포인터의 상수화
int num1=10;
int num2=10;
const int *p1=&num1; //읽기전용 포인터 p1
int * const p2=&num1; //상수화된 포인터 p2
const int * const p3 = &num1; //상수화된 읽기전용 포인터 p3
p1은 변수 num1의 값을 읽기만 가능하다.
p2는 포인팅주소를 &num2로 변경할수없다.
p3는 num1의 값을 읽기만 가능하고, 포인팅주소를 &num2로 변경도 불가능하다.
[출처] 포인터 pointer 집중적으로 파헤쳐본다.|작성자 ArtOfRudah
'OLD_posting' 카테고리의 다른 글
동적 분석 (0) | 2013.05.21 |
---|---|
포렌식 도구 모음 (Digital Forensics Tools) (0) | 2013.04.28 |
파일구리 검색어제한 해제패치 소스 (0) | 2013.03.09 |
윈도우7 로그인 암호 및 비밀번호 초기화 방법 (0) | 2013.01.28 |
javscript 페이지 새로고침(만료시간 없기) (0) | 2012.12.10 |