반응형

 

포인터 증감 연산

포인터 변수에 저장된 값에 대해서 증감연산을 진행할 수 있다.

곱셈 나눗셈등등은 불가하다.

 

(type)형 포인터 변수 대상의 증감 연산 시 sizeof(type)의 크기만큼 증감이 된다.

 

아래의 코드를 보면 ptr++, ptr--  증감연산자가 값에 영향을 끼치는게 아니라

주소에 영향을 끼치는 것을 알 수 있습니다!

int arr[3] = {10, 20, 30};
int* ptr = arr;
printf("%d %d %d \n", *ptr, *(ptr+1), *(ptr+2));	// 10 20 30 출력

printf("%d ", *ptr);	// 10 출력
ptr++;
printf("%d ", *ptr);	// 20 출력
ptr++;
printf("%d ", *ptr);	// 30 출력
ptr--;
printf("%d ", *ptr);	// 20 출력
ptr--;
printf("%d ", *ptr);	// 10 출력

즉, 결론은 arr이 포인터 변수의 이름이건 배열의 이름이건

arr[i] == *(arr+i) == *(ptr+i) 이렇게 사용할 수 있다

 

*(arr+i) => 포인터 연산자안의 괄호에서의 연산은

정수끼리의 연산이 아니라 주소의 연산이라고 생각하면 된다.

반응형

 

반응형

'C' 카테고리의 다른 글

포인터와 함수 이해  (0) 2023.05.15
포인터 배열  (0) 2023.05.15
포인터와 배열의 관계  (0) 2023.05.15
문자열에 대해서!  (0) 2023.05.15
C언어의 꽃 포인터!  (0) 2023.05.14
반응형
배열의 이름

배열의 이름은 배열의 시작 주소 값을 의미하는 포인터(배열의 첫 번째 요소)입니다.

단순히 주소 값이 아닌 포인터인 이유는 메모리 접근에 사용되는 * 연산이 가능하기 때문입니다.

 

배열의 모든 요소는 붙어있다. 그래서 배열의 요소들의 주소값은 연속된 값이다.

 

그렇다면 배열 이름과 포인터 변수는 같은 용도냐?

그것은 아닙니다 아래의 표에서 둘의 차이점을 알아보겠습니다

배열이름과 포인터 변수의 비교

포인터 변수는 주소를 변경가능하지만

배열은 주소가 고정되기에 포인터 변수처럼

주소값 변경이 불가능합니다.

 

 

1차원 배열 이름의 포인터형

아래의 코드에서 int형 포인터인 arr1은 *연산의 결과 4바이트 메모리 공간에 정수를 저장한다.

double형 포인터 arr2는 8바이트 메모리 공간에 실수를 저장한다.

int arr1[3] = {10,20,30};
double arr2[3] = {1.1, 2.2, 3.3};

*arr1 += 100;
*arr2 += 120.5;
printf("%d %g \n", arr1[0], arr2[0]);	// 101 121.6

 

 

 

포인터의 배열접근

아래의 코드에서 포인터 변수 또한 배열처럼 메모리 공간에 접근이 가능하다는 것을 알 수 있습니다.

int arr[5] = {1,2,3,4,5};
int arrLen = sizeof(arr) / sizeof(int);		// 배열의 길이
int* ptr = &arr[0]; 	// int* ptr = arr;	배열의 이름은 배열의 시작주소와 같다.

for(int i = ; i < arrLen; i++)
{
	printF("%d %d \n", ptr[i], arr[i]);	// 1 1 / 2 2 / 3 3 / 4 4 / 5 5
}

 

 

포인터와 배열의 관계 정리

포인터와 배열은 같다.

 

arr은 int형 배열이다.

ptr은 int형 포인터이다.

 

배열의 변수명은 배열의 시작주소를 의미합니다.

 

int형 포인터를 선언하는곳에

배열을 넣으면 정상적으로 작동합니다.

 

int형 포인터는 주소의 값을 담습니다.

그곳에 배열 변수명을 할당해주면 배열의 변수명은

배열의 시작주소를 뜻하기에 할당이 가능합니다.

 

이는 곧 int형 배열은 int형 포인터와 같다는 뜻입니다!

int arr[3] = {10, 20, 30}
int* ptr = arr;	// 컴파일 성공!

 

이미지 출처 : 윤성우의 열혈 C 프로그래밍  

 

반응형

 

반응형

'C' 카테고리의 다른 글

포인터 배열  (0) 2023.05.15
포인터 연산  (0) 2023.05.15
문자열에 대해서!  (0) 2023.05.15
C언어의 꽃 포인터!  (0) 2023.05.14
난수에 대하여!  (0) 2023.05.12
반응형
배열을 이용한 문자열

아래의 코드에서 Hello World!에서 공백까지 포함해서 12글자인데 str[13]으로 표현한 이유는

문자열의 끝에는 \0(NULL)문자가 있어야 하기 때문입니다.

 

컴파일러는 문자열의 끝을 \0(NULL)문자로 판단합니다.

char str[13] = "Hello World!";

 

참고로 \0(NULL)과 공백은 다릅니다.

\0(NULL)은 아스키코드 값0에 해당하며,

공백은 아스키코드 값32에 해당합니다.

 

널 문자는 모니터 출력에서 의미를 갖지 않는다.

그래서 아무것도 출력이 되지 않을 뿐입니다.

 

 

 

문자열입력 : scanf함수

char형 배열 즉 문자열은

입력할때 서식문자로 %s로 받으며,

인자로 넣을때 주소연산자 &를 쓰지 않는다.

문자배열 즉, 문자열은 주소의 의미를 가지고 있습니다!

char str[50];

scanf("%s", str);

 

아래의 코드에서

문자열을 입력할때 abc def를 입력하지만

결과는 abc만 나온다. 아래의 코드는 문자열에서

널문자를 만나기전 까지 문자열의 인덱스를 순회하면서

한글자씩 출력하는 코드입니다.

그렇다면 abc 와 def사이에 공백이 있지만 끝나지 않을거 같지만

scanf는 공백기준으로 입력을 받는다.

그렇기에 입력버퍼에는 abc def가 들어가 있지만

scanf의 기능 때문에 입력버퍼에는 def가 남아있고

abc가 출력이 되는것입니다!

char str[50] = {0};
int index = 0;

printf("문자열 입력: ");           // abc def
scanf("%s" , str);
printf("입력 받은 문자열 : %s \n", str);      // abc

printf("문자 단위 출력 : ");
while(str[index] != '\0')
{
	printf("%c", str[index]);	// abc
    index++;
}

 

NULL문자 표현방법
  • '\0'
  • NULL
  • 0

 

아스키코드표 출처 : http://cafe.daum.net/flowlife

 

데이터 과학자(Data Scientist) 세상

컴퓨터 프로그래밍/데이터 과학자/데이터 엔지니어에 관심있는 분들과 함께합니다.

cafe.daum.net

 

반응형

 

반응형

'C' 카테고리의 다른 글

포인터 연산  (0) 2023.05.15
포인터와 배열의 관계  (0) 2023.05.15
C언어의 꽃 포인터!  (0) 2023.05.14
난수에 대하여!  (0) 2023.05.12
배열에 대하여!  (0) 2023.05.12
반응형
포인터란?

포인터 변수는 주소 값의 저장을 목적으로 선언됩니다.

주소의 시작 주소를 뜻한다.

 

 

 

포인터 변수

아래의 그림처럼 포인터 변수를 선언하는 방법은

자료형 * 변수명; 입니다.

포인터에 같은 자료형 변수의 주소값을 할당하여 저장할 수 있습니다.

 

포인터 변수의 크기는 시스템의 주소 값 크기에 따라 다릅니다.

16비트 시스템 => 주소 값 크기 16비트 => 포인터 변수 크기 16비트

32비트 시스템 => 주소 값 크기 32비트 => 포인터 변수 크기 32비트

64비트 시스템 => 주소 값 크기 64비트 => 포인터 변수 크기 64비트

int num = 1;

// 포인터 변수	자료형 * 변수명;
int * pnum;

// 정수형 포인터변수에 정수현변수의 주소 값을 넣는다.
pnum = &num;

 

 

 

포인터 변수 선언

변수의 자료형에 따라 포인터 변수의 선언방법에도 차이가 있다.

포인터 변수에 저장되는 값은 도무 정수의 형태이지만, 그래도 선언하는 방법에 차이가 있다.

차이가 있는 이유는 변수의 자료형에 따라 메모리 접근방식이 다르기 때문이다.

 

아래의 세개의 포인터 선언은 모두 동일한 선언문이다.

*의 위치에 따라서 차이는 없다.

 

하지만 2번 권장

1. int * ptr;
2. int* ptr;
3. int *ptr;

 

 

 

&연산자

&연산자는 변수의 주소 값을 반환해주는 연산자입니다.

연산자이기에 상수가 아닌 변수가 피연산자이어야 합니다.

&연산자의 반환 값은 포인터 변수에 저장을 합니다.

int num = 10;
int* pnum = &num;

 

포인터 변수에 변수의 주소값을 저장하려면 자료형이 같아야 합니다.

 

* 연산자

* 연산자는 포인터가 가리키는 메모리를 참조합니다.

* 연산자는 피연산자는 포인터변수여야 한다

아래의 코드에서 pnum은 num의 주소값을 가지고 있고,

*pnum은 num의 주소에 있는 값을 가지고 있다. 

*pnum의 값을 바꾸면 num의 값 또한 바뀌게 된다.

int num = 10;
int * pnum = &num;
*pnum = 20;
printf("%d", *pnum);

 

이 부분은 다소 헷갈릴 수 있으니 아래의 예시를 통해서 자세히 알아보자!

int num1 = 10;
int num2 = 8;

int *pnum;

pnum = &num1;		// 포인터 pnum이 num1의 주소를 뜻함.
(*pnum) += 10;		// *pnum은 num1에 주소에 있는 값을 건드린다. 즉 num1 += 10;

pnum = &num2;		// 포인터 pnum이 num2의 주소를 뜻함.
(*pnum) -= 3;		// *pnum은 num2에 주소에 있는 값을 건드린다. 즉 num2 -= 3;

// num1 : 20, num2 : 5 포인터로 주소를 참조하여 값을 변경

 

 

 

잘못된 포인터 사용법

아래의 코드에서 1번 예시와 2번 예시는 잘못된 예시이다.

1번 예시에서 pnum1에는 쓰레기 값으로 초기화 된다. 따라서 값을 저장하더라도

그 값이 어디에 저장되는지 알 수 없다.

 

2번 예시에서 pnum2에는 데이터를 저장했는데 저장된 곳이 어디인지 아무도 모른다.

 

포인터를 선언할 때는 3번 예시처럼 널 포인터로 초기화 하는것이 안전합니다.

널 포인터 NULL은 숫자 0을 의미하며 0은 주소값이 아니라 아무것도 가리키지 않는것을 뜻합니다.

NULL로 초기화하는 것을 권장합니다!

// 1번 예시
int * pnum1;
*pnum1 = 10;


// 2번 예시
int * pnum2 = 20;
*pnum2 = 10;


// 3번 예시
int * pnum3 = 0;
int * pnum4 = NULL;

 

 

 

포인터 정리
  • int* ptr = &num; 은 포인터를 선언하는 방법이다
  • ptr 은 할당받은 주소를 뜻한다.
  • *ptr 은 할당받은 주소의 값을 뜻한다.
  • &ptr 은 ptr 자체의 주소를 뜻한다.

아래의 코드를 예시로 설명하겠습니다.

num1의 주소는 100이라고 가정하겠습니다.

 

ptr의 값은 num1의 주소(100)를 할당받는다.

*ptr의 값은 ptr에 할당되어 있는 num1의 주소(100)에서 값(10)을 받는다.

&ptr의 값은 ptr자신의 주소(300)를 뜻합니다

 

ptr = &num2; 이후

ptr의 값은 num2의 주소(200)를 할당받는다

*ptr의 값은 ptr에 할당되어 있는 num2의 주소(200)에서 값(20)을 받는다.

&ptr의 값은 ptr자신의 주소(300)를 뜻합니다.

int num1 = 10; // &num1 = 100(주소값)
int num2 = 20;	// &num2 = 200(주소값)
int *ptr = &num;	// &ptr = 300(주소값)
ptr => ?	// ptr의 값은 num1의 주소를 할당받는다.
*ptr => ?	// ptr에 할당되어 있는 num1의 주소에서 값을 받는다.
&ptr => ?	// ptr자체의 주소값이며 지금은 300

ptr = &num2;
ptr => ?	// ptr의 값은 num2의 주소를 할당받는다.
*ptr => ?	// ptr에 할당되어 있는 num2의 주소에서 값을 받는다.
&ptr => ?	// ptr자체의 주소값이며 지금도 300

 

반응형

 

반응형

'C' 카테고리의 다른 글

포인터와 배열의 관계  (0) 2023.05.15
문자열에 대해서!  (0) 2023.05.15
난수에 대하여!  (0) 2023.05.12
배열에 대하여!  (0) 2023.05.12
재귀함수에 대하여!  (0) 2023.05.10
반응형
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/*
월남뽕
1. 카드는 52장
2. 하트 다이아 스페이드 클로버 // 문양 4개
3. A (1)/ 2~10 / J Q K ( 11, 12, 13)

게임 룰
딜러(PC)가2개의 카드를 랜덤하게 공개.
보여줄때는 문양까지 나와야한다.

플레이어가 카드 오픈차례때 딜러가 뽑은 카드 두개 사이의 수가 나와야 이긴다.
만약 딜러가 뽑은 카드 두개중 하나와 크기가 같더라도 패배
예시
하트 J 11 / 다이아 K 12 / 클로버 12    //패배
하트 1 / 다이아 2 / 하트 3             // 승리

배팅 시스템이 있다. / 소지금 배팅액
승리는 베팅금 두배
실패는 베팅금 차감

베팅 후
포기시 베팅금 1/2배 차감

한 게임에 쓴 카드 3장은 덱에서 버린다.
남은카드가 3장이 안되거나 or 탕진 시 게임 종료
턴수 표기 / 소지금 표기
*/
void BetweenTwoCardsGame();
void DeckInit();
void Shuffle();
int SameCardShuffle();
void CardPrint();
void PlayerBetting();
void PlayerChoiceCard();
void PlayerCardShuffle();

// 플레이어
int playerMoney = 10000;
int bettingMoney = 0;
int playerCard = 0;
int dealChoice = 0;

// 카드 덱
int hart[13];		// 1~13
int diamond[13];
int spade[13];
int clover[13];

// 셔플 덱
int previousCard = 0;
int nextCard = 0;

int deckShuffle[52] = { 0 };
int deckCount = 52;
int gameCount = 1;


int main()
{
	printf("게임 시작! \n");
	BetweenTwoCardsGame();
	return 0;
}

// 카드게임
void BetweenTwoCardsGame()
{
	DeckInit();			// 덱 설정
	while (deckCount > 2 && playerMoney > 0)
	{
		printf("========================\n");
		printf("%d번째 판\n", gameCount);
		PlayerBetting();	// 배팅액 설정
		Shuffle();				// 카드 섞기
		PlayerChoiceCard();		// 플레이어 카드 선택
		deckCount = deckCount - 3;		// 판수 조절
		gameCount++;
		printf("남은 패수 :%d\n", deckCount);
	}

}

// 덱 설정
void DeckInit()
{
	// 카드 값 설정 
	for (int i = 0; i < 13; i++)	// 		1번째 카드세트 : 1 ~ 13카드설정
	{
		hart[i] = ((i + 1) + (13*0));
	}
	for (int i = 0; i < 13; i++)	// 		2번째 카드세트 : 14 ~ 27카드설정
	{
		diamond[i] = ((i + 1) + (13 * 1));
	}
	for (int i = 0; i < 13; i++)	// 		3번째 카드세트 : 27 ~ 39카드설정
	{
		spade[i] = ((i + 1) +(13 * 2));
	}
	for (int i = 0; i < 13; i++)	// 		4번째 카드세트 : 40 ~ 52카드설정
	{
		clover[i] = ((i + 1) + (13 * 3));
	}
}


// 카드 섞기
void Shuffle()
{
	// 1~52 카드 셔플
	srand((unsigned int)time(NULL));
	int cardShuffle1 = rand() % 52 + 1;
	int cardShuffle2 = rand() % 52 + 1;

	// 뽑은 카드 뽑았던 카드 비교  재귀함수로
	for (int i = 0; i < 52; i++)
	{
		cardShuffle1 == deckShuffle[i] ? SameCardShuffle(cardShuffle1) : cardShuffle1;
	}

	// 같은 카드 뽑기 방지
	while (cardShuffle1 == cardShuffle2)
	{
		cardShuffle2 = rand() % 52 + 1;
	}
	// 뽑은 카드 뽑았던 카드 비교  재귀함수로
	for (int i = 0; i < 52; i++)
	{
		cardShuffle2 == deckShuffle[i] ? SameCardShuffle(cardShuffle2) : cardShuffle2;
	}

	// 뽑았던 카드 버리기 / deckShuffle[i] 에 값을 넣어서 두번 다시 못뽑게 하기용
	for (int i = 0; i < 52; i++)
	{
		if (deckShuffle[i] != cardShuffle1 && deckShuffle[i] ==0)
		{
			deckShuffle[i] = cardShuffle1;
			previousCard = cardShuffle1;
			cardShuffle1 = 0;	// 이걸 안하면 문자가 하나 안나옴
			continue;
		}

		if (deckShuffle[i] != cardShuffle2 && deckShuffle[i] == 0)
		{
			deckShuffle[i] = cardShuffle2;
			nextCard = cardShuffle2;
			cardShuffle2 = 0;
			break;
		}
	}

	// 카드 크기 비교 (출력 순서대로 하기 위해서)  // 출력부분
	if ((previousCard %13) < (nextCard %13))
	{
		CardPrint(previousCard);
		CardPrint(nextCard);
		printf("\n");
	}
	else if ((previousCard % 13) > (nextCard % 13))
	{
		CardPrint(nextCard);
		CardPrint(previousCard);
		printf("\n");
	}

}

// 뽑았던 카드와 비교해 같으면 다시 셔플
int SameCardShuffle(int cardShuffle)
{
	// 제한해주는 부분
	if (cardShuffle == 53)
		cardShuffle = 1;
	for (int i = 0; i < 52; i++) 
	{
		if (deckShuffle[i] == 0)
			continue;
		cardShuffle == deckShuffle[i] ? SameCardShuffle(cardShuffle + 1) : cardShuffle;
	}
	return cardShuffle;
}

// 카드 출력
void CardPrint(int cardNum)
{
	// 문양부분 출력 
	if (cardNum > 0 && cardNum < 14)
		printf("|♥");
	else if (cardNum >= 14 && cardNum < 27)
		printf("|◆");
	else if (cardNum >= 27 && cardNum < 40)
		printf("|♠");
	else if (cardNum >= 40 && cardNum < 53)
		printf("|♣");

	// 숫자부분 출력
	switch ((cardNum % 13) + 1)
	{
		case 1:
			printf("A| ");
			break;
		case 11:
			printf("J| ");
			break;
		case 12:
			printf("Q| ");
			break;
		case 13:
			printf("K| ");
			break;
		default :
			printf("%d| ", (cardNum % 13) + 1);		// 숫자출력구간
			break;
	}
}

void PlayerBetting()
{
	// 배팅액 설정
	printf("배팅액을 설정해 주세요 : ");
	scanf("%d", &bettingMoney);
	while (bettingMoney > playerMoney)
	{
		printf("------------------------\n");
		printf("배팅액이 소지금보다 큽니다 \n");
		printf("배팅액을 재설정해 주세요 : ");
		scanf("%d", &bettingMoney);
	}
	while (bettingMoney <= 0)
	{
		printf("------------------------\n");
		printf("배팅액을 제대로 입력해주세요 \n");
		printf("배팅액을 재설정해 주세요 : ");
		scanf("%d", &bettingMoney);
	}
}

// 플레이어 선택
void PlayerChoiceCard()
{
	printf("플레이어 선택시간 콜:1 / 다이 :2  \n");
	scanf("%d", &dealChoice);

	while (dealChoice != 1 && dealChoice != 2)
	{
		printf("------------------------\n");
		printf("재선택 콜:1 / 다이 :2  \n");
		scanf("%d", &dealChoice);
	}

	if (dealChoice == 2)
	{
		printf("소지금 차감 : 배팅액의 절반\n");
		playerMoney -= bettingMoney / 2;
		printf("플레이어 현재 소지금 : %d\n", playerMoney);
		dealChoice = 0;
		return;
	}

	// 플레이어 카드 셔플
	PlayerCardShuffle();

	// 카드 크기 비교 (출력 순서대로 하기 위해서)  // 출력부분
	if ((previousCard % 13) < (nextCard % 13))
	{
		CardPrint(previousCard);
		CardPrint(playerCard);
		CardPrint(nextCard);
		printf("\n");
	}
	else if ((previousCard % 13) > (nextCard % 13))
	{
		int temp = previousCard;
		previousCard = nextCard;
		nextCard = temp;

		CardPrint(previousCard);
		CardPrint(playerCard);
		CardPrint(nextCard);
		printf("\n");
	}

	// 승리
	if ((playerCard % 13 > previousCard % 13) && 
		(playerCard % 13 < nextCard % 13))
	{
		printf("플레이어 승리!!\n");
		printf("배팅액 두배 획득! : %d\n", bettingMoney * 2);
		playerMoney += bettingMoney * 2;
		printf("플레이어 현재 소지금 : %d\n", playerMoney);
	}
	// 패배
	else if(playerCard % 13 <= previousCard % 13)
	{
		printf("플레이어 패배!!\n");
		printf("배팅액 차감! : %d\n", bettingMoney);
		playerMoney -= bettingMoney;
		printf("플레이어 현재 소지금 : %d\n", playerMoney);

		if (playerMoney <= 0)
		{
			printf("플레이어 파산!\n");
			printf("게임 종료!\n");
		}
	}
	else if (playerCard % 13 >= nextCard % 13)
	{
		printf("플레이어 패배!!\n");
		printf("배팅액 차감! : %d\n", bettingMoney);
		playerMoney -= bettingMoney;
		printf("플레이어 현재 소지금 : %d\n", playerMoney);

		if (playerMoney <= 0)
		{
			printf("플레이어 파산!\n");
			printf("게임 종료!\n");
		}
	}
}

// 플레이어 카드 셔플
void PlayerCardShuffle()
{
	srand((unsigned int)time(NULL));
	playerCard = rand() % 52 + 1;

	for (int i = 0; i < 53; i++)
	{
		if (playerCard == deckShuffle[i])
			playerCard = rand() % 52 + 1;
	}
	// 플레이어 덱 사용 후 버리기
	for (int i = 0; i < 53; i++)
	{
		if (deckShuffle[i] != playerCard && deckShuffle[i] == 0)
		{
			deckShuffle[i] = playerCard;
			break;
		}
	}
}

 

반응형

 

반응형

'연습문제' 카테고리의 다른 글

C 포인터2 문제  (0) 2023.05.18
C 포인터 문제  (0) 2023.05.17
C언어 베이스볼 게임  (0) 2023.05.12
C언어 숫자 추측게임  (0) 2023.05.12
C언어 가위바위보 게임  (0) 2023.05.12

+ Recent posts