포인터의 종류 중 하나로 함수의 메모리 시작주소를 저장하는 포인터를 함수 포인터라 한다.

형식 리턴형 (*변수명)(인자...);
#include<stdio.h>
#define PI 3.1415
void Greeting();
double GetCircleArea(int);

void main(void)
{
	void (*ptr1)();
	double (*ptr2)(int);
	int radius;
	double area;

	ptr1 = &Greeting;
	(*ptr1)();

	ptr2 = &GetCircleArea;
	radius = 10;
	area = (*ptr2)(radius);
	printf("반지름이 %d인 원의 면적은 %.2f \n", radius, area);

}

void Greeting(){
	printf("Hello CodeIn!! \n");
}

double GetCircleArea(int radius){
	double area = PI*radius*radius;
	return area;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void main()
{
	char* ptr1;
	char* ptr2 = "korea";
	char* ptr3 = NULL;
	ptr3 = (char *)malloc(sizeof(char)*20);

	//strcpy(ptr1, "Good Korea"); //warning 초기화하지 않고 씀 
	//strcpy(ptr2, "Good Korea"); //warning 이미 korea가 할당되어 korea가 상수이므로 
			           //korea를 수정할 수 없음 문자열 상수
	strcpy(ptr3, "Good Korea");

	printf("%s\n", ptr3);

	free(ptr3);

}

ptr2 는 "korea"가 이미 할당되어 문자열 상수를 가르키는 포인터가 된다.
문자열 상수는 내용을 고칠 수 없으므로 사용할 수 없다.

하지만
ptr2 = "hi korea";
로 바꿀 수는 있다.

int (*ptr)[3]과 int* arr[3]의 차이점은 무엇인가? 

int* arr[3] 은  포인터 배열이다. 

 변수명  저장하는 내용
arr[0]  int* 
 arr[1] int* 
 arr[2]  int*
 
int (*ptr)[3]은 배열 포인터이다.

 int a[2][3]
 a[0][0]  a[0][1]  a[0][2]
 a[1][0]  a[1][1]  a[1][2]

int a[2][3] = {{3,4,5}, {6,7,8}};
int (*ptr)[3];

ptr=a; 로 대입할 수 있다.  

포인터 배열은 포인터를 가지고 있는 배열이며 배열 포인터는 배열을 가르키는 포인터이다.
즉 끝의 단어로
포인터 배열 = 배열, 배열 포인터=포인터 이다.


 
Const는 변수 선언시 자료형 앞뒤에 사용되는 변수를 상수로 선언한다. 

즉 Const로 선언된 변수는 수정할 수 없다. 

포인터와 const 키워드 
#include<stdio.h>

void main()
{
	int a = 10;
	int b = 20;

	const int *pa = &a;
	//*pa = 20; //값을 변경할 수 없다. (Error)
	pa = &b;	//주소 변경 가능
	
	int const *pb = &b;
	pb = &a;

	int* const p = &a;
	*p = 30;
	
}
위에서 보면 const int *pa와 int const *pb은 같은 내용이다.
즉 *pb 값 포인터에 의해서 읽혀지는 값을 변경할 수 없다.
그러나 주소 변경은 가능하다. 

const int *pa = int const *pa =  const int* pa

다 같은 표현인 것이다.  

int* const p 는 다르다. 값의 변경은 가능하지만 포인터값의 p의 주소값을 수정할 수 없다.  

 

배열과 포인터가 완전 같지는 않다고 한다. 그럼 차이점은 무엇일까?

int ar[n];
int *pi;

1. 포인터는 변수인데 비해 배열은 상수이다.

  pi는 고유의 메모리를 차지하고 있고 언제든지 다른 대상을 가리킬 수 있지만 ar은 선언할 때 그 위치가 이미 고정되므로 다른 대상을 가리킬 수 없다. ar로는 오로지 배열의 선두 번지를 읽을 수 있을 뿐이다.

2. pi가 가리키는 배열의 크키는 동적으로 결정할 수 있지만 ar이 가리키는 배열의 크기는 선언할 때 정적으로 결정된다.
 고정된 길이의 배열이 필요하면 int ar[n]; 선언문으로 배열을 생성하는 것이 편리하고 가변길이의 배열이 필요하면 int *형의 포인터 변수를 선언한  후  malloc으로 할당해서 사용해야 한다. 포인터로 할당한 배열은 실행 중에라도 realloc으로 크기를 재할당하여 변경할 수 있다.
 
3. 배열은 그 자체가 크기 때문에 함수의 인수로 전달할 수 없지만 포인터는 대상체가 무엇이든간에 4바이트의 크기밖에 차지하지 않으므로 함수로 전달할 수 있다.
 
그래서 배열을 함수로 전달할 때는 반드시 포인터를 사용해야 한다.

4.배열로 요소를 읽는 것과 포인터로 대상체를 읽는 동작의 속도 차이가 있다.
 배열의 첨자 연산은 매번 배열선두에서부터 출발하지만 포인터는 대상체로 직접 이동해서 바로 읽으므로 액세스 속도가 빠르다. *pi는 pi가 가리키는 곳을 바로 읽지만 ar[n]은 *(ar+n)으로 일단 번지를 더한 후 읽어야 하므로 조금 느리다. 포인터가 배열보다 두배 정도 빠르다.


ptr이 임의의 배열을 가리키는 포인터이고 n이 정수일 때
ptr[n] = *(ptr+n)

#include <Turboc.h>

void main(void)
{
	int ar[5]={1,2,3,4,5};

	printf("ar[2]=%d\n", ar[2]);
	printf("ar[2]=%d\n", *(ar+2));
	printf("ar[2]=%d\n", 2[ar]);
}

결과는 모두 같이 3이 나온다. 3번이 이해가 잘 안가지만 말이다. ㅡㅡㅋ
2[ar] 맨 위의 공식되로 변환하면 *(2+ar) 이 되고 *(ar+2) 교환 법칙에 의해서 동일하다고 한다.
흠 그럼 다 되는 것인가 2가 배열을 가지는 것도 아닌것이.. 문법이니 외우자 쩝

더 깊게 들어가보면 *(*(ar+2)+1)이 ar[2][1]과 같다는 것을 증명하는 예제이다.

 ar[0][[0]  ar[0][[1]  ar[0][[2]  ar[0][[3]
 ar[1][[0]  ar[1][[1]  ar[1][[2]  ar[1][[3]
 ar[2][[0]  ar[2][[1]  ar[2][[2]  ar[2][[3]

#include <Turboc.h>

void main(void)
{
	int ar[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

	printf("ar[2][1]=%d\n", ar[2][1]);
	printf("ar[2][1]=%d\n", *(*(ar+2) +1));

	//부분 배열을 가르키는 포인터
	printf("sizeof(ar+2)=%d\n", sizeof(ar+2));
	//선두번지의 포인터 상수 즉 부분 배열이 된다. 
	printf("sizeof(*(ar+2))=%d\n", sizeof(*(ar+2)));
	printf("sizeof(*(ar+2)+1)=%d\n", sizeof(*(ar+2)+1));
	

	printf("ar[2][0]=%d\n", *(ar+2));
	printf("ar=%d\n",ar);
	printf("*ar=%d\n",*ar);
	printf("**ar=%d\n",**ar);
	printf("ar[0][3]=%d\n", *(*ar + 1));
	printf("sizeof(*(ar+2)+0)=%d\n", sizeof(*(ar+2)+0));
}

결과
ar[2][1]=10
ar[2][1]=10
sizeof(ar+2)=4
sizeof(*(ar+2))=16
sizeof(*(ar+2)+1)=4
ar[2][0]=1245012
ar=1244980
*ar=1244980
**ar=1
ar[0][3]=2
sizeof(*(ar+2)+0)=4


1번째 2번째는 결과가 같다는 것을 증명했다. 
ar+2 이를 했을 경우 ar[2] 번지로 이동한다. 부분배열을 가르키는 포인터이다.  
*(ar+2)가 되었을때 16비트가 되면서 부분 배열의 선두 번지 &ar[2][0]으로 평가된다.
 *(ar+2)+1 포인터 상수에 정수를 더해서 ar[2][1] 포인터를 리턴한다.
*(*(ar+2)+1)  ar[2][1]에 해당하는 값을 가져온다.

재미있는 것은 ar=*ar이 같은 주소를 가르키는 포인터이고 **ar 했을 경우 그 값을 가져온다는 것이다.
2차원 배열은 이중포인터라는 것, 
 즉  *(ar+2) *은 값을 나타내는 표시인데 *(ar+2)값이 포인터라는 것이다. 한차례 더 써줄 경우 거기에 있는 값을 가져올 것이다. 만약 *(*(ar+2))를 할 경우 9를 가져올 것이다. 

그럼 1차원 배열을 보도록 하겠다.
#include <Turboc.h>

void main(void)
{
	int ar[4]={10,20,30,40};

	printf("%d\n", *ar+2);
	printf("%d\n", *(ar+2));
}

결과
12
30

결과를 보면 알 수 있듯이 1차원에서는 *ar 값이 바로 값이 되는 경우이고 2차원일 경우 *ar이 포인터가 되는 경우이다.
연산 결과 *이 앞에 있으므로 첫번째는 바로 값으로 변환되고 ar의 경우 포인터 연상이 되어 2칸 이동하여 30의 결과를 가져온다.

어쩌면 포인터와 배열명은 같을 수도 있다.
#include <Turboc.h>

void main(void)
{
	int arr[2] = {1,2};
	int *pArr=arr;

	printf("%d, %d \n", arr[0], *(arr+1));

	printf("%d, %d \n", pArr[0], *(pArr+1));
}

결과는 1,2로 같다. 이제까지 잘 이해해 오다가 포인터에서 다시 막막하기 시작한다. 이번엔 정복해야 되는데.. 쩝
지금까지 한 것이 정답이 아닐수도 있다. 이건 내가 돌려보며 이해한것이니 혹시라도 다른 사람들이 보더라도 오해가 없기를
그리고 지적해주기 바란다. C언어 공부중이다. ㅎㅎㅎ

배열의 내부적 처리
 C에서 다차원 배열이란 1차배열의 연장선을 말한다. 내부적으로는 하나로 쭉 연결된 구조이다. 하지만 부분 배열이 온전하게 배열로 대접받는다

#include <Turboc.h>

void arDump(void *array, int length);

void main(void)
{
	unsigned char ari[2][3] = {{1,2,3},{4,5,6}};

	arDump(ari, sizeof(ari));
	arDump(ari[0], sizeof(ari[0]));
	arDump(ari[1], sizeof(ari[1]));
}

void arDump(void *array, int length)
{
	int i;
	for(i=0;i<length;i++){
		printf("%02X ", *((unsigned char *)array+i));
	}

	printf("\n");
}
결과

01 02 03 04 05 06
01 02 03
04 05 06

ari[0], ari[1] 라는 부분 배열이 배열명으로 인정되며 부분 배열 혼자만 떼어내서 사용하는 것이 가능하다. ari[0]는 배열의 이름이기 때문에 부분 배열의 시작 번지를 가리키는 포인터 상수이며 따라서 arDump 함수로 전달할 수 있다.
수학함수는 수학적인 계산을 하는 함수들이다.

double sin(double x);
double cos(double x);
double tan(double x);
double asin(double x);
double acos(double x);
double atan(double x);
double sinh(double x);
double cosh(double x);
double tanh(double x);


asin, acos, atan 기본 삼각 함수의 역함수이며
sinh, cosh, tanh는 쌍곡선 삼각함수이다.

삼각함수가 받아들이는 인수 x는 360분법의 각도가 아니라 호도(라디안)값이다.

라디안(호도) : 1호도는 원주의 길이가 반지름과 같아지는 각도인데 180/3.1416으로 정의 되어 있다.

호도 = 각도 * 3.1416/180



반지름 r 과 r의 길이가 같아지는 정도..? 잘 모르겠다.  일단 0~90도까지 값이 어떻게 나오는지 봐야겠다.
예제를 한 번 돌려 보았다.

#include "Turboc.h"
#include "math.h"

void main(void)
{
	int r;

	for(r=0;r<=90;r+=10){
		printf("sin(%d도)=%f\n",r,sin(r*3.1416/180));
	}
}
결과
sin(0도)=0.000000
sin(10도)=0.173649
sin(20도)=0.342021
sin(30도)=0.500001
sin(40도)=0.642789
sin(50도)=0.766046
sin(60도)=0.866027
sin(70도)=0.939694
sin(80도)=0.984808
sin(90도)=1.000000


염병.. 90도일때 1라디안이랑 가까워지는 구나.. 어찌라고?? 단순무식한 나에게는 한계인가? 무엇을 만들수 있을까 시계와
같은 것을 만들때 꼭 필요하다고 하다. 

#include "Turboc.h"
#include "math.h"

void main()
{
	double angle;
	int x=-1,y=-1;

	clrscr();
	gotoxy(40,12);
	putch('S');
	for(angle=0;;angle+=10){
		if(angle==360) angle=10;
		//if(kbhit()) break;
		gotoxy(40+x,12+y); 
		putch(' ');
		x=int(cos(angle*3.1416/180)*20);
		y=int(sin(angle*3.1416/180)*10);
		gotoxy(40+x,12+y); putch('E');
		delay(100);
	}
}



이런걸 만들 수 있다. 와우 신기하구나... 꿀꺽

+ Recent posts