C언어 C+ [C특강] 포인터 총정리
포인터는 C 언어를 공부할 때 어려운 부분이지요.
포인터는 처음 이해하기가 어렵고, 그 다음에 이해를 하더라도 사용하기가 어렵지요.
포인터를 이해하기 위해서는 C 언어 책들을 많이 보시기 바랍니다.
이 책 저 책 보다 보면 포인터를 정복하기는 쉬울 것입니다.
포인터는 이해하는 것이 중요한 것이지 외우는 것이 중요하지는 않습니다.
저 같은 경우는 자주 사용하는 포인터는 항상 머리속에 있지만
그렇지 않은 포인터들은 이렇게 정리해 놓고 보고 쓰곤 합니다.
여러분들도 그렇게 하는 것이 좋지 않을까 생각합니다.
또한 포인터에 관련된 것을 모아 놓은 "윈도우 프로그래밍 플러스" 책 또한 참고할만 하겠지요..
물론 제가 쓴 책이긴 한데 이 책이 있으면 여러 모로 필드에서 도움이 많이 될 것입니다.
포인터 못하면 ㅋㅋㅋ C#이나 자바 같은 거 하면 되는지?
저는 C#도 모르고 자바도 모르고 아직도 C, C++, MFC, TCP/IP, MS-SQL, ASP, VB, Excel, Access 이런 것들로
밥 먹고 삽니다...ㅋㅋㅋㅋㅋ
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <conio.h>
#pragma warning(disable:4101) // C4101 경고 에러 제거
void main( void )
{
/* 기본 포인터 int*/
{
int i;
int* pi1; // 선언 방법 1 (권장)
int *pi2; // 선언 방법 2
int * pi3; // 선언 방법 3
pi2 = &i; // i의 번지값을 대입
// 변수에 값을 대입 (세 문장은 모두 동일)
i = 5; // 직접 대입
*pi2 = 5; // pi2 변수의 값(i의 번지)의 번지에 간접 접근해서 대입
pi2[0] = 5; // pi2 변수의 0번째 배열 요소에 접근해서 대입
}
//*/
//.// 일반 변수의 크기 (각 데이터형이 차지하는 메모리 크기)
printf( "%d \n", sizeof(char) ); // 1 바이트
printf( "%d \n", sizeof(int) ); // 4 바이트
printf( "%d \n", sizeof(long) ); // 4 바이트
printf( "%d \n", sizeof(float) ); // 4 바이트
printf( "%d \n", sizeof(double) ); // 8 바이트
//.// 포인터 변수의 크기 (번지값을 저장하기 위한 변수이므로 모두 4)
printf( "%d \n", sizeof(char*) ); // 4 바이트
printf( "%d \n", sizeof(int*) ); // 4 바이트
printf( "%d \n", sizeof(long*) ); // 4 바이트
printf( "%d \n", sizeof(float*) ); // 4 바이트
printf( "%d \n", sizeof(double*) ); // 4 바이트
/* 문자형 포인터 char*/
{
char ch;
char* pch;
pch = &ch;
ch = 'a'; // 변수에 값을 대입 (세 문장은 모두 동일)
*pch = 'a'; // pi 변수의 값(i의 번지)의 번지에 간접 접근해서 대입
pch[0] = 'a'; // pi 변수의 0번째 배열 요소에 접근해서 대입
}
//*/
/* 문자열형 포인터 char*/
{
char string[10] = "winter";
char* pstr;
pstr = string; // 선두 번지 상수 값
pstr = &string[0]; // 0번째 요소의 번지 값
*pstr = 'W'; // 첫 번째 요소에 'W'를 대입
pstr[0] = 'W'; // 첫 번째 요소에 'W'를 대입
*(pstr+0) = 'W'; // 첫 번째 요소에 'W'를 대입
pstr[1] = 'I'; // 두 번째 요소에 'I'를 대입
*(pstr+1) = 'I'; // 두 번째 요소에 'I'를 대입
pstr++; // 1번지 만큼 증가, ++pstr도 동일
*pstr = 'I'; // 두 번째 요소에 'I'를 대입
pstr[0] = 'I'; // 두 번째 요소에 'I'를 대입, pstr이 1증가했기 때문
*(pstr+0) = 'I'; // 두 번째 요소에 'I'를 대입
pstr--; // 1번지 만큼 감소, --pstr도 동일
strcpy( string , "summer" ); // string과 pstr은 같은 번지 값
strcpy( &string[0], "summer" ); // string과 &string[0]은 같은 번지 값
strcpy( pstr , "summer" ); // strcpy는 같은 번지를 넘겨 받음
strcpy( string+2 , "summer" ); // string+2는 &string[2]와 동일
strcpy( &string[2], "summer" ); //
strcpy( pstr+2 , "summer" ); //
}
//*/
/* 문자열형 포인터 char*의 잘못된 사용 */
{
char* pstr = "winter"; // pstr은 상수 영역 포인터
char* ptmp; // 임시 포인터
*pstr = 'W'; // 상수 영역을 읽기만 가능, 프로그램 다운
pstr[0] = 'W'; // 상수 영역을 읽기만 가능, 프로그램 다운
*(pstr+0) = 'W'; // 상수 영역을 읽기만 가능, 프로그램 다운
ptmp = pstr; // pstr 값과 일치 시킴
*ptmp = 'W'; // pstr과 같은 번지 값을 갖기 때문에 프로그램 다운
ptmp[0] = 'W'; // pstr과 같은 번지 값을 갖기 때문에 프로그램 다운
*(ptmp+0) = 'W'; // pstr과 같은 번지 값을 갖기 때문에 프로그램 다운
printf( "%c", *pstr ); // w가 출력, 읽기는 가능
printf( "%c", pstr[0] ); // w가 출력, 읽기는 가능
pstr++;
printf( "%c", *pstr ); // i가 출력, 읽기는 가능
printf( "%c", pstr[0] ); // i가 출력, pstr++로 pstr의 시작 번지가 1증가됨
pstr--;
strcpy( pstr, "summer" ); // 상수 영역에 summer 복사 불가능
strcpy( ptmp, "summer" ); // pstr == ptmp 이므로 불가능
printf( pstr ); // winter가 출력, 읽기는 가능
printf( ptmp ); // winter가 출력, 일기는 가능
}
//*/
/* 1차원 정수형 배열의 포인터 int*
{
int array[5] = { 1, 2, 3, 4, 5 };
int* p;
p = array; // 배열의 선두 번지
p = &array[0]; // 배열의 선두 번지
p = array + 1; // 배열의 두 번째 번지
p = &array[1]; // 배열의 두 번째 번지
p = array;
*p = 100; // 현재 p가 가리키는 요소에 100을 대입
p[0] = 100; // 현재 p가 가리키는 0번째 요소에 100을 대입
*(p+0) = 100; // 현재 p가 가리키는 0번째 요소에 100을 대입
p++; // int형 데이터 배열이므로 4가 증가 (중요)
p = p + 1; // p++과 동일, 4가 증가
p = p + 2; // 8이 증가, (sizeof(int) * 2) == 8
}
//*/
/* 2차원 정수형 배열의 포인터 int (*)[]
{
int array[2][3] = { 1, 2, 3, 4, 5, 6 };
int (*p)[3];
int **p2;
p = array; // 배열의 선두 번지, int (*)[3]
p = &array[0]; // 배열의 선두 번지
printf( "%#x \n", p[1] ); // 0x12ff34
printf( "%#x \n", p+1 ); // 0x12ff34
printf( "%#x \n", *(p+1) ); // 0x12ff34
printf( "%d \n", **(p+1) ); // 4
printf( "%d \n", *(*(p+1)) ); // 4
printf( "%d \n", *(*(p+1)+0) ); // 4
printf( "%d \n", *(*(p+1)+1) ); // 5
printf( "%d \n", *(*(p+1)+2) ); // 6
//p = &array[0][0]; - 틀림, &array[0][0]은 int *
//p = array[0]; - 틀림, array [0] 은 int *
//p = array [0][0]; - 틀림, array [0][0]은 int
p = array + 1; // 배열의 두 번째 번지
p = &array[1]; // 배열의 두 번째 번지
p = array;
p[0][0] = 100; // 현재 p가 가리키는 [0][0]번째 요소에 100을 대입
*(*(p+0)+0) = 100; // 현재 p가 가리키는 [0][0]번째 요소에 100을 대입
p[1][2] = 200; // 현재 p가 가리키는 [1][2]번째 요소에 100을 대입
*(*(p+1)+2) = 200; // 현재 p가 가리키는 [1][2]번째 요소에 100을 대입
p++; // int(*)[3]형 배열이므로 12가 증가 sizeof(int) * 3
p = p + 1; // p++과 동일, 12가 증가
p = p + 2; // 24가 증가, (sizeof(int) * 3 * 2)
}
//*/
/* 2차원 배열을 int *로 사용
{
int array[2][3] = { 1, 2, 3, 4, 5, 6 };
int *p;
int i;
p = array[0]; // 배열의 선두 번지, int *
for( i=0; i<sizeof(array)/sizeof(int)/2; i++ )
{
printf( "%d \n", *p++ );
}
//p = &array[0]; - 틀림 int (*)[3]
p = &array[0][0]; // 첫 번째 배열의 주소
p = &array[0][1]; // 두 번째 배열의 주소
p = &array[1][0]; // 네 번째 배열의 주소
printf( "%d \n", p[0] ); // 4
printf( "%#x \n", p+0 ); // 0x12ff14
printf( "%d \n", *(p+0) ); // 4
printf( "%d \n", *(p+1) ); // 5
printf( "%d \n", *(p+2) ); // 6
p = array[0]; // 배열의 선두 번지, int *
p[0] = 100; // 현재 p가 가리키는 [0][0]번째 요소에 100을 대입
*(p+0) = 100; // 현재 p가 가리키는 [0][0]번째 요소에 100을 대입
p[1] = 200; // 현재 p가 가리키는 [1][2]번째 요소에 100을 대입
*(p+1) = 200; // 현재 p가 가리키는 [1][2]번째 요소에 100을 대입
p++; // int*형이므로 4만 증가
p = p + 1; // p++과 동일, 4가 증가
p = p + 2; // 8이 증가, sizeof(int) * 2
}
//*/
/* 3차원 정수형 배열의 포인터 int (*)[][]
{
int array[2][3][4] =
{
{ { 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12} },
{ {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} }
};
int (*p)[3][4];
int ***p2;
p = array; // 배열의 선두 번지, int (*)[3][4]
p = &array[0]; // 배열의 선두 번지
printf( "%#x \n", p[0] ); // 0x12fea0
printf( "%#x \n", p+0 ); // 0x12fea0
printf( "%#x \n", p+1 ); // 0x12fed0 <-
printf( "%#x \n", *(p+0) ); // 0x12fea0
printf( "%#x \n", **(p+0) ); // 0x12fea0
printf( "%d \n", ***(p+0) ); // 1
printf( "%#x \n", *(*(p+0)) ); // 0x12fea0
printf( "%#x \n", *(*(p+0)+0) ); // 0x12fea0
printf( "%#x \n", *(*(p+0)+1) ); // 0x12feb0 <-
printf( "%#x \n", *(*(p+0)+2) ); // 0x12fec0 <-
printf( "%d \n", *(*(*(p+0)+0)+0) ); // 1 , p[0][0][0] == [0]
printf( "%d \n", *(*(*(p+0)+0)+1) ); // 2 , p[0][0][1] == [1]
printf( "%d \n", *(*(*(p+0)+0)+2) ); // 3 , p[0][0][2] == [2]
printf( "%d \n", *(*(*(p+0)+0)+3) ); // 4 , p[0][0][3] == [3]
printf( "%d \n", *(*(*(p+0)+1)+0) ); // 5 , p[0][1][0] == [4]
printf( "%d \n", *(*(*(p+0)+1)+1) ); // 6 , p[0][1][1] == [5]
printf( "%d \n", *(*(*(p+0)+1)+2) ); // 7 , p[0][1][2] == [6]
printf( "%d \n", *(*(*(p+0)+1)+3) ); // 8 , p[0][1][2] == [7]
printf( "%d \n", *(*(*(p+1)+2)+0) ); // 21 , p[1][2][0] == [20]
printf( "%d \n", *(*(*(p+1)+2)+1) ); // 22 , p[1][2][1] == [21]
printf( "%d \n", *(*(*(p+1)+2)+2) ); // 23 , p[1][2][2] == [22]
printf( "%d \n", *(*(*(p+1)+2)+3) ); // 23 , p[1][2][3] == [23]
p++; // 48이 증가 sizeof(int) * 3 * 4
p = p + 1; // p++과 동일, 48이 증가
p = p + 2; // 96이 증가, sizeof(int) * 3 * 4 * 2
}
//*/
/* 구조체 포인터
{
struct Jusorok
{
int data;
char name[20];
int *p;
struct Jusorok* next;
};
struct Jusorok J;
struct Jusorok* PJ;
//PJ = J; // 타입 불일치 에러
PJ = &J; // 올바른 표현
J.data = 5;
strcpy( J.name, "이상미" );
J.p = (int*) malloc( 500 );
J.next = NULL;
printf( "%d \n", J.data ); // pJ->data 와 동일
printf( "%d \n", PJ->data ); // J.data와 동일
}
//*/
/* 링크드 리스트
{
typedef struct tagList
{
int data;
struct tagList* next;
} LIST, *PLIST;
PLIST p;
PLIST Head, Tail;
p = (PLIST) malloc( sizeof(LIST) );
memset( p, 0, sizeof(LIST) );
p->data = 1;
Head = Tail = p;
printf( "%#x \n", &p ); // 0x12fe70
printf( "%#x \n", p ); // 0x3733e0
printf( "%#x \n", &p->data ); // 0x3733e0
printf( "%d \n", p->data ); // 1
printf( "%#x \n", &p->next ); // 0x3733e4
printf( "%#x \n", p->next ); // 0
p = (PLIST) malloc( sizeof(LIST) );
memset( p, 0, sizeof(LIST) );
p->data = 2;
Tail->next = p;
Tail = p;
printf( "%#x \n", &p ); // 0x12fe70
printf( "%#x \n", p ); // 0x373418
printf( "%#x \n", &p->data ); // 0x373418
printf( "%d \n", p->data ); // 2
printf( "%#x \n", &p->next ); // 0x37341c
printf( "%#x \n", p->next ); // 0
p = Head;
while( p )
{
printf( "%d \n", p->data ); // 1, 2
p = p->next;
}
}
//*/
/* 1차원 구조체 배열
{
typedef struct
{
int data;
} ARRAY;
ARRAY array[10];
ARRAY *p;
int i;
for( i=0; i<sizeof(array)/sizeof(ARRAY); i++ )
{
array[i].data = i;
}
p = array;
for( i=0; i<sizeof(array)/sizeof(ARRAY); i++ )
{
printf( "%d \n", p[i].data );
printf( "%d \n", (*(p+i)).data );
printf( "%d \n", (p+i)->data );
}
}
//*/
/* 함수 포인터
{
int (*LEN) ( const char* );
typedef int(*FLEN) ( const char* );
FLEN l1, l2;
FLEN f[3] = { strlen, strlen, strlen };
LEN = strlen;
l1 = l2 = strlen;
printf( "%d \n", strlen ("12345") ); // 5
printf( "%d \n", LEN ("12345") ); // 5
printf( "%d \n", l1 ("12345") ); // 5
printf( "%d \n", l2 ("12345") ); // 5
printf( "%d \n", f[0] ("12345") ); // 5
printf( "%d \n", (f[1]) ("12345") ); // 5
printf( "%d \n", (*f[2])("12345") ); // 5
}
//*/
/* 표현식
{
int number[] = {0,1,2,3,4,5,6,7,8,9};
char *p;
p = number;
printf( "%d \n", *p++ );
printf( "%d \n", *p-- );
printf( "%d \n", *++p );
printf( "%d \n", ++*p );
printf( "%d \n", (*p)++ );
printf( "%d \n", (*p)-- );
printf( "%d \n", *(p+1) );
printf( "%d \n", *(1+p) );
}
//*/
문자열형 포인터 char*의 잘못된 사용'에서요.
*pstr = 'W0'; 상수 영역을 "읽기만 가능, 프로그램 다운"이라고 하셨는데,
OS마다 가상 메모리 구조가 틀리겠지만, 0x00000000 주소값은 NULL 값 할당 영역에
포함되는 것으로 알고 있습니다. NULL 값 할당영역은 Memory Access 자체가 불가능하지
않습니까? 그러니까 읽기/쓰기 모두 불가능한 것으로 알고 있는데요..
보통 Access Violation 에러가 발생하는 이유가 이 영역을 '읽기'때문에 접근 권한
문제로 참조 에러가 발생하잖아요. 값은 UnInitialize Data Segment rdata 영역에 위치하지만
주소값 자체는 상수 영역에 저장되는 것이 아니라, 가상 메모리 구조에서 Null 값 할당 영역에
위치하는 것으로 알고 있습니다.
작은 것에 딴지를 걸어서 죄송하지만, 보다 확실하고 정확하게 알아야하는 것이
지식이라고 생각하기 때문에요.^^;