카테고리 없음

03 - 28 C++ 보충 7챕터

pp4037 2026. 3. 28. 20:39
//챕터 7-1 : 포인터
// 1 : 33 : 00
//포인터 : 메모리 주소를 저장한 변수
//자료형 변수명 = 값; 예시 : int A = 5;

//자료형* 변수명 = 메모리주소값; // 자료형쪽에 Asterisk(*)를 붙힙시다. int* A = 메모리주소값; 자료형이 정해져야 메모리주소값의 크기가 정해지기에 꼭 같이 적어야한다.
// 변수명쪽에 Asterisk(*)를 붙히면 뒤에서 배울 역참조 연산자와 심히 헷갈립니다.
// 포인터를 사용할 시 시작 메모리주소값만 알 경우 많은량의 데이터를 인자값으로 주고받을 수 있다.

#include <stdio.h>

int main(void)
{
	int A = 5; // 일반 정수형 타입 변수 A
	int* A; // 정수형 타입 변수 A의 메모리주소값

	printf("%d", &A); // &변수명 == 변수의 주소

	return 0;
}
// Main.c
//챕터 7-2 : 주소 연산자와 역참조 연산자
//Ptr == 포인터를 가리키는 약칭
// 주소 연산자: &변수명 == 변수의 메모리주소. 예제 : int A; scanf("%d", &A); == 변수 A의 메모리주소에 %d형식으로 입력해라.
// 메모리 주소를 얻을 수 있는 방법
// 1. 주소 연산자
// 2. 배열
// 배열 : 배열의 이름이 변수고 그 변수가 배열의 시작 메모리 주소를 저장한 변수(포인터). 때문에 배열의 경우 별도의 포인터 연산자를 기입할 필요가 없다. 

// 주소 연산자: &변수명 == 변수의 메모리주소. 
// 변수타입* 변수명 == 포인터(변수). 예시 : int* A = &B;
// 변수타입 *변수명 == 메모리 주소를 저장한 변수에 그 메모리 주소에 저장되어 있던 값을 출력하는 역참조 연산자.
// int A == 정수형 변수 A
// int &A == 변수 A가 저장된 메모리 주소. 예시 : A = 10; 면 10을 가지고 있는 A의 집주소가 &A
// int* B == 메모리 주소를 저장할 수 있는 변수(포인터). 예시 : int* B = &A; 면 10을 가지고 있는 A의 집주소가 포인터 B에 대입됨.
// int *B == 받은 메모리 주소에 저장되어 있던 값을 출력하는 역참조 연산자. 예시 : *B; 라면 10을 가지고 있는 A의 집주소가 대입된 포인터 B의 그 집주소의 값을 출력하라는 뜻.
// 메모리 주소 != 값 -> 집주소와 집주인의 소지금의 차이라고 할 수 있음.
// int* B = &A; 전제하에 *B == *(&A) == A 라고 볼 수 있다.
#include <stdio.h>

//int main(void)
//{
//	int X = 10; 정수형 변수 X에 10을 대입시킨다.
//	int* PtrToX = &X; 변수 X의 메모리주소를 정수형 포인터 변수 PtrTox에 대입시킨다.
//
//	printf("%d\n", *PtrToX); 포인터 변수 PtrTox가 가리키는 메모리주소의 값을 출력한다.
//
//	*PtrToX = 100; 100을 포인터 변수 PtrTox가 가리키는 메모리주소의 값에 대입시킨다.
//
//	printf("%d\n", *PtrToX); 메모리주소가 아닌 메모리주소가 가리키는 값이 바뀌었기에 100으로 변하며, 마찬가지로 메모리주소를 공유하는 int X의 값도 100으로 변경되었다.
//
//	return 0;
//}

int main(void)
{
	int A = 5;
	

	printf("%d\n", A);

	int* B = &A;

	printf("%d\n", *B);

	*B = 200;

	printf("%d\n", *B);
	printf("%d\n", A);


	return 0;
}
// Main.c

#include <stdio.h>

int main(void)
{
	int X = 50, Y = 10;
	int* PtrToX = &X;

	printf("%d\n", *PtrToX);

	*PtrToX = *PtrToX * 5;
	printf("%d\n", *PtrToX);

	PtrToX = &Y; // 한 번 포인터 변수로 정의되면 역참조 연산자 의외일 때는 *를 붙일 필요가 없다. 

	printf("%d\n", *PtrToX);

	return 0;
}

 

어려움

// Main.c
#include <stdio.h>

int main(void)
{
	int X = 10;
	int* PtrToX = &X;

	printf("%d\n", X); // 10
	printf("%d\n", (*PtrToX)++); // 소괄호 안에 있는 PtrToX가 가리키는 메모리주소의 값을 먼저 출력한 뒤 그 값에 +1 했기에 10
	printf("%d\n", X); // 11
	printf("%d\n", *PtrToX++); //
	// 문제 속의 문제. 계산될 연산자의 순서를 맞추시오.

	return 0;
}

 

// Main.c

#include <stdio.h>

int main(void)
{
	int Num = 100;
	int* PointerToNum = &Num;

	printf("Num:               %d\n", Num); // Num의 값
	printf("&Num:              %X\n", &Num); // Num의 메모리주소
	printf("&Num:              %p\n", (void*)&Num); // %q -> 포인터를 출력하는 서식지정자. 차후 배울 포인터 to void 라는 방식으로 형변환을 시켜야지 사용 가능. 위의 방식과 비슷하지만 좀 더 올바른 방식이다.
	printf("PointerToNum:      %X\n", PointerToNum);
	printf("PointerToNum:      %p\n", (void*)PointerToNum);
	printf("*PointerToNum:     %d\n", *PointerToNum);
	printf("*PointerToNum:     %d\n", *(&Num)); // 올바르진 않지만, 소거하는 식으로라도 이해해 보자.
	printf("(*PointerToNum)*2: %d\n", (*PointerToNum) * 2);

	return 0;
}
// Main.c

#include <stdio.h>

void Swap1(int Op1, int Op2);// 전방 함수 1 : 이 밑에 함수가 있을테니 컴파일 오류를 일으키지 말라는 코드

void Swap2(int* Op1, int* Op2); // 전방 함수 2

int main(void)
{
	int Num1 = 10;
	int Num2 = 20;

	printf("before Swap1()\n");
	printf("Num1: %d, Num2: %d\n", Num1, Num2); // Num1 == 10, Num2 == 20

	Swap1(Num1, Num2); // Swap1 함수에 Num1 값과 Num2값을 Swap1의 매개변수에 인자값으로 대입시키고 함수를 실행.

	printf("after Swap1()\n");
	printf("Num1: %d, Num2: %d\n\n", Num1, Num2); // 여전히 Num1 == 10, Num == 20. 이유는 Swap1 함수의 매개변수와 main 함수의 변수는 별개의 존재이기 때문.

	printf("before Swap2()\n");
	printf("Num1: %d, Num2: %d\n", Num1, Num2);

	Swap2(&Num1, &Num2); //Swap2 함수에 Num1, Num2 변수의 메모리주소를 Swap2의 매개변수에 인자값으로 대입시키고 함수를 실행.

	printf("after Swap2()\n");
	printf("Num1: %d, Num2: %d\n\n", Num1, Num2); // 이번에는 Num1 == 20, Num2 == 10으로 바뀌었다. 이유는 Swap2 함수의 매개변수(포인터)에 대입된 값이 변환되었기에, 같은 메모리주소를 공유하는 Num1과 Num2에게도 영향이 갔기 때문이다.

	return 0;
}

void Swap1(int Op1, int Op2) // 값을 별도로 반환하지 않는 void 반환타입이기 때문에 변하지 않는다.
{
	int Temp; // 두 숫자를 바꾸기 위해선 먼저 제 3의 변수를 설정한다.

	Temp = Op1; // 설정한 제 3의 변수에 변수1의 값을 대입시킨다.
	Op1 = Op2; // 변수1에 변수2의 값을 대입시킨다.
	Op2 = Temp; // 변수2에 변수3에 대입되었던 변수1의 값을 대입시킨다.
	// 결과 == Temp == Op1, Op1 <- Op2, Op2 <- Op1

	return;
}

void Swap2(int* Op1, int* Op2) // main 함수에서 메모리주소 형태(&변수명)로 받았기에 포인터형 변수로 매개변수를 설정한다. int* A = &B;
{
	int Temp;

	Temp = *Op1; // 포인터형 변수로 받은 값을 역참조연산자( *변수명)를 통해 메모리주소에 있는 값을 불러왔기에 일반 변수로도 대입이 가능하다.
	*Op1 = *Op2;
	*Op2 = Temp;

	return;
}
#include <stdio.h>

void Swap(int* A, int* B);


int main(void)
{
	int A = 2;
	int B = 3;

	printf("before Swap1()\n");
	printf("A: %d, B: %d\n", A, B); // A == 2, B == 3

	Swap(&A, &B);

	printf("after Swap1()\n");
	printf("A: %d, B: %d\n", A, B); // A == 3, B == 2



	return 0;
}

void Swap(int* A, int* B)
{
	int C = *A;
	*A = *B;
	*B = C;

	return;
}
// Main.c
//GetMinMax() 함수
#include <stdio.h>
#include <assert.h>

void GetMinMax(const size_t InArrayLength, const int InArray[], int* OutPtrToMin, int* OutPtrToMax);

int main(void)
{
    const int Nums[5] = { 7, 8, 1, 10, 5 }; // const 수식자로 해당 배열의 값은 이후로 변하지 않는다. Nums 배열에 값을 대입시킨다.
    int ResultMin; 
    int ResultMax;

    GetMinMax(5, Nums, &ResultMin, &ResultMax); // GetMinMax 함수에 5(Nums배열의 메모리 크기), Nums, ResultMin의 메모리주소, ResultMax의 메모리주소를 인자값으로 매개변수에 대입시키고 반환값을 받는다.
    //함수 내에서 처리된 OutPtrToMin, OutPtrToMax의 메모리주소가 가리키는 값이 &ResultMin, &ResultMax에 대입되었다.
    printf("ResultMin: %d\n", ResultMin);
    printf("ResultMax: %d\n", ResultMax);

    return 0;
}

void GetMinMax(const size_t InArrayLength, const int InArray[], int* OutPtrToMin, int* OutPtrToMax) // 함수의 선언단계의 매개변수에서 배열은 메모리 크기를 기입할 필요가 없다.
{
    size_t i;

    *OutPtrToMin = InArray[0]; // InArray 배열의 1번째 메모리의 값을 OutPtrToMin의 메모리주소의 값에 대입시킨다. 
    *OutPtrToMax = InArray[0]; // InArray 배열의 1번째 메모리의 값을 OutPtrToMax의 메모리주소의 값에 대입시킨다.

    for (i = 0; i < InArrayLength; ++i) // i의 초기값 0; i 가 InArrayLength 보다 작은 동안 true, 이후 i의 값을 1 올린다.
    {
        if (InArray[i] < *OutPtrToMin) // InArray 배열의 i 번째 메모리값이 OutPtrToMin가 가리키는 메모리주소의 값보다 낮을 때 true
        {
            *OutPtrToMin = InArray[i]; // OutPtrToMin의 메모리주소가 가리키는 값에 InArray 배열의 i 번째 메모리값을 대입시킨다.
        } // 이렇게 되면 작은 값을 OutPtrToMin의 메모리주소가 가리키는 값에 계속 대입시킬 수 있다. if(5 < 1) 5 = 1; 과 같은 식이기 때문.

        if (*OutPtrToMax < InArray[i]) // InArray 배열의 i 번째 메모리값이 OutPtrToMax가 가리키는 메모리주소의 값보다 높을 때 true
        {
            *OutPtrToMax = InArray[i]; // OutPtrToMax의 메모리주소가 가리키는 값에 InArray 배열의 i 번째 메모리값을 대입시킨다.
        } // 이렇게 되면 큰 값을 OutPtrToMax의 메모리주소가 가리키는 값에 계속 대입시킬 수 있다. if(1 < 5) 1 = 5; 과 같은 식이기 때문.
    }

    return;
}
// Main.c
// 챕터 7-3 : NULL과 void 포인터
// 포인터를 초기화 하고 싶을 경우 : NULL 값을 넣으면 된다. 예시 : int* PtrToA = NULL; 자료형* 변수명 = 메모리주소값;
// NULL 포인터의 쓰임새
// 1. 포인터의 초기화
// 2. 포인터가 더이상 사용중이지 않음을 알리고 싶을 때.
// 3. 포인터가 유효한 메모리 주소를 저장하고 있는지 확인할 때.
#include <stdio.h>

int main(void)
{
	int* Ptr = NULL; // 포인터 Ptr의 메모리주소값에 NULL을 대입한다.

	if (NULL == Ptr) // NULL이 포인터 Ptr과 같다면 true 
	{
		printf("NULL == Ptr");
	}

	if (Ptr != NULL) // Ptr과 NULL이 다르다면 true
	{
		printf("Ptr is not the \"NULL pointer\"\n"); // 위의 두 가지 방법을 사용하는게 보다 안전하다.
	}

	/* 간혹 다음과 같이 쓰기도 합니다. worst.*/
	if (Ptr)
	{
		printf("Ptr is not the \"NULL pointer\"\n");
	}

	/* 올바르지 않은 예들. 명시적인 부분에서 올바르지 않음. */
	if (0 == Ptr)
	{
		printf("Ptr is the \"NULL pointer\"\n");
	}
	if (Ptr != 0)
	{
		printf("Ptr is not the \"NULL pointer\"\n");
	}

	return 0;
}
// Main.c
// void 포인터
// void 포인터는 어떤 자료형의 포인터라도 대입 가능하다. void* 변수명 = 메모리 주소 값; <- 메모리 주소에 어떤 자료형의 포인터가 오더라도 괜찮음.
// 사용처 : 자료형이 당장 정해져있지 않았지만 포인터를 만들어야할 때 사용됨.
// 주의사항
// 1. void*에 역참조 연산은 불가능. 이유 : 역참조 연산을 하려면 메모리 주소 값을 가져와야하는데, 포인터의 자료형마다 메모리 주소 값의 크키가 다르기 때문에 void 포인터는 얼마만큼의 메모리 주소 값을 가졌는지 알 수 없기 때문.
// 2. void*에 정수를 더하거나 빼는 연산은 불가능. 이유 : 마찬가지로 메모리 주소 값의 크기가 정해져있지 않아서 몇 바이트 만큼 더하고 빼는지 모르기 때문.
#include <stdio.h>

int main(void)
{
	float PI = 3.14f, Result;
	void* p;
	float* q;

	p = &PI;
	q = p;

	printf("%f\n", *p);
	printf("%f\n", *(float*)p);

	Result = *p + *q;
	Result = *(float*)p + *q;
	printf("%f\n", Result);

	return 0;
}