void 포인터


void 포인터??

이런 포인터는 처음 들어봤네요~ 분명 void라는 것은 함수 앞에서 반환형이 없을 때 쓰이는 키워드로 아는뎁;

void main은 많이 봤는데...


지금부터 이야기할 주제가 바로 void포인터라고 합니다. 우리는 이제껏 자료형이 정해져있는 포인터 예를 들면


int *a


라는 형태는 봐왔잖아요?


그리고 그 포인터에 주소를 할당하는 방법은 이런 것이죠.


int a=100;

int *b=&a; 


이걸 말로 풀어서 설명한다고 하면

b는 a의 주소를 가지고 있고, b를 통해 a를 참조할 수 있는데 그곳에는 int형 데이터가 있다!

라고 말이에요.


우리는 이 말속에서 힌트를 얻을 수 있습니다. 

그곳에는 int(정수형)형 데이터가 있다 라는 말을 집중해주세요.


위의 int *를 void *로만 바꾸어 써보고 읽어볼게요.


int a=100;

void *b=&a;


b는 a의 주소를 가지고 있고, b를 통해 a를 참조할 수 있는데 그곳에는 void형 데이터가 있다.




void 형 데이터가 있다....(?) void는 "빈공간"이라는 뜻을 내포하고 있는데요. 컴퓨터는 자료형을 모르기 때문에 빈공간처럼 보는 겁니다.

그러기 때문에 앞으로 우리는 이렇게 읽어야 할 겁니다.


b는 a의 주소를 가지고 있고, b를 통해 a를 참조할 수 있는데 그곳에는 알 수 없는 자료형 데이터가 있다.


이 형태 그대로 데이터를 참조하면 컴퓨터는 "아 몰랑!" 합니다.


printf("%d\n", *b);     //오류

 

얼마나 참조해야하는지 알 수 없기 때문입니다. void는 단순히 주소값만을 가지고 있습니다.


우리는 void가 가리키고 있는 데이터의 형태를 알고 있습니다. 우리는 똑똑하니까요(?). 그래서 우리는 *b가 무엇이냐 라고 질문할때 100이라고 대답할 수 있습니다. 우리는 똑똑하기 때문이죠.


하지만 단순히 주소값만!알고 있는 우리 void형은 그 형태가 int형이든 char 형이든 구조체든 문자열을 가리키고 있는 포인터이든 상관없이 단순히 주소값만이요. 그러니 void포인터는 자료형이 무엇이든 간에 주소값만 바라봅니다.


주소만 갖으면 되기 때문에 포인터의 크기(4바이트)만 갖고 있고, 포인터 연산조차 할 수 없습니다.


정말 읽을 수 없는 지 한번 코드로 살펴봅시다.

#include <stdio.h>

int main() {
	int a = 10;
	void *b = &a;
	printf("%d\n", *b);
}
	


실행시킬 수 조차없이 빨간줄로 


Error:Expression must be a pointer to a complete object type


라는 에러를 보게 됩니다. 

완전한 형태의 포인터로 바뀌어야한다 라고요.




우리는 컴퓨터에게 "너가 가리키고 있는 데이터 자료형은 int형이야" 라고 명확히 알려주어야합니다. "그러니까 넌 묻지도 따지지도 말고 4바이트만 읽으면 돼!" 라고요.

어떻게 알려줄까요??


우리는 형변환을 알고 있습니다. 그걸 사용하는 것이죠.


printf("%d\n", *(int*)b); 


이렇게 하면 void*는 int형을 읽을 수 있는 int*로 변환되게 됩니다.

정말 빨간 줄 없이 int형 데이터를 읽을 수 있는 지 코드로 볼까요?

#include <stdio.h>

int main() {
	int a = 10;
	void *b = &a;
	printf("%d\n", *(int*)b);
}


빨간 밑줄은 없어졌군요~ 





실행도 정상적으로 되는 것을 확인할 수 있고, 10을 정확히 읽는 것도 확인할 수 있습니다.


char형은 어떻게 변환할까요? 그것도 간단합니다. 바로 char*로만 바꾸어주면 됩니다.

#include <stdio.h>

int main() {
	char a= 'c';
	void *b = &a;
	printf("%c\n", *(char*)b);
}





네, 'c'라는 문자를 제대로 읽을 수 있군요.




문자열 역시 다르지 않습니다.

#include <stdio.h>

int main() {
	char *str= "문자열";
	void *b = str;
	printf("%s\n", (char*)b);
}

str자체가 문자열을 가리키고 있는 포인터이기 때문에 str변수 앞에 &(amp)를 붙여주지 않습니다. 이해하죠?




문자열도 잘 읽어오는군요.



이렇게 void포인터는 만능입니다. 어떤 자료형이건 바로 참조할 수있죠. 알맞은 자료형으로만 변경한다면 말이죠.


우리는 void포인터를 동적할당할때 유용하게 사용합니다. 동적할당에 사용하는 malloc과 같은 함수들이 void*로 반환하기 때문입니다.

malloc은 특정한 size의 크기로 메모리를 할당하고나서 우리들에게 알맞게 변환해서 써라 라는 의미로 void*를 내뱉게 됩니다.


어차피 나중에 배울 malloc함수의 원형 한번 볼까요?


void *malloc(size_t size)


앞서 말한대로 void*를 토하고 있습니다. 너네가 맘대로 바꾸라고 말이죠.


한번 보세요. 아~ 이런 변태같은 함수도 있구나~ 라고 기억하시기 바랍니다.



이것으로 void포인터에 대해 설명해보았습니다. 감사합니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

문자열과 char 포인터

오늘은 심심한데 문자열에 대해서 이야기 해볼까 해요~ 문자열과 포인터는 C언어에서 너무나 귀찮은 놈들인데,,, 그래도 꼭 쓰이니까요. char 자료형은 문자를 변수로 갖는 건 모두 아는 사실이죠?

근데~ 우리는 문자열을 쉽게 할당하고 싶단 말이에요. 우리는 배열이라는 아주 편리한 변수 선언 법을 알고 있답니다.

배열로 문자열을 표현하는 방법을 알아보겠습니다.

char hello[6] = { 'h','e','l','l','o','\0' };

이 표현은 char이 자료형을 배열로 문자열을 표현한 방법이랍니다. '\0' 라는 문자는 NULL문자라는 뜻입니다. 문자열의 끝을 알려줍니다.

그래서 hello가 다섯글자임에도 불구하고 배열 크기를 6으로 잡은 겁니다.

또 이런 선언법도 가능합니다.

char hello[6] = "hello";

그렇다면 우리가 문자열의 길이를 알고 싶다면 널문자가 나타나기 전까지만 세어주면 문자열의 길이를 알 수 있겠네요.

#include <stdio.h>

int main() {
        char *ptr = "ABCDEF";
        int len = -1;
       
        while (*(ptr+(++len)));
        printf("문자열 길이: %d\n", len);
}

그 결과는 이렇겠네요.

 

len=-1인 이유는 null문자 이전까지만 세어주기 위함입니다. while의 조건절은 null이면 멈추어 버립니다. 여기까지는 쉽네요. 포인터로는 어떻게 표현할까요?

사실 문자열("~~~~")은 그 문자열이 시작되는 주소를 가리키게 됩니다. 주소를 가리킨다!?  그러면 포인터가 생각나지 않나요?

왜냐면 주소를 포인터로 가리키면 문자열을 찾을 수 있으니까요.

그러면 이렇게 선언할 수 있을까요?

char *ptr = "hello";

포인터 ptr은 "hello"라는 문자열을 가리키는 포인터입니다. 

그림에서 보는 것과 같이 ptr은 문자열 "hello"의 주소를 가리키고 있고, 그렇기 때문에 참조가 가능한 상태가 됩니다. 그렇다면 어떤 포인터 역시 hello를 가리킨다면 그 주소는 같을까요?

코드와 결과로 확인해보도록 합시다.

 

#include <stdio.h>  int main() {   	char *ptr1 = "hello"; 	char *ptr2 = "hello"; 	printf("%s, %s\n", ptr1, ptr2); 	printf("%p, %p\n", ptr1, ptr2); } #include <stdio.h> 

int main() {   
        char *ptr1 = "hello"; 
        char *ptr2 = "hello"; 

        printf("%s, %s\n", ptr1, ptr2);
        printf("%p, %p\n", ptr1, ptr2);
}

 

같다는 것을 알 수 있습니다. 우리는 이런 그림을 그려볼 수 있겠네요.

ptr1과 ptr2는 서로 같은 문자열을 가리킵니다.  배열과 포인터에 대해서 선언방법은 그렇게 차이가 없어보이죠?

그렇다면 문자열 배열과 포인터는 서로 같은 성질을 갖고 있을까요?

만약 아래와 같은 코드를 입력한다 arr에 ptr가 가리키는 문자열을 넣으라는 거겠죠??

char arr[10] = "world";

char *ptr = "hello";

arr = ptr;

 "hello"라는 문자열의 길이는 배열크기보다 작기 때문에 들어갈 것입니다. 이렇게 생각하셨다면 다시 생각해봅시다. 오류나니까요.

문자열 "hello" 그 주소 자체를 반환합니다. 그러니까 "hello"의 시작주소가 되는 것이죠. 그것이 ptr이 갖고 있는 값입니다.

arr자체는 arr[0]의 주소, 즉 배열의 시작 위치를 말합니다. 이러한 시작 주소를 마음대로 ptr이 가리키고 있는 주소로 바꿀 수 없습니다.

이 의미는 더 쉽게 풀어서 이야기하면

int a = 0;

int b = 30;

&a = b;

랑 유사한 짓거리를 하는 것이라는 거죠. 마치 a의 주소를 b의 값으로 변경하라는 것과 유사하게 되어버립니다.

하지만 그 반대는 가능합니다. 이렇게요.

char arr[10] = "world" ;

char *ptr = "hello";

ptr = arr;

ptr은 주소를 갖을 수 있는 포인터, arr은 arr[0]의 주소! 말이 되죠 이건??

그러니 ptr은 arr과 동일한 곳을 가리키게 되는 겁니다.

만약

arr = ptr;

이걸 죽어도 써야겠다. 난 arr에다가 ptr의 문자열을 진짜 안쓰면 디질거 같다.  하시는 분들은 ptr의 문자열을 복사해서 쓰는 방법밖에 없습니다.

strcpy(arr, ptr)

이렇게 하시면 arr에 ptr이 가리키는 문자열을 그대로 복사해서 arr에 쑤셔 넣습니다. 주의 할 사항은 arr의 크기는 ptr이 가리키고 있는 문자열의 길이 이상으로 커야한다는 겁니다.

그렇지 않으면 런타임 오류납니다. 컴파일에서 문자열의 길이를 검사하지 않습니다!

이 오류가 바로 그 유명한 버퍼오버플로우(buffer overflow)가 됩니다. 취약점인거죠. 버퍼오버플로우를 통해 해커는 함수의 return 주소를 변경하여 자신의 실행코드를 실행합니다. 별짓을 다할 수가 있게 됩니다그래서 그 대안으로 strncpy, strncat 이런것이 나오게 된겁니다.

그리고 또!

포인터와 배열에는 다른 차이점이 있습니다.

문자열을 초기화 할때 배열은 배열 원소를 변경할 수 있지만, 포인터는 배열의 원소를 바꿀 수 없습니다. 즉, 포인터로 초기화 한다면 상수적인 성격을 띈다라는 것입니다.

가령, 아래 코드가 있다면 ptr[0] 변경시 오류가 발생합니다.

char hello[10] = "hello";

char *ptr = "hello";

ptr[0] = 'H';  //오류

hello[0] = 'H';

하지만

char hello[10] = "hello";

char *ptr = hello; 

ptr[0] = 'H'; 

hello[0] = 'H';

이건 오류가 나지 않습니다. 왜냐면 hello는 배열이거든요. 배열은 그 원소의 값이 변경가능합니다. ptr은 배열의 시작주소를 참조하고 있는 포인터이기 때문입니다.

 

그래서 이러한 strcat를 써먹을때도

#include <stdio.h>
#include <string.h>  

int main() {
        char *hello = "hello, ";  
        strcat(hello, "world");
        printf("%s\n", hello);
}

가 아닌

#include <stdio.h>
#include <string.h>

int main() { 
        char hello[20] = "hello, ";  
        strcat(hello, "world");
        printf("%s\n", hello);
}

 

이런 형태나

#include <stdio.h>
#include <string.h>  

int main() {  
        char hello[20] = "hello, ";
        char *ptr = hello;  
        strcat(ptr, "world");  
        printf("%s\n", ptr);
}

이런식으로 쓰여야 한다는 겁니다. 이렇게 간단하게 문자열과 포인터에 대해서 알아보았습니다. 부족한 점은 나중에 또 보충 설명해보도록 할게요 ㅎㅎ

 

 

반응형
블로그 이미지

REAKWON

와나진짜

,

배열과 포인터

 

배열과 포인터는 아주 유사한 성격을 갖고 있습니다. 배열은 뭘까요?

 

우선 배열에 대해서 알아봅시다. 배열은 같은 자료형이 연속된 공간으로 나열되있는 것을 뜻합니다. 배열에 대해서 조금 더 쉽게 접근하기 위해서 index를 갖는데요. index를 통해 빠르게 배열 원소에 접근하게 됩니다.

 

int a[5]={5, 3, 1, 2, 4};

 

이와 같은 배열이 있다면 메모리의 구조는 바로 이렇게 구성될겁니다.

 

 

왜 이런식으로 배열이 구성이 될까요??

 

왜 배열의 원소는 4바이트나 되는 걸까요.

그 이유는 int가 4바이트이기 때문입니다. 따라서 4바이트씩 주소가 증가하고 있는 겁니다.

그렇다면 char형의 배열은 한 원소의 크기가 1바이트이고, long long배열의 한 원소 크기는 8바이트가 되는 겁니다. got it?

 

 

사실 a라는 변수는 a[0]의 주소값을 갖고 있습니다. 배열의 시작주소를 가지고 있다는 거지요.

 

따라서 a는 &a[0]와 같은 값을 나타냅니다. a는 a[0]의 주소를 가리키고 있다는 겁니다. 

a가 주소값을 가지고 있다고??

그렇다면 a가 가지고 있는 주소를 참조한다면 *a로 a가 참조하는 값을 구할 수 있겠네요. 그러면 정말 a[0]의 값을 참조할까요? 

코드로 확인해보세요.

 

 

이제부터는 다른 배열의 다른 연산법을 알아보겠습니다. a는 a[0]을 가리키고 있는 포인터라고 했습니다. a[0]은 5의 값을 가지고 있습니다. 

헌데, a가 a[0]를 가리키고 있으니, *a는 a[0]의 값을 나타내게 되는 것이지요.

 

그렇다면 이렇게 변형해볼까요?

*(a+0) 는 *a와 같은 뜻이 될까요? 아마 그렇겠죠?

그렇다면 *(a+1)은 어떤 값을 나타나게 될까요?? 결론부터 말씀드리면 a[1]의 값을 갖게 됩니다. 어떤 포인터의 +1은 그것이 참조하고 있는 자료형의 크기만큼 주소를 더하라는 의미가 됩니다. 

 

그렇다면 *(a+i)는 a[i]가 되겠네요!! 와우!!

 

이제껏 지껄인 말이 확실한지 코드로 확인해보도록 하겠습니다.

 

#include <stdio.h>

int main() {
	int i;
	int a[5] = { 5,3,1,2,4 };
	
	
	
	printf("a의 주소 &a[0] = %p, a = %p \n", &a[0],a);
	printf("a의 값: %d\n", *a);
	for (i = 0; i < 5; i++)
		printf("\t 주소 %p  a[%d] : %d, *(a+%d) : %d\n",(a+i),i,a[i],i,*(a+i));

}

 

 

다음 사진은 결과를 보여줍니다.

 

 

 

제가 설명한 내용이 맞나요??

a의 값은 a[0]의 주소와 같네요!! 주소 값은 역시 4바이트씩 증가합니다. 

아까 포인터 연산 역시 제가 설명한 대로 나오고 있습니다.

포인터로 배열을 참조할 수 있습니다.

 

방금 전 배열 a를 포인터 ptr로 참조해보도록 하지요.

 

int *ptr = a;

 

로 간단하게 a라는 배열을 포인터로 참조할 수 있습니다.

a 역시 배열의 주소를 가지고 있고 ptr 역시 배열 a를 가리키고 있습니다. 그래서 a와 ptr은 같은 곳을 바라보게 되는 거죠.

 

 

그러면 ptr 역시 a가 가지는 특성을 그대로 가지고 있을까요?

코드로 확인해봅시다. 컴퓨터는 거짓말을 하지 않으니까

 

 

#include <stdio.h>

int main() {
	int i;
	int a[5] = { 5,3,1,2,4 };
	int *ptr = a;
	
	
	
	printf("a의 주소 &a[0] = %p, a = %p \n", &a[0],a);
	printf("a의 값: %d\n", *a);
	
	for (i = 0; i < 5; i++)
		printf("\t 주소 %p  a[%d] : %d, *(a+%d) : %d\n",(a+i),i,a[i],i,*(a+i));

	printf("\n");

	printf("ptr이 가리키는 주소 : %p\n", ptr);
	for (i = 0; i < 5; i++)
		printf("\t 주소 %p  p[%d] : %d, *(p+%d) : %d\n"
			,(ptr+i),i,ptr[i],i,*(ptr+i));
	
	
}

그 결과입니다.
 

 

정확히 똑같은 성질을 갖고 있다는 것을 알 수 있습니다.

 

그러면 둘의 차이는 없을까요??

 

이 둘의 차이는 크기에서 차이가 납니다.

sizeof연산을 한 번 해보도록 하지요.

 

 

#include <stdio.h>
int main() {
	int i;
	int a[5] = { 5,3,1,2,4 };
	int *ptr = a;

	printf("a의 사이즈 :%d, ptr의 사이즈 :%d\n", sizeof(a), sizeof(ptr));
}

 

결과

 

음.....

a의 사이즈는 20이고, ptr의 사이즈는 4바이트네요.

a는 배열의 크기를 갖고 있습니다.

a는 int형(4바이트) 배열로 5개가 할당되어있으니 20바이트이지요.

 

하지만 ptr은 단순히 그 배열을 참조해야하기 때문에 주소값만 저장할 수 있는 공간이면 충분합니다. 따라서 4바이트면 충분합니다.

 

그리고 배열은 상수화가 된다는 것이 특징입니다. 배열의 원소는 변경가능합니다. 하지만 배열 변수를 다른 값으로 변경할 수는 없습니다.

 

int a[5];

int b[5] = {5, 3, 1, 2, 4} ;

 

a=b;

 

a는 b와 크기가 같고 자료형이 같음에도 불구하고 이와 같은 코드는 컴파일 에러를 불러일으키게 되는 겁니다.

 

a는 a[0]의 주소입니다. b 역시 b[0]의 주소입니다.

주소는 read-only입니다. 즉, 값을 대입할 수가 없습니다 절대로!

 

 

위의 코드는 이것과 같은 얘기가 됩니다. 

&a[0] = &b[0] 이라는 거죠.

이제까지 이야기한 내용을 이해하셨다고 한다면 이해가 갈겁니다.

 

주소는 포인터만이 참조할 수 있는 포인터만의 특성입니다.

 

그러니까 위의 코드는 이렇게 바뀌어야합니다.

 

int *a;

int b[5] = {5, 3, 1, 2, 4} ;

 

a=b; //a=&b[0]과 같음.

 

배열과 포인터의 관계 이해하셨는지요??

이것으로 배열과 포인터의 관계를 마치겠습니다~~~

반응형
블로그 이미지

REAKWON

와나진짜

,

C언어 포인터란?  포인터라쓰고 탈모라 읽는다

C언어에서 포인터는 프로그래밍을 하면서 아주 어렵게 배우는 주제이기도 합니다. 어렵다기보다는 헷갈리는 경우가 많아서 멘탈붕괴가 오고는 하죠. 포인터는 말 그대로 무엇을 가리키는 놈입니다. 무엇을 가리킬까요??

다른 변수의 주소를 가리킵니다! 

모든 변수는 그 데이터가 저장되는 공간의 주소를 갖고 있습니다.

그것을 어떻게 표현할까요??

int B = 4;

int *A = &B;

이렇게 표현하게 됩니다. A는 B의 주소를 값으로 갖고 있다는 의미랍니다. 만약 B의 주소가 0x20이고 B의 값은 4라고 할때 A의 값은 0x20이 됩니다.

만약 B가 가지고 있는 값을 가져오고 싶다고 할때는 *A로 값을 참조할 수 있습니다.

그림으로 나타내면 이런 식으로 나타낼 수 가 있겠지요.

 

정말 제 말대로 되는 지 코드로 살펴보도록 하지요.

#include <stdio.h> 
int main() { 
        int B = 4;   
        int *A = &B;    
        printf("B의 값:%d\n", B);
        printf("B의 주소 :%p\n", &B);   
        printf("A의 값:%p\n", A);       
        printf("A가 참조하는 값 :%d\n", *A);     
}

 

네, 그렇게 나오네요
 
그렇다면 이중포인터는 무엇을 말할까요??
똑똑하신 여러분은 이미 알고 계시겠지만 포인터를 2번 쓰는 것을 말합니다.
아래 그림과 같은 상황이 바로 이중 포인터라는 것이죠, 포인터의 포인터! 스트레스의 향연

A는 B를 가리키고 있고, B는 C를 가리키고 있습니다.

int C = 10;

int *B = &C;

int **A = &B;

이렇게 쓸 수 있다는 거지요. A에 **(포인터의 포인터, 이중포인터)가 붙어있는 것을 확인 할 수 있네요

A는 B의 주소값을 값으로 가지고 있고, B는 C의 주소값을 값으로 갖고 있습니다. 그렇다면 A가 B의 값을 참조하려고 한다면 *A가 되겠죠?? 그러면 무슨 값이 나올까요??

바로 B가 가지고 있는 값, C의 주소입니다. A가 C의 값을 참조하기 위해서는 한 번 더 가리켜야하는데요.

그때 더블포인터가 사용이 되는 것입니다.  **A는 10을 확인할 수 있을 겁니다.

코드로 확인해 보도록 하죠.

#include <stdio.h>

int main() { 
        int C = 10; 
        int *B = &C;
        int **A = &B;

        printf("C의 값 : %d\n", C); 
        printf("C의 주소 : %p\n", &C); 
        printf("B의 값 : %p\n", B); 
        printf("B의 주소 : %p\n", &B); 
        printf("B가 참조하는 값 : %d\n", *B);
        printf("A의 값 : %p\n", A); 
        printf("A가 참조하는 값 : %p\n", *A); 
        printf("A가 참조하는 값이 참조하는 값 : %d\n", **A);
}

결과는 아래와 같습니다.

 

 

앞서 말한 대로 C의 주소를 B가 값으로 가지고 있고, B의 주소를 A가 값으로 가지고 있는 걸 확인할 수 있겠죠?? *A는 C의 주소값을 갖고 있습니다. 왜냐면 *A는 B의 값을 가리키고 있는 데 B의 값은 C의 주소이니까요!

그렇다면 삼중포인터는 어떻게 될까요??

뭐하러 어려운 포인터를 쓰나?

포인터는 언제 써먹을 수 있을까요? 대표적으로 다음과 같은 상황일때 사용합니다. 

어떤 함수가 있다고 칠게요. 아주 단순합니다. 그저 매개변수인 a와 b의 값을 교환하는 swap이라는 함수입니다.

void swap(int a, int b) {
        int temp; 
        temp = a; 
        a = b;
        b = temp;
}  

int main() {
        int x = 10;
        int y = 20; 
        swap(x, y); 
        printf("x: %d, y: %d\n", x, y); 
}

 

call-by-value

우리는 x와 y의 값을 바꾸고 싶다 이겁니다. 그 과정을 살펴보지요.

1. 우선 temp에 a의 값을 넣습니다. 그렇다면 10이 temp의 값에 들어있겠네요.

2. 그리고 b의 값을 a에 집어 넣습니다. 그렇다면 a의 값은 b의 값인 20이 들어가겠군요.

3. 마지막으로 변수 b에 temp값을 넣습니다. temp는 10이었으니까 b는 10이 되어지겠군요.

이제 함수에서 빠져나옵니다. 그 값이 바뀌어있을까요? 아닙니다. 이 함수는 a와 b를 함수 내부에서만 교체할 뿐이지, 함수 호출이 끝나고 반환되서도 x와 y의 값은 변함이 없습니다. 왜 그럴까요?

함수인자 a와 b는 전달받은 매개변수의 값(x, y)만 복사해오기 때문입니다.

예를 들면 졸업증명서 원본이 있고, 그걸 복사해서 사본을 갖고 있습니다. 사본을 불에 태워도 원본이 같이 탈까요? 동시에 같이 태우지 않는 한 원본은 살아있습니다. 이렇기 때문에 main에서 swap을 호출하고 나서도 x와 y의 값은 변함이 없습니다.

이런 함수 호출 방법을 Call-By-Value라고 부르는 것입니다.

 

call-by-reference

그렇다면 우리는 어떻게 함수를 변경해야 할까요?

그럴때 포인터가 사용이 됩니다.

void swap(int *a, int *b) { 
        int temp; 
        temp = *a; 
        *a = *b; 
        *b = temp;
}

int main() { 
        int x = 10; 
        int y = 20; 

        swap(&x, &y);
        printf("x: %d, y: %d\n", x,y);
}

a는 x의 주소를 가리키고 있고, b는 y의 주소를 가리키고 있습니다. a는 x의 주소를 참조하고 있기 때문에 x에 영향이 주게 되는 겁니다. b 역시 마찬가지가 되겠구요.

그림을 통해서 설명해보도록 하지요.

1.  temp = *a

temp에 a가 가리키는 값을 대입합니다. a가 가리키는 값은 10입니다.

 

2.  *a = *b

a가 가리키는 값에 b가 가리키는 값을 넣습니다. 그림과 같이 x의 값이 변경됩니다.

3. *b = temp

b가 가리키는 값에 temp의 값을 저장합니다. temp는 방금전 10이었기 때문에 b가 가리키는 곳(y)의 값은 10으로 변경됩니다.

 

결과는 어떨까요?

결과 역시 우리가 예상했던 대로군요. 우리는 이와 같이 함수에서의 조작이 외부 변수에 조작이 가해질때, 그럴때를 Call-By-Reference라고 합니다.

반응형
블로그 이미지

REAKWON

와나진짜

,