포인터 시작 (C)

  • 57 minutes to read

다음 동영상 강의는 제가 2007년 01월 10일 오프라인 강의했던 자료입니다. 오래 전 자료이지만 지금도 개념은 동일합니다.

자바캠퍼스 - C 언어 포인터 시작하기

코드: 01.포인터시작.c

// 포인터시작.c
// pointer-start-c.c 
#include <stdio.h>

int main(void)
{
    int a, b, c; // 값형 변수
    int* p; // 포인터(참조;주소)형 변수

    a = 100;
    b = 200;

    p = &c;

    c = a + b;

    printf("a : %d\n", a);
    printf("b : %d\n", b);
    printf("c : %d\n", c);
    printf("p : %p\n", p); // 번지
    printf("*p : %d\n", *p); // 참조하고 있는 공간의 값 == 역참조 

    return 0;
}

결과

 a: 100
 b: 200
 c: 300
 p: 00EFFE80
*p: 300

예제: 02.포인터연산.c

// 포인터연산.c
#include <stdio.h>

int main(void)
{
	// 값형 변수
	int a, b, c;
	// 포인터(참조)형 변수
	int* pa, * pb, * pc;

	a = 10;
	b = 20;
	c = 0;

	pa = &a;
	pb = &b;
	pc = &c;

	*pc = *pa + *pb;

	printf("%d, %d, %d\n", *pa, *pb, *pc);

	return 0; 
}

실행

10, 20, 30

예제: 03.정수형포인터.c

// 03.정수형포인터.c
#include <stdio.h>

int main(void)
{
    // 값형 변수
    int a = 365;
    // 참조형 변수
    int* pa = &a;

    printf("a : %d\n", a);//365
    printf("&a : %p\n", &a);//1244992
    //printf("*a : %d\n", *a);

    // pa에 들어있는 다른 변수(공간)의 번지 수
    printf("pa : %p\n", pa);//1244992번지
    // pa가 참조하고 있는 변수에 들어있는 값
    printf("*pa : %d\n", *pa);//365
    // pa 변수 자체의 고유 어드레스(주소)
    printf("&pa : %p\n", &pa);//1244980

    return 0;
}

실행

a : 365
&a : 00EBFBF4
pa : 00EBFBF4
*pa : 365
&pa : 00EBFBE8

포인터_실수형.c

/*
    7.4. 예제. 실수형 포인터 변수 선언 : 포인터_실수형.c
*/
#include <stdio.h>

int main(void)
{
    float a, b, c;
    float* p;

    a = 100.0, b = 200.0;
    p = &c;	// c의 주소를 p에 기억

    *p = a + b; // c = a + b
    printf("%.1f + %.1f = %.1f\n", a, b, *p);
    printf("%x %x %x %x %x\n", &a, &b, &c, p, &p);
}

//100.0 + 200.0 = 300.0
//12ff7c 12ff78 12ff74 12ff74 12ff70
100.0 + 200.0 = 300.0
eff780 eff774 eff768 eff768 eff75c

포인터 관련 오프라인 강의 소스 모음

01.포인터변수.c

#include <stdio.h>

int main(void)
{
    int a = 10;

    int* b;

    b = &a;//a주소 : 별칭

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

    return 0;
}
10 10

포인터 변수의 값 가져오기

단항 연산자인 * 연산자를 사용하면 포인터가 가리키는 변수의 값을 가져옵니다. 단항 * 연산자를 포인터 간접 참조 연산자로 부릅니다.

* 역참조(dereference)

포인터 변수가 가리키는 곳의 실제 데이터를 가져올 때에는 * 기호를 포인터 변수에 붙여 값을 가져올 수 있습니다. 프로그래밍에서 포인터에 포함된 주소의 정보에 액세스하는 것을 역참조라고 합니다. 물론, 그냥 참조로 표현해도 됩니다.

정수형 포인터 변수를 선언 및 참조 후 사용하는 방법을 코드로 살펴보겠습니다.

코드: IntegerPointer.c

#include <stdio.h>

int main(void)
{
    // 정수형 변수 선언 및 초기화
    int width = 1920;
    int height = 1080;

    // 정수형 포인터 선언 및 참조
    int* w = &width;
    int* h = &height;

    // 역참조로 값 출력
    printf("FHD: %d X %d\n", *w, *h);

    // 역참조(dereference)로 값 설정
    *w = 3840;
    *h = 2160;

    // 역참조로 값 출력
    printf("UHD: %d X %d\n", *w, *h);

    return 0;
}
FHD: 1920 X 1080
UHD: 3840 X 2160

일반 변수와 포인터 변수

C 언어 포인터로 정수 형식을 참조하는 정수형 포인터를 소개합니다.

다음 코드를 그림으로 표현해 보겠습니다.

코드: pointer_integer.c

int a = 20, b = 35, c = 0; // 정수 변수 선언
int* pa, * pb, * pc; // 정수 참조 변수 선언

pa = &a; pb = &b; pc = &c; // 각각의 변수를 참조

*pc = *pa + *pb; // 연산

printf("%d + %d = %d\n", *pa, *pb, *pc); // 20 + 35 = 55

그림: 일반 변수를 참조하는 포인터 변수

일반 변수를 참조하는 포인터 변수

포인터_포인터변수.c

포인터_포인터변수.c

/*
    포인터 변수: 특정 변수의 주소를 담을 수 있는 그릇
*/
#include <stdio.h>

int main(void)
{
    int i, j, k; // 일반 변수 : 값
    int* p;		 // 포인터 변수 : 다른 변수의 주소, 레퍼런스(참조) 변수

    i = 10;
    j = 20;
    k = 0;

    p = &k;	// 변수 k의 주소를 p에 기억

    printf("%d %d %d %d\n", i, j, k, *p); // *p => 포인터 변수가 참조하고 있는 변수의 값

    *p = i * j; // k = i * j;

    printf("%d %d %d %d\n", i, j, k, *p);

    return 0;
}
10 20 0 0
10 20 200 200

포인터_정수형.c

pointer_integer.c, 포인터_정수형.c

#include <stdio.h>

int main(void)
{
    int i = 10;
    int j = 20;
    int k = 0;

    int* p1;
    int* p2;
    int* p3;

    // 각각의 일반변수를 포인터(참조형) 변수에 참조
    p1 = &i;
    p2 = &j;
    p3 = &k;

    *p3 = *p1 * *p2;

    printf("%d * %d = %d\n", *p1, *p2, *p3);

    return 0;
}
10 * 20 = 200

포인터_포인터변수의주소출력.c

pointer_variable_address.c, 포인터_포인터변수의주소출력.c

/*
	변수(일반, 포인터)의 주소 출력: &
*/
#include <stdio.h>

int main(void)
{
	int i;
	int* p;

	i = 100;
	p = &i;

	printf("일반변수 i의 값: %d\n", i); // 100
	printf("포인터변수 p가 참조하고 있는 변수의 값: %d\n", *p); // 100

	printf("일반변수 i의 주소값: %u\n", &i); // %u(unsigned): 부호없는 정수
	printf("포인터변수 p의 주소값: %u\n", &p); // 
	printf("포인터변수 p의 값: %u\n", p); // i변수의 주솟값과 동일

	return 0;
}
일반변수 i의 값: 100
포인터변수 p가 참조하고 있는 변수의 값: 100
일반변수 i의 주소값: 2707094068
포인터변수 p의 주소값: 2707094104
포인터변수 p의 값: 2707094068

C 언어 포인터 변수 값 출력 방법 3가지

코드: 03.포인터변수의주소.c

#include <stdio.h>

int main(void)
{
    int a, * pa;

    a = 10;
    pa = &a;

    //[1] 값형 a변수
    printf("%d\n", a);//a의 값
    printf("%d\n", &a);//a의 메모리상의 주소 : 15727580
    //[2] 포인터형 pa변수
    printf("%d\n", pa);//pa변수의 실제값:a의주소값 : 15727580
    printf("%d\n", &pa);//pa변수의 주소:15727568
    printf("%d\n", *pa);//pa변수가참조하는곳의실제데이터

    return 0;
}

실행

10
15727580
15727580
15727568
10

강의

동영상 강의: C 언어_포인터_03_포인터변수의주소

포인터_실수형포인터.c

pointer_float_type.c, 포인터_실수형포인터.c

#include <stdio.h>

int main(void)
{
    float i;
    float* p; // 특정 주소(번지;Address)값을 담을 수 있는 그릇

    i = 100.0;
    p = &i;

    printf("p가 참조하고 있는 곳의 값: %.2f\n", *p);
    printf("p의 주소 값: %#x\n", &p); // 0xFFFFFF
    printf("p의 자체 값: %#x\n", p); // 변수 i의 주소값
    printf("i의 주소 값: %#x\n", &i); // 변수 i의 주소값

    return 0;
}

실행 결과의 주솟값은 실행 시점의 상황에 따라 다르게 나오는게 정상입니다.

p가 참조하고 있는 곳의 값: 100.00
p의 주소 값: 0x2daffb88
p의 자체 값: 0x2daffb64
i의 주소 값: 0x2daffb64
p가 참조하고 있는 곳의 값: 100.00
p의 주소 값: 0x8a93fad8
p의 자체 값: 0x8a93fab4
i의 주소 값: 0x8a93fab4

값형 변수와 참조형 변수

C 언어에서 변수 선언할 때 값형 변수와 참조형 변수로 구분할 수 있습니다. 참조형 변수가 포인터입니다.

코드: pointer_variable_five.c

// pointer_variable_five.c*** ```C
/*
	값(Value)형 변수/배열
	포인터(참조;Reference)형 변수/배열
*/
#include <stdio.h>

int main(void)
{
	// Value Type 변수
	int a = 10;

	// Reference Type 변수: 포인터 변수
	int* pa;

	// 참조 추가
	pa = &a; // 포인터 변수에 주소 대입

	*pa = 20; // 역참조를 사용하여 포인터 변수가 참조하는 변수에 값 대입

	// 값형 변수 표현
	printf("[1] %d\n", a); // 20
	printf("[2] %p\n", &a); // 000000FFC78FFC24 번지: 계속 바뀌는 부분

	// 포인터형 변수 표현
	printf("[3] %p\n", pa); // 000000FFC78FFC24 번지
	printf("[4] %p\n", &pa); // 000000FFC78FFC48 번지
	printf("[5] %d\n", *pa); // 20

	return 0; 
}
[1] 20
[2] 000000FFC78FFC24
[3] 000000FFC78FFC24
[4] 000000FFC78FFC48
[5] 20

[2]번과 [3]번은 같은 주솟값이 나오고 [4]번 출력 결과는 이웃된 근처의 주솟값이 나옵니다. 이 주솟값은 계속 바뀌는 데이터입니다.

C 언어 포인터 - 값형 변수와 참조형 변수의 차이

동영상 강의: 값형 변수와 참조형 변수의 차이

예제: valuetype-referencetype.c

// valuetype-referencetype.c
// 값형 변수와 참조형 변수의 차이 
#include <stdio.h>

void plus(int* p) // 매개 변수로 포인터를 받음 
{
    *p = *p + 10; // 역참조에 의해서 main 함수의 i 변수 값을 증가
    printf("*p = %d\n", *p);
}

int main(void)
{
    int i = 10; // main과 plus에서 데이터 저장 공간으로 사용
    int j = 20; // 값형 변수 선언과 동시에 초기화

    int* p; // 포인터 변수 선언 
    p = &i; // 참조형 변수에 값형 변수 참조 

    plus(p); // 넘겨준 포인터 변수의 값을 10 증가 

    printf("i : %d, j = %d\n", i, j);

    return 0; 
}

실행

plus function: *p: 20
main function: i: 20, j: 20, *p: 20

포인터 변수는 값형 변수를 참조

포인터 변수는 참조형 변수로도 불립니다. 포인터 변수는 변수 위치에 직접 값이 저장되는 값형 변수를 참조합니다.

먼저 다음 샘플 코드를 살펴보세요.

// pointer_reference.c
int v = 10; // 값형 변수
int* p = &v; // 참조형(포인터) 변수

위 코드를 그림으로 표현해보면 다음과 같습니다.

[그림] 참조형 변수는 값형 변수를 참조

참조형 변수는 값형 변수를 참조

코드

// pointer_reference.c
// 포인터 변수는 값형 변수를 참조 
#include <stdio.h>

int main(void)
{
    int v = 10; // 값형 변수
    int* p = &v; // 참조형(포인터) 변수 
    printf("%d\n", *p); // 10
}

실행

10

값 형 변수와 참조형 변수의 차이

코드: valuetype-referencetype.c

// valuetype-referencetype.c
// 값형 변수와 참조형 변수의 차이 
#include <stdio.h>

void plus(int* p) // 매개 변수로 포인터를 받음 
{
    *p = *p + 10; // 역참조에 의해서 main 함수의 i 변수 값을 증가
    printf("plus function: *p: %d\n", *p);
}

int main(void)
{
    int i = 10; // main과 plus에서 데이터 저장 공간으로 사용
    int j = 20; // 값형 변수 선언과 동시에 초기화

    int* p; // 포인터 변수 선언 
    p = &i; // 참조형 변수에 값형 변수 참조 

    plus(p); // 넘겨준 포인터 변수의 값을 10 증가 

    printf("main function: i: %d, j: %d, *p: %d\n", i, j, *p);

    return 0; 
}
plus function: *p: 20
main function: i: 20, j: 20, *p: 20

정수 포인터와 실수 포인터

포인터는 이미 있는 정수 포인터와 실수 포인터를 참조해서 사용할 수 있습니다.

코드: NumberPointer.c

#include <stdio.h>

int main(void)
{
    int age = 21;
    const float PI = 3.14F;

    int* p1 = &age;
    float* p2 = &PI;

    printf("변수 age 값: %d\n", age);
    printf("변수 age 주소: %p\n", &age);

    printf("포인터 변수 p1의 값: %p\n", p1);
    printf("포인터 변수 p1의 참조 값(역참조): %d\n", *p1);
    printf("포인터 변수 p1의 주소: %p\n", &p1);

    printf("\n");

    printf("상수 PI 값: %.2f\n", PI);
    printf("상수 PI 주소: %p\n", &PI);

    printf("포인터 변수 p2의 값: %p\n", p2);
    printf("포인터 변수 p2의 참조 값(역참조): %.2f\n", *p2);
    printf("포인터 변수 p2의 주소: %p\n", &p2);

    return 0;
}
변수 age 값: 21
변수 age 주소: 00F6FE5C
포인터 변수 p1의 값: 00F6FE5C
포인터 변수 p1의 참조 값(역참조): 21
포인터 변수 p1의 주소: 00F6FE44

상수 PI 값: 3.14
상수 PI 주소: 00F6FE50
포인터 변수 p2의 값: 00F6FE50
포인터 변수 p2의 참조 값(역참조): 3.14
포인터 변수 p2의 주소: 00F6FE38

정수 포인터와 실수 포인터

현재 예제의 실행 결과의 주소값은 매번 달라집니다. 포인터 변수에는 참조하고 있는 변수의 주솟값이 저장됩니다. 포인터 변수 p1의 주소를 &p1 형태로 출력해보면 변수 age의 주소가 나오는 것을 확인할 수 있습니다. 이러한 포인터에 들어 있는 값 자체를 얻고자할 때에는 * 기호를 사용하여 역참조하면 됩니다.

널 포인터 사용하기

NULL 키워드를 사용하는 널 포인터(null pointer)는 단어 그대로 아무것도 가리키지 않는 상태를 의미합니다.

포인터 변수에 NULL로 초기화하면 메모리가 할당되지 않은 상태입니다.

코드: null_pointer.c



특정 포인터 변수가 NULL인지를 비교해서 널 포인터라면 새롭게 메모리를 할당하는 식으로 사용될 수 있습니다.

포인터 변수 주소 증가 또는 감소

  • *p : 역참조로 포인터 변수가 가리키는 곳의 값을 반환
  • *p++ : *p가 가리키는 곳의 값을 1 증가
  • *++p : p를 1증가(포인터 주소를 다음 주소로 증가 후)한 다음에 그곳의 값을 가져오기
  • ++*p : *p가 가리키는 곳의 값을 가져와서 1증가해서 반환
  • (*p)++ : *p가 가리키는 곳의 값을 가져와서 사용 후 나중에 1증가

포인터로 문자열 저장하기

C 언어에서는 문자 포인터를 사용해서 문자열을 저장할 수 있습니다. 문자열을 저장할 때에는 포인터 변수 선언과 동시에 문자열 리터럴을 초기화하거나 선언 따로 초기화 따로를 할 수 있습니다.

코드: StringPointer.c

#include <stdio.h>

int main(void)
{
    //[1] 문자열 선언과 동시에 초기화
    char* name = "김서울";

    //[2] 문자열 포인터 선언 후 초기화 
    char* address;
    address = "SEOUL"; // 문자열(문자 배열)의 시작 주소를 포인터에 저장 

    // 전체 출력
    printf("%s - %s\n", name, address);

    // 부분 출력(인덱스 이후로 출력): 한글은 2byte 문자
    printf("%s - %s\n", (name + 2), (address + 3));

    return 0;
}
김서울 - SEOUL
서울 – UL

코드에서처럼 문자열 포인터 선언 후 따로 초기화할 때에는 *address가 아닌 address에 문자열 리터럴을 할당해야 합니다. 왜냐하면 문자열 리터럴은 그 자체가 배열의 시작 주소를 담고 있기 때문입니다.

포인터_문자열입출력.c***

코드: 포인터_문자열입출력.c

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>

int main(void)
{
	//char* s;
	char s[10];

	//s = "안녕";
	scanf("%s", s);

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

	return 0; 
}
안녕
안녕

포인터로 문자열 저장하고 출력하기

pointer_array_string.c, 포인터배열_문자열.c

/*
	포인터를 사용하여 문자배열(문자열)을 저장하기
*/
#include <stdio.h>

int main(void)
{
	char c;
	char* p;

	c = 'A';
	p = "Hello World"; // "Hello World\0"

	printf("%c\n", c); // A
	printf("%s\n", p); // Hello World

	return 0;
}
A
Hello World

C 언어 포인터 전체 복습 설명

C 언어 포인터에 대한 전체 복습 강좌입니다.

강의

https://youtu.be/gbq9-8xOpXE

코드: 포인터설명_참조형.c

// 포인터설명_참조형.c
// 포인터(Pointer) : 참조형
#include <stdio.h>

void main()
{
	// 값형(Value Type) 변수 : 해당 변수 이름공간에 데이터 저장
	int a;
	a = 10;
	// 포인터형(Pointer Type;Reference Type) 변수 : 
	// 다른 변수의 공간을 참조(가리키는)하는 형태
	int* p;
	p = &a;//a변수의 주소(Address)를 포인터형 변수에 기록
	// 출력
	printf("a의 값 : %d\n", a);//10
	printf("a의 주소 : %d\n", &a);//1244992

	printf("p의 값 : %d\n", p);//1244992
	printf("p가 가리키는 값 : %d", *p);//10
	printf("p의 주소 : %d\n", &p);//1244992+-???=1244980
}

실행

a의 값 : 10
a의 주소 : 20184744
p의 값 : 20184744
p가 가리키는 값 : 10p의 주소 : 20184732

코드: 포인터배열설명.c

// 포인터배열설명.c
// 포인터 배열
#include <stdio.h>

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

	// 포인터 형 변수 선언
	int* p;

	p = a;//&a[0];//배열명 자체가 배열의 시작 주소

	for (i = 0; i < 5; i++)
	{
		printf("배열: %d, 포인터: %d\n", a[i], *(p + i));
	}
}

실행

배열: 2, 포인터: 2
배열: 1, 포인터: 1
배열: 5, 포인터: 5
배열: 4, 포인터: 4
배열: 3, 포인터: 3

코드: 포인터_문자열처리설명.c

// 포인터_문자열처리설명.c
#include <stdio.h>

void main()
{
    char c = 'A';	// 값형
    char* s; // string s; // 참조형

    s = "안녕하\0세요.";

    printf("%c %s\n", c, s);
    printf("%s\n", s + 7);//영문1Byte, 한글2Byte
}

실행

A 안녕하
세요.

코드: 포인터_포인터배열설명.c

// 포인터_포인터배열설명.c
// 포인터 배열
#include <stdio.h>

void main()
{
    char* s[3];

    s[0] = "AA";
    s[1] = "BB";
    s[2] = "CC";

    printf("%s %s %s\n", s[0], s[1], s[2]);
}

실행

AA BB CC

11 포인터를 사용한 문자열 출력 제어

pointer_array_string_literal.c, 포인터배열_문자열출력.c

// 포인터를 사용한 문자열 출력 제어
#include <stdio.h>

int main(void)
{
    // C언어: 문자열 처리 키워드: X, Java/C#: String 키워드
    //string s = "안녕하세요...";
    char* str;

    str = "안녕하세요. 반갑습니다."; // 한글/일어/중국어: 2byte코드, 영어: 1byte코드

    printf("%s\n", str);		// 전체 출력
    printf("%s\n", str + 12);	// 반갑습니다.
    printf("%s\n", str + 3);	// '녕'을 반쪽만 출력하다보니 깨져서 보여짐

    str = "상수에 값을 덮어쓰기"; // 허용, scanf() 접근 불가
    printf("%s\n", str);		// 전체 출력

    return 0;
}
안녕하세요. 반갑습니다.
반갑습니다.
聆究셀? 반갑습니다.
상수에 값을 덮어쓰기

포인터로 문자열 배열 선언하기

C 언어에서 문자열 형식을 만들 수 있는 char* 데이터 타입으로 하나의 이상의 문자열을 담을 수 있는 문자열 배열을 선언할 수 있습니다. 문자열 배열은 선언 후 따로 각각의 요소를 문자열 리터럴로 저장할 수 있습니다.

코드: StringArray.c

#include <stdio.h>

int main(void)
{
    char* languages[3]; // 문자열 배열 선언

    languages[0] = "C";
    languages[1] = "C++";
    languages[2] = "C#";

    for (int i = 0; i < 3; i++)
    {
        printf("%s\n", languages[i]);
    }

    return 0;
}
C
C++
C#

문자열 리터럴은 그 자체가 첫 번째 문자의 시작 주소를 담고 있기에 포인터 배열의 각 요소에 대입되어 사용할 수 있습니다.

포인터로 배열의 배열 표현하기

pointer_array_zigzag.c, 포인터배열_배열의배열.c

// 배열의 배열
#include <stdio.h>

int main(void)
{
    char* arr[3]; // 기본: 문자 3개 저장, 포인터 배열: 문자열 3개를 저장해 놓을 그릇

    arr[0] = "C언어";
    arr[1] = "C++";
    arr[2] = "C#";

    for (int i = 0; i < 3; i++)
    {
        printf("%s \n", arr[i]);
    }

    return 0;
}
C언어
C++
C#

gets 함수로 문자열 입력

pointer_array_gets.c, 포인터배열_문자열입력.c

// 화면으로부터 문자열을 입력받아서 출력하는 프로그램: scanf() 대신에 gets() 함수 사용
#include <stdio.h>

int main(void)
{
	char str[100];

	printf("문자열을 입력하시오 : ");
	//scanf("%s", str);
	gets(str); // 한줄의 문자열을 읽어오는 함수

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

	return 0;
}
문자열을 입력하시오 : C Language
C Language

포인터를 사용한 기초 알고리즘

포인터를 사용하여 최댓값 구하기

algorithm_pointer_max.c, 알고리즘_포인터_최댓값.c

// 최대값을 구하는 알고리즘
#include <stdio.h>
#include <limits.h> // INT_MIN, INT_MAX

void main(void)
{
    //[1] Input
    int intNum[] = { -3, -2, -1, -4, -5 };
    int intMax = INT_MIN;
    int i;
    int* p; p = intNum; // &intNum[0];

    //[2] Process: MAX
    for (i = 0; i < 5; i++)
    {
        if (intMax < *(p + i))
        {
            intMax = *(p + i);
        }
    }

    //[3] Output
    printf("최대값: %d\n", intMax);

    return 0;
}
최대값: -1

포인터를 사용하여 배열 정렬하기

algorithm_pointer_selectionsort.c, 알고리즘_포인터_선택정렬.c

#include <stdio.h>

// 포인터를 사용한 선택 정렬 함수
void SelectionSort(int* p)
{
    int temp;
    for (int i = 0; i < 5 - 1; i++) {
        for (int j = i + 1; j < 5; j++) {
            if (*(p + i) > *(p + j)) {
                temp = *(p + i);
                *(p + i) = *(p + j);
                *(p + j) = temp;
            }
        }
    }
}

int main(void)
{
    //[1] Input
    int intNum[5] = { 3, 2, 1, 5, 4 };
    int temp;

    //[2] Process: 정렬알고리즘(선택, 버블, 퀵, ...)
    SelectionSort(intNum);
    //for (int i = 0; i < 5 - 1; i++)
    //{
    //    for (int j = i + 1; j < 5; j++)
    //    {
    //        if (intNum[i] > intNum[j])
    //        {
    //            temp = intNum[i];
    //            intNum[i] = intNum[j];
    //            intNum[j] = temp;
    //        }
    //    }
    //}

    //[3] Output
    for (int i = 0; i < 5; i++)
    {
        printf("%d\n", intNum[i]);
    }

    return 0;
}
1
2
3
4
5

값 형식(Value Type)과 참조 형식(Reference Type)

클래스와 구조체와 같은 데이터 타입을 말할 때 값 형식(Value Type)과 참조 형식(Reference Type)으로 구분짓기도 합니다.

  • 값 형식: 지금까지 다룬 int, double과 같은 데이터 구조는 내부적으로 구조체로 되어 있고 전형적인 값 형식의 데이터 구조입니다.
  • 참조 형식: 모든 클래스의 부모 역할을 하는 최상위 클래스인 Object 클래스와 이를 나타내는 object 키워드는 전형적인 참조 형식의 데이터 구조입니다.

값 형식(Value Type)

값 형식은 개체에 값 자체를 담고 있는 구조입니다.

그림: 값 형식

값 형식

참조 형식(Reference Type)

참조 형식은 개체가 값을 담고 있는 또 다른 개체를 포인터로 바라보는 구조입니다. 여러 값들이 동일한 개체를 가리킬 수 있습니다.

그림: 참조 형식

값 형식

멤버 액세스 및 요소 액세스 연산자

포인터에서 사용할 수 있는 연산자에는 -> (멤버 액세스) 연산자와 [] (요소 액세스) 연산자가 있습니다.

퀴즈

다음 중 동적으로 메모리를 할당하는 방법을 고르세요.

a. int *p = malloc(80);

정답: a

TODO:

포인터 관련 연산자

다음 연산자를 사용하여 포인터로 작업할 수 있습니다.

-

  • 산술 연산자: +, -, ++--
  • 비교 연산자: ==, !=, <, >, <=>=
NOTE

포인터를 사용한 작업에는 안전하지 않은 컨텍스트가 필요합니다. 안전하지 않은 블록을 포함하는 코드는 AllowUnsafeBlocks 컴파일러 옵션을 사용하여 컴파일해야 합니다.

Address-of 연산자 &

단항 & 연산자는 해당 피연산자의 주소를 반환합니다.

unsafe
{
    int number = 27;
    int* pointerToNumber = &number;

    Console.WriteLine($"Value of the variable: {number}");
    Console.WriteLine($"Address of the variable: {(long)pointerToNumber:X}");
}
// Output is similar to:
// Value of the variable: 27
// Address of the variable: 6C1457DBD4

& 연산자의 피연산자는 고정 변수여야 합니다. 고정 변수는 가비지 수집기의 작동에 영향을 받지 않는 스토리지 위치에 있는 변수입니다. 앞의 예제에서 로컬 변수 number는 스택에 있으므로 고정 변수입니다. 가비지 수집기에 의해 영향을 받을 수 있는 스토리지 위치에 상주하는 변수(예: 재배치됨)를 이동 가능한 변수라고 합니다. 개체 필드 및 배열 요소는 이동 가능한 변수의 예입니다. fixed 문으로 "fix" 또는 "pin"으로 할 경우 이동 가능한 변수의 주소를 가져올 수 있습니다. 가져온 주소는 fixed 문 블록 내에서만 유효합니다. 다음 예제에서는 fixed 문과 & 연산자를 사용하는 방법을 보여줍니다.

unsafe
{
    byte[] bytes = { 1, 2, 3 };
    fixed (byte* pointerToFirst = &bytes[0])
    {
        // The address stored in pointerToFirst
        // is valid only inside this fixed statement block.
    }
}

변수, 배열과 달리 상수 또는 리터럴 값의 주소를 가져올 수 없습니다.

포인터 간접 참조 연산자 *

단항 포인터 간접 참조 연산자 *는 피연산자가 가리키는 변수를 가져옵니다. 역참조 연산자라고도 합니다. * 연산자의 피연산자는 포인터 형식이여야 합니다.

PointerIndirection.cs

unsafe
{
    char letter = 'A';
    char* pointerToLetter = &letter;
    Console.WriteLine($"Value of the `letter` variable: {letter}");
    Console.WriteLine($"Address of the `letter` variable: {(long)pointerToLetter:X}");

    *pointerToLetter = 'Z';
    Console.WriteLine($"Value of the `letter` variable after update: {letter}");
}
// Output is similar to:
// Value of the `letter` variable: A
// Address of the `letter` variable: DCB977DDF4
// Value of the `letter` variable after update: Z

* 연산자를 void* 유형의 식에 적용할 수 없습니다.

산술 연산자 학습을 통해서 알고 있는 단항 연산자가 아닌 이항 연산자로서 사용되는 * 연산자는 해당 숫자 피연산자의 곱을 컴퓨팅합니다.

포인터 멤버 액세스 연산자 ->

화살표 연산자로 불리는 -> 연산자는 포인터 간접 참조멤버 액세스를 결합합니다. 즉, xT* 형식의 포인터이고 yT의 액세스 가능한 멤버인 경우 다음과 같이 사용될 수 있습니다.

x->y

위의 식은 아래의 삭과 동일합니다.

(*x).y

다음 예제에서는 -> 연산자의 사용법을 보여 줍니다.

public struct Coords
{
    public int X;
    public int Y;
    public override string ToString() => $"({X}, {Y})";
}

public class PointerMemberAccessExample
{
    public static unsafe void Main()
    {
        Coords coords;
        Coords* p = &coords;
        p->X = 3;
        p->Y = 4;
        Console.WriteLine(p->ToString());  // output: (3, 4)
    }
}

You cannot apply the -> operator to an expression of type void*.

-> 연산자를 void* 유형의 식에 적용할 수 없습니다.

포인터 요소 액세스 연산자 []

포인터 형식의 식 p의 경우 p[n] 양식의 포인터 요소 액세스는 *(p + n)으로 평가됩니다. 여기서 n은 암시적으로 int, uint, long 또는 ulong으로 전환할 수 있는 형식이어야 합니다.

다음 예제에서는 포인터 및 [] 연산자를 사용하여 배열 요소에 액세스하는 방법을 보여 줍니다.

unsafe
{
    char* pointerToChars = stackalloc char[123];

    for (int i = 65; i < 123; i++)
    {
        pointerToChars[i] = (char)i;
    }

    Console.Write("Uppercase letters: ");
    for (int i = 65; i < 91; i++)
    {
        Console.Write(pointerToChars[i]);
    }
}
// Output:
// Uppercase letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ

앞의 예제에서 stackalloc 식은 스택에 메모리 블록을 할당합니다.

NOTE

포인터 요소 액세스 연산자는 범위 이탈 오류를 검사하지 않습니다.

void* 형식의 식으로 포인터 요소 액세스에 [] 형태를 사용할 수 없습니다.

배열 요소 또는 인덱서 액세스에 대해 [] 연산자를 사용할 수도 있습니다.

포인터 산술 연산자

포인터를 사용하여 다음과 같은 산술 연산을 수행할 수 있습니다.

  • 포인터에서 정수 값 추가 또는 빼기
  • 두 포인터 빼기
  • 포인터 증가 또는 감소

void* 형식의 포인터를 사용하여 이러한 작업을 수행할 수 없습니다.

포인터에서 정수 값 더하기 또는 빼기

T* 형식의 포인터 pint, uint, long 또는 ulong으로 암시적으로 변환할 수 있는 형식의 n 식에 대한 더하기와 빼기가 다음과 같이 정의됩니다.

  • p + nn + p 식 모두 p에서 지정한 주소에 n * sizeof(T)를 추가한 결과 T* 유형의 포인터를 생성합니다.
  • p - n 식은 p에 의해 지정된 주소에서 n * sizeof(T)를 뺀 결과 T* 유형의 포인터를 생성합니다.

sizeof 연산자는 바이트 단위의 형식 크기를 가져옵니다.

다음 예제에서는 포인터로 + 연산자를 사용하는 방법을 보여줍니다.

unsafe
{
    const int Count = 3;
    int[] numbers = new int[Count] { 10, 20, 30 };
    fixed (int* pointerToFirst = &numbers[0])
    {
        int* pointerToLast = pointerToFirst + (Count - 1);

        Console.WriteLine($"Value {*pointerToFirst} at address {(long)pointerToFirst}");
        Console.WriteLine($"Value {*pointerToLast} at address {(long)pointerToLast}");
    }
}
// Output is similar to:
// Value 10 at address 1818345918136
// Value 30 at address 1818345918144

포인터 빼기

T* 형식의 두 가지 포인터 p1p2에 대해 p1 - p2 식은 p1p2에 의해 지정된 주소 간의 차이를 sizeof(T)로 나누어 생성합니다. 결과의 형식은 long입니다. 즉, p1 - p2((long)(p1) - (long)(p2)) / sizeof(T)로 계산됩니다.

다음 예제에서는 포인터 빼기를 보여줍니다.

unsafe
{
    int* numbers = stackalloc int[] { 0, 1, 2, 3, 4, 5 };
    int* p1 = &numbers[1];
    int* p2 = &numbers[5];
    Console.WriteLine(p2 - p1);  // output: 4
}

포인터 증가 및 감소

++ 증가 연산자는 포인터 피연산자에 1을 추가합니다. -- 감소 연산자는 포인터 피연산자에서 1을 뺍니다.

두 연산자는 접미사(p++p--)와 접두사(++p--p)의 두 가지 형태로 지원됩니다. p++p--는 해당 라인에서의 실행 이전p 값입니다. ++p--p는 해당 라인에서의 실행 이후의 값입니다. 이 내용은 우리가 앞서 증감 연산자 파트에서 학습한 내용과 동일합니다.

The following example demonstrates the behavior of both postfix and prefix increment operators: 다음 예제에서는 접미사 및 접두사 증가 연산자의 동작을 보여줍니다.

unsafe
{
    int* numbers = stackalloc int[] { 0, 1, 2 };
    int* p1 = &numbers[0];
    int* p2 = p1;
    Console.WriteLine($"Before operation: p1 - {(long)p1}, p2 - {(long)p2}");
    Console.WriteLine($"Postfix increment of p1: {(long)(p1++)}");
    Console.WriteLine($"Prefix increment of p2: {(long)(++p2)}");
    Console.WriteLine($"After operation: p1 - {(long)p1}, p2 - {(long)p2}");
}
// Output is similar to
// Before operation: p1 - 816489946512, p2 - 816489946512
// Postfix increment of p1: 816489946512
// Prefix increment of p2: 816489946516
// After operation: p1 - 816489946516, p2 - 816489946516

포인터 비교 연산자

==, !=, <, >, <=>= 연산자를 사용하여 void*를 포함한 모든 포인터 형식의 피연산자를 비교할 수 있습니다. 이러한 연산자는 두 피연산자가 지정한 주소를 부호 없는 정수인 것처럼 비교합니다.

연산자 우선 순위

다음 목록에서는 포인터 관련 연산자를 가장 높은 우선순위부터 가장 낮은 것으로 정렬합니다.

  • 접미사 증가 x++ 및 감소 x-- 연산자와 ->[] 연산자
  • 접두사 증가 ++x 및 감소 --x 연산자와 &* 연산자
  • 가감 연산자인 +- 연산자
  • 비교 연산자인 <, >, <=>= 연산자
  • 같음 ==!= 연산자

괄호 연산자인 ()를 사용하여 연산자 우선순위에 따라 주어진 평가 순서를 변경합니다.

연산자 오버로드 가능성

사용자 정의 형식은 포인터 관련 연산자 &, *, ->[](을)를 오버로드할 수 없습니다.

안전하지 않은 코드, 포인터 형식 및 함수 포인터

NOTE

현재 문서는 다음 경로의 Microsoft Learn 자료를 번역했습니다. Unsafe code, pointer types, and function pointers. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.

작성하는 대부분의 C# 코드는 "확인 가능한 안전한 코드"입니다. 확인 가능한 안전한 코드는 .NET 도구가 코드가 안전한지 확인할 수 있음을 의미합니다. 일반적으로 안전한 코드는 포인터를 사용하여 메모리에 직접 액세스하지 않습니다. 또한 원시 메모리를 할당하지 않습니다. 대신 관리형 개체를 만듭니다.

C#은 unsafe 컨텍스트를 지원하는데, 이 컨텍스트에서는 확인할 수 없는 코드를 작성할 수 있습니다. unsafe 컨텍스트에서 코드는 포인터를 사용하고, 메모리 블록을 할당 및 해제하고, 함수 포인터를 사용하여 메서드를 호출할 수 있습니다. C#의 안전하지 않은 코드가 반드시 위험한 것은 아닙니다. 단지 CLR에서 안전을 확인할 수 없을 뿐입니다.

안전하지 않은 코드에는 다음과 같은 속성이 있습니다.

  • 메서드, 형식 및 코드 블록은 안전하지 않은 것으로 정의할 수 있습니다.
  • 경우에 따라, 안전하지 않은 코드는 배열 범위 검사를 제거하여 애플리케이션의 성능을 향상할 수 있습니다.
  • 포인터가 필요한 네이티브 함수를 호출하는 경우 안전하지 않은 코드가 필요합니다.
  • 안전하지 않은 코드를 사용하면 보안 및 안정성 위험이 발생합니다.
  • 안전하지 않은 블록을 포함하는 코드는 AllowUnsafeBlocks 컴파일러 옵션을 사용하여 컴파일해야 합니다.

포인터 형식

안전하지 않은 컨텍스트에서, 형식은 포인터 형식일 수도 있고 값 형식 또는 참조 형식일 수도 있습니다. 포인터 형식 선언은 다음 형식 중 하나를 사용합니다.

type* identifier;
void* identifier; // 사용 가능하나 권장하지 않음

포인터 형식에서 * 앞에 지정된 형식을 참조 형식이라고 합니다. 비관리형 형식만 참조 형식일 수 있습니다.

포인터 형식은 개체에서 상속되지 않으며 포인터 형식과 object는 서로 변환되지 않습니다. 또한 박싱과 언박싱은 포인터를 지원하지 않습니다. 그러나 다른 포인터 형식 간의 변환 및 포인터 형식과 정수 형식 사이의 변환은 허용됩니다.

When you declare multiple pointers in the same declaration, you write the asterisk (*) together with the underlying type only. It isn't used as a prefix to each pointer name. For example:

동일한 선언에서 여러 포인터를 선언하는 경우 별표(*)는 기본 형식에만 함께 사용됩니다. 각 포인터 이름의 접두사로는 사용되지 않습니다. 예를 들면 다음과 같습니다.

int* p1, p2, p3;   // 허용
int *p1, *p2, *p3;   // C#에선 불가능

개체 참조는 포인터가 해당 개체 참조를 가리키는 경우에도 가비지 수집될 수 있으므로 포인터는 참조나 참조가 들어 있는 구조체를 가리킬 수 없습니다. 가비지 수집기는 포인터 형식에서 개체를 가리키는지 여부를 추적하지 않습니다.

MyType* 형식의 포인터 변수 값은 MyType 형식의 변수 주소입니다. 다음은 포인터 형식 선언의 예제입니다.

  • int* p: p는 정수에 대한 포인터입니다.
  • int** p: p는 정수에 대한 포인터를 가리키는 포인터입니다.
  • int*[] p: p는 정수에 대한 포인터의 1차원 배열입니다.
  • char* p: p는 문자에 대한 포인터입니다.
  • void* p: p는 알 수 없는 형식에 대한 포인터입니다.

포인터 간접 참조 연산자 *를 사용하면 포인터 변수가 가리키는 위치의 내용에 액세스할 수 있습니다. 예를 들어, 다음 선언을 참조하십시오.

int* myVariable;
...
*myVariable

*myVariable 식을 사용하면 myVariable 변수가 가리키는 곳의 int 형식의 값을 가져옵니다.

fixed 문에 대한 문서에는 몇 가지 포인터 예제가 있습니다. 다음 예제는 unsafe 키워드 및 fixed 문을 사용하고 정수 포인터를 증분하는 방법을 보여줍니다. 이 코드를 실행하려면 콘솔 애플리케이션의 Main 메서드에 붙여 넣습니다. 이러한 예제는 AllowUnsafeBlocks 컴파일러 옵션으로 컴파일되어야 합니다.

void* 형식의 포인터에는 간접 참조 연산자를 적용할 수 없습니다. 그러나 캐스트를 사용하여 void 포인터를 다른 포인터 형식으로 변환하거나 반대로 변환할 수 있습니다.

포인터는 null일 수 있습니다. null 포인터에 간접 참조 연산자를 적용할 때 발생하는 동작은 구현에 따라 다릅니다.

메서드 사이에 포인터를 전달하면 정의되지 않은 동작이 발생할 수 있습니다. in, out 또는 ref 매개 변수를 통해, 또는 함수 결과로 지역 변수에 포인터를 반환하는 메서드를 고려합니다. fixed 블록에서 포인터가 설정되면 이 포인터가 가리키는 변수의 고정 상태가 해제될 수 있습니다.

다음 표에서는 안전하지 않은 컨텍스트에서 포인터에 대해 수행할 수 있는 연산자와 문을 보여 줍니다.

연산자/문 설명
* 포인터 간접 참조를 수행합니다.
-> 포인터를 통해 구조체 멤버에 액세스합니다.
[] 포인터를 인덱싱합니다.
& 변수 주소를 가져옵니다.
++-- 포인터를 증가 및 감소시킵니다.
+- 포인터 산술 연산을 수행합니다.
==, !=, <, >, <=>= 포인터를 비교합니다.
stackalloc 스택에 메모리를 할당합니다.
fixed 해당 주소를 찾을 수 있도록 임시로 변수를 고정합니다.

모든 포인터 형식을 암시적으로 void* 형식으로 변환할 수 있습니다. 모든 포인터 형식에 값 null을 할당할 수 있습니다. 캐스트 식을 사용하여 모든 포인터 형식을 명시적으로 다른 포인터 형식으로 변환할 수 있습니다. 정수 형식을 포인터 형식으로 변환하거나, 포인터 형식을 정수 형식으로 변환할 수도 있습니다. 이 변환에는 명시적 캐스트가 필요합니다.

다음 예제에서는 int*byte*로 변환합니다. 포인터는 변수의 최하위 주소 지정 바이트를 가리킵니다. int 크기(4바이트)까지 결과를 연속적으로 증가할 경우 변수의 나머지 바이트를 표시할 수 있습니다.

고정 크기 버퍼

fixed 키워드를 사용하여 데이터 구조에 고정 크기 배열이 있는 버퍼를 만들 수 있습니다. 고정 크기 버퍼는 다른 언어 또는 플랫폼의 데이터 원본과 상호 운용되는 메서드를 작성할 때 유용합니다. 고정 크기 버퍼는 일반 구조체 멤버에 허용되는 모든 특성 또는 한정자를 사용할 수 있습니다. 배열 형식이 bool, byte, char, short, int, long, sbyte, ushort, uint, ulong, float, 또는 double이어야 한다는 것이 유일한 제한 사항입니다.

private fixed char name[30];

안전한 코드에서 배열을 포함하는 C# 구조체는 배열 요소를 포함하지 않습니다. 대신 구조체에 요소에 대한 참조가 포함되어 있습니다. unsafe 코드 블록에서 사용될 경우 구조체에 고정 크기 배열을 포함할 수 있습니다.

pathName이 참조이므로 다음 struct의 크기는 배열에 있는 요소의 수에 따라 달라지지 않습니다.

구조체는 안전하지 않은 코드에 포함된 배열을 포함할 수 있습니다. 다음 예제에서 fixedBuffer 배열은 고정 크기입니다. fixed 문을 사용하여 첫 번째 요소에 대한 포인터를 가져옵니다. 이 포인터를 통해 배열의 요소에 액세스합니다. fixed 문은 fixedBuffer의 인스턴스 필드를 메모리의 특정 위치에 고정합니다.

128개 요소 char 배열의 크기는 256바이트입니다. 고정 크기 문자 버퍼는 인코딩에 관계없이 항상 문자당 2바이트를 사용합니다.

또 다른 일반적인 고정 크기 배열은 bool 배열입니다. bool 배열의 요소 크기는 항상 1바이트입니다. bool 배열은 비트 배열이나 버퍼를 만드는 데 적합하지 않습니다.

이전 예제에서는 고정 크기 버퍼가 있는 unsafe struct방법을 보여줍니다.

internal unsafe struct Buffer
{
    public fixed char fixedBuffer[128];
}

Buffer에 대해 컴파일에서 생성된 C#은 다음과 같은 특성이 있습니다.

internal struct Buffer
{
    [StructLayout(LayoutKind.Sequential, Size = 256)]
    [CompilerGenerated]
    [UnsafeValueType]
    public struct <fixedBuffer>e__FixedBuffer
    {
        public char FixedElementField;
    }

    [FixedBuffer(typeof(char), 128)]
    public <fixedBuffer>e__FixedBuffer fixedBuffer;
}

고정 크기 버퍼는 다음과 같은 방법으로 일반 배열과 다릅니다.

  • unsafe 컨텍스트에서만 사용할 수 있습니다.
  • 단지 구조체의 인스턴스 필드일 수 있습니다.
  • 항상 벡터 또는 1차원 배열입니다.
  • 선언에는 fixed char id[8]와 같은 길이가 포함되어야 합니다. fixed char id[]를 사용할 수 없습니다.

포인터를 사용하여 바이트 배열을 복사하는 방법

다음 예제에서는 포인터를 사용하여 배열 간에 바이트를 복사합니다.

이 예제에서는 Copy 메서드에서 포인터를 사용할 수 있도록 하는 unsafe 키워드를 사용합니다. fixed 문은 소스 및 대상 배열에 대한 포인터를 선언하는 데 사용됩니다. fixed 문은 소스 및 대상 배열의 위치가 메모리에 고정되므로 가비지 수집에 의해 이동되지 않습니다. fixed 블록이 완료되면 배열에 대한 메모리 블록이 고정 해제됩니다. 이 예제의 Copy 메서드는 unsafe 키워드를 사용하므로 AllowUnsafeBlocks 컴파일러 옵션으로 컴파일해야 합니다.

이 예제에서는 두 번째 관리되지 않는 포인터보다는 인덱스를 사용하여 두 배열의 요소에 액세스합니다. pSourcepTarget 포인터의 선언은 배열을 고정합니다. 이 기능은 C# 7.3부터 사용할 수 있습니다.

함수 포인터

C#은 안전한 함수 포인터 개체를 정의하는 delegate 형식을 제공합니다. 대리자를 호출하려면 System.Delegate에서 파생된 형식을 인스턴스화하고 해당 Invoke 메서드에 대한 가상 메서드 호출을 수행해야 합니다. 이 가상 호출은 callvirt IL 명령을 사용합니다. 성능이 중요한 코드 경로에서는 calli IL 명령을 사용하는 것이 더 효율적입니다.

delegate* 구문을 사용하여 함수 포인터를 정의할 수 있습니다. 컴파일러는 delegate 개체를 인스턴스화하고 Invoke를 호출하는 대신 calli 명령을 사용하여 함수를 호출합니다. 다음 코드에서는 delegate 또는 delegate*를 사용하여 같은 형식의 두 개체를 결합하는 두 메서드를 선언합니다. 첫 번째 메서드는 System.Func<T1,T2,TResult> 대리자 형식을 사용합니다. 두 번째 메서드는 동일한 매개 변수 및 반환 형식이 포함된 delegate* 선언을 사용합니다.

다음 코드에서는 정적 로컬 함수를 선언하고 해당 로컬 함수에 대한 포인터를 사용하여 UnsafeCombine 메서드를 호출하는 방법을 보여 줍니다.

위의 코드는 함수 포인터로 액세스되는 함수에 대한 몇 가지 규칙을 보여 줍니다.

  • 함수 포인터는 unsafe 컨텍스트에서만 선언할 수 있습니다.
  • delegate*를 사용하거나 delegate*를 반환하는 메서드는 unsafe 컨텍스트에서만 호출할 수 있습니다.
  • 함수의 주소를 가져오는 & 연산자는 static 함수에서만 사용할 수 있습니다. 이 규칙은 멤버 함수와 로컬 함수에 모두 적용됩니다.

구문은 delegate 형식 선언 및 포인터 사용과 유사합니다. delegate* 접미사는 선언이 함수 포인터임을 나타냅니다. 메서드 그룹을 함수 포인터에 할당할 때 &는 연산에 메서드의 주소가 사용됨을 나타냅니다.

managedunmanaged 키워드를 사용하여 delegate*에 대한 호출 규칙을 지정할 수 있습니다. 또한 unmanaged 함수 포인터의 경우 호출 규칙을 지정할 수 있습니다. 다음 선언에서는 각각의 예제를 보여 줍니다. 첫 번째 선언은 managed 호출 규칙(기본값)을 사용합니다. 다음 4개에서는 unmanaged 호출 규칙을 사용합니다. 각각은 ECMA 335 호출 규칙(Cdecl, Stdcall, Fastcall 또는 Thiscall) 중 하나를 지정합니다. 마지막 선언은 플랫폼에 unmanaged 대한 기본 호출 규칙을 선택하도록 CLR에 지시하는 호출 규칙을 사용합니다. CLR은 런타임에 호출 규칙을 선택합니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com