#include <stdio.h>
int main(void)
{
// A = 101 , B = 201, C = 301
// 문 앞에 암호가 걸려있음
int A = 1;
int B = 2;
int C = 3;
printf("Address of A : %p, Password : %d\n", &A, A);
printf("Address of B : %p, Password : %d\n", &B, B);
printf("Address of C : %p, Password : %d\n", &C, C);
int * missionman; //포인터 변수
missionman = &A;
printf("Address of missionman : %p, Password : %d\n", missionman, *missionman);
missionman = &B;
printf("Address of missionman : %p, Password : %d\n", missionman, *missionman);
missionman = &C;
printf("Address of missionman : %p, Password : %d\n", missionman, *missionman);
//미션 : 각 암호에 3을 곱해라
missionman = &A;
*missionman = *missionman *3;
printf("Address of missionman : %p, Password : %d\n", missionman, *missionman);
missionman = &B;
*missionman = *missionman *3;
printf("Address of missionman : %p, Password : %d\n", missionman, *missionman);
missionman = &C;
*missionman = *missionman *3;
printf("Address of missionman : %p, Password : %d\n", missionman, *missionman);
return 0;
}

missionman은 정수형 변수의 주소를 통해서 int형 변수의 값을 직접알아볼 수 있다.
(missionman = &A ---> * A 를 출력하면 1이나옴)
missionman이라는 포인터 변수는 int, char...의 주소값을 알고있는 변수에 찾아가서 값도 바꿀 수 있고, 변수가 존재하는 메모리공간의 주소를 알아올 수도 있다.
여기서 &A로 나타내고 출력된 값은 메모리 공간의 주소를 나타낸다.
강의에서는
printf("Address of A : %d, Password : %d\n", &A, A);
로도 출력이 되는데 왜 우분투에서 작성한 c파일에서는 %p로만 출력이 될까?
#include <stdio.h>
int main(void)
{
int arr[3] = {5, 10, 15};
int * ptr = arr;
for(int i = 0; i < 3; i++)
{
printf("배열 arr[%d] 의 값 : %d\n", i, arr[i]);
}
for(int i = 0; i < 3; i++)
{
printf("포인터 arr[%d] 의 값 : %d\n", i, ptr[i]);
}
ptr[0] = 100;
ptr[1] = 200;
ptr[2] = 300;
for(int i = 0; i < 3; i++)
{
printf("배열 arr[%d] 의 값 : %d\n", i, arr[i]);
}
for(int i = 0; i < 3; i++)
{
printf("포인터 arr[%d] 의 값 : %d\n", i, ptr[i]);
}
return 0;
}
printf("배열 arr[%d] 의 값 : %d\n", i, arr[i]);
// 위의 코드를
printf("배열 arr[%d] 의 값 : %d\n", i, *(arr + i));
//로 수정할 수 있다.
// arr은 arr이라는 배열이 처음 시작되는 주소 값을 갖고 있다.
// 그 주소로부터 i번째 해당하는 값을 가져오는 것임
// 따라서 두 코드의 실행 결과가 같음.

printf("arr 자체의 값 : %p\n", arr);
printf("arr[0] 의 주소 : %p\n", &arr[0]);

printf("arr 자체의 값이 가지는 주소의 실제 값 : %d\n", *arr);
printf("arr[0]의 실제 값 : %d\n", *&arr[0]);

#include <stdio.h>
void swap(int a, int b);
void swap_addr(int *a, int *b);
int main(void)
{
int a = 10;
int b = 20;
printf("a 의 주소 : %p\n", &a);
printf("b 의 주소 : %p\n", &b);
// a와 b의 값을 바꾼다.
printf("Swap 함수 전 => a : %d, b : %d\n", a, b);
swap(a,b);
printf("Swap 함수 후 => a : %d, b : %d\n", a, b);
// swap은 값에 의한 복사라고 생각하면 된다.
// 주소값을 넘겨주기
printf("(주소값 전달)Swap 함수 전 => a : %d, b : %d\n", a, b);
swap_addr(&a,&b);
printf("(주소값 전달)Swap 함수 후 => a : %d, b : %d\n", a, b);
return 0;
}
void swap(int a, int b)
{
printf("(Swap 함수 내) a 의 주소 : %p\n", &a);
printf("(Swap 함수 내) b 의 주소 : %p\n", &b);
int temp = a;
a = b;
b = temp;
printf("Swap 함수 중간 => a : %d, b : %d\n", a, b);
}
void swap_addr(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
printf("(주소값 전달) Swap 함수 중간 => a : %d, b : %d\n", *a, *b);
}
일반적인 함수 호출 방식은 값에 의한 호출입니다. 함수에 인자로 변수를 전달하면 해당 변수의 값이 함수로 복사됩니다. 따라서 함수 내에서 값을 변경해도 원래 변수에는 영향을 미치지 않습니다.
예를 들어, 다음 코드를 봅시다:
#include <stdio.h>
void swap(int a, int b) {
int 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); // x = 10, y = 20
return 0;
}
이 코드는 swap 함수 내에서 변수 a와 b의 값을 교환하지만, main 함수의 x와 y에는 아무런 변화가 없습니다. 이는 swap 함수로 x와 y의 값만 전달되었기 때문입니다.
포인터를 사용하면 변수가 저장된 메모리 주소를 함수로 전달할 수 있습니다. 이를 통해 함수 내에서 직접 변수의 값을 변경할 수 있습니다.
예제를 통해 이해해보겠습니다:
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10;
int y = 20;
swap(&x, &y); // 변수 x와 y의 주소를 swap 함수에 전달
printf("x = %d, y = %d\n", x, y); // x = 20, y = 10
return 0;
}
이 코드는 swap 함수가 변수 x와 y의 값을 실제로 교환합니다. swap 함수에 변수의 주소를 전달하고, 포인터를 사용하여 주소가 가리키는 값을 변경했기 때문에 x와 y의 값이 바뀝니다.
void swap(int *a, int *b) {
int temp = *a; // temp에 a가 가리키는 값 저장
*a = *b; // a가 가리키는 위치에 b가 가리키는 값 저장
*b = temp; // b가 가리키는 위치에 temp 값 저장
}
*a는 포인터 a가 가리키는 변수의 값을 의미합니다.&x는 변수 x의 주소를 의미합니다.따라서, swap(&x, &y)는 변수 x와 y의 주소를 swap 함수에 전달합니다. 함수 내에서 포인터를 사용하여 주소가 가리키는 실제 값을 교환하게 됩니다.
이처럼 포인터를 사용하면 함수 내에서 원래 변수의 값을 직접 변경할 수 있습니다.
#include <stdio.h>
void changeArray(int * ptr);
int main(void)
{
int arr2[3] = {10, 20, 30};
// 여기서 하고 싶은건 10, 20, 30을 갖는 배열에서 3번째 숫자를 50으로 바꾸는 것
changeArray(arr2);
//changeArray(&arr2[0]);
for(int i = 0; i < 3; i++)
{
printf("%d\n", arr2[i]);
}
return 0;
}
void changeArray(int * ptr)
{
ptr[2] = 50;
}
//changeArray(arr2);
//대신에 배열 항목에 해당하는 주소를 불러와 실행시켜도
//똑같은 값을 출력하는 것을 확인할 수 있다.
changeArray(&arr2[0]);

물고기가 6마리 존재한다
이들은 어항에 살고 있는데 사막에 존재한다
사막이 너무 덥고 건조해서 물이 아주 빠릴 증발한다
물이 증발하기 전에 부지런히 어항에 물을 줘서 물고기를 살려야한다
물고기는 시간이 지날수록 점점 커진다.
주의할점 : 강의 내용과 동일한 코드로 실행을 하면 물의 양이 줄어들지 않고, 시간경과도 바뀌지 않는 오류가 발생한다. 따라서 다음과 같은 코드로 수정하여 실행하였다.
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
// 물고기가 6마리 존재한다
// 이들은 어항에 살고 있는데 사막에 존재한다
// 사막이 너무 덥고 건조해서 물이 아주 빠릴 증발한다
// 물이 증발하기 전에 부지런히 어항에 물을 줘서 물고기를 살려야한다
// 물고기는 시간이 지날수록 점점 커진다.
void initData();
int level;
int arrayFish[6];
int * cursor; // 몇 번 물고기에게 물을 줄지 정하기 위한 포인터 변수
void printfFishes();
void decreaseWater(long elapsedTime);
int checkFishAlive();
int main(void)
{
time_t startTime = 0; // 게임 시작 시간 정의
time_t totalElapsedTime = 0;// 총 경과시간
time_t prevElapsedTime = 0; // 직전 경과 시간(최근에 물을 준 시간 간격)
int num; // 몇 번 어항에 물을 줄 것인지, 사용자 입력
initData(); // 레벨이 올라가면 게임 데이터를 초기화 해주어야함
cursor = arrayFish;
startTime = time(NULL); // 현재 시간을 ms 단위로 반환해준다. clock()는 #include <time.h>
while(1) // 물고기가 다 죽거나 게임이 끝날때 까지 무한반복을 해야하기에 while문을 사용한다
{
printfFishes(); //현재 물고기들의 어항 상태를 보고 해주기
//getchar(); // 임시로 사용자 입력 대기. 사용자 입력이 들어와야 다음 진행을 할 수 있도록 설정하여 무한 루프가 진행되는 걸 방지할 수 있다.
printf("몇 번 어항에 물을 주시겠습니까?\n");
scanf("%d", &num);
// 입력값을 체크하기( 현재 어항의 수보다 작거나 크다면 오입력했다는 내용을 출력해주기
if(num < 1 || num > 6)
{
printf("입력값이 잘못되었습니다. 다시 입력해주세요\n");
continue;
}
totalElapsedTime = (time(NULL) - startTime);
printf("총 경과 시간 : %ld 초 \n", totalElapsedTime);
// 직전에 물 준 시간 이후로 흐른 시간을 표시해주기
time_t elapsedTime = totalElapsedTime - prevElapsedTime;
printf("최근 경과 시간 : %ld 초\n", elapsedTime);
//어항의 물을 감소시키기
decreaseWater(elapsedTime);
// 사용자가 입력한 어항에 물 주기
// 만약, 어항의 물이 0이면? 물을 주지 않는다.이미 물고기가 죽은 상태
if(cursor[num-1] <=0)
{
printf("%d번 물고기는 이미 죽었습니다. 물을 주지 않습니다.\n", num);
}
// 만약 어항의 물이 0이 아닌 경우에는 물을 준다
// 그런데 100을 넘지 않는지를 체크해야한다. 물을 주는 시점에서 1이 더해진게 100을 넘지 않는지 확인해야한다 !
else if(cursor[num-1] + 1 <=100)
{
//물을 준다
printf("%d 번 어항에 물을 줍니다. \n\n", num);
cursor[num-1] += 1;
}
// 레벨업을 할 건지 확인하기 (레벨업은 20초 마다 한 번 씩 수행)
if(totalElapsedTime / 20 > level -1)
{
level++;
//level++; 로 해도 똑같이 돌아가는지 확인해보기
printf(" *** 축 레벨업 ! 기존 %d 레벨에서 %d 레벨로 업그레이드 ! ***\n", level-1, level);
if(level ==5)
{
printf("\n\n 축하합니다. 최고 레벨을 달성하였습니다.\n\n");
exit(0);
}
}
// 모든 물고기가 죽었는지 확인하기
if (checkFishAlive() == 0)
{
printf("모든 물고기가 죽었습니다.\n");
exit(0);
}
else
{
printf("물고기가 아직 살아있습니다.\n");
}
prevElapsedTime = totalElapsedTime;
// 최초 물을 준 시간이 10초가 지나고 주었다
// 그 다음 물을 준 시간은 15초일 때다. 그러면 5초라는 값이 prevElapsedTime이다.
// 그 후 25초가 지났을 때 물을 줄텐데, 이는 10초를 계산해야한다.
// 그러면 25에서 15를 빼야하는데, 15라는 저장될 곳이 없다.
// 그래서 15라는 값을 while문 끝나기 전에 prevElapsedTime에 저장해준다
}
return 0;
}
void initData()
{
level = 1; // 게임레벨(1~5)
for(int i = 0; i<6; i++)
{
arrayFish[i] = 100; // 어항의 물 높이 (0 ~ 100)
}
}
void printfFishes()
{
printf("%3d번 %3d번 %3d번 %3d번 %3d번 %3d번\n", 1,2,3,4,5,6); // 여기서 %3d의 3은 출력 칸을 의미한다.
for(int i = 0; i < 6; i++)
{
printf(" %4d ", arrayFish[i]);
}
printf("\n\n");
}
void decreaseWater(long elapsedTime)
{
for(int i = 0; i<6; i++)
{
arrayFish[i] -=(level * 3 *(int)elapsedTime);
// 난이도 조절을 위해 곱셈을 해준다
if(arrayFish[i] < 0)
{
arrayFish[i] = 0;
}
}
}
int checkFishAlive()
{
for(int i = 0; i<6; i++)
{
if(arrayFish[i] > 0)
return 1; // 참 True
}
return 0;
// 6개의 어항을 확인하여 모든 어항의 물이 0보다 작게된다면
// return 1;을 만나지 못하고 return 0; 을 만나게 된다.
}

long startTime = 0; // 게임 시작 시간
long totalElapsedTime = 0; // 총 경과 시간
long prevElapsedTime = 0; // 직전에 물을 준 시간(물을 준 시간 간격)
startTime = clock(); // 프로그램 시작 시간 저장
// 총 경과 시간 계산
totalElapsedTime = (clock() - startTime) / CLOCKS_PER_SEC; // 초 단위로 표시
// 물을 준 시간 간격 계산
prevElapsedTime = totalElapsedTime - prevElapsedTime;
// 어항의 물을 감소시키기
decreaseWater(prevElapsedTime);
/* 수정된 코드 */
time_t startTime = 0; // 게임 시작 시간
time_t totalElapsedTime = 0; // 총 경과 시간
time_t prevElapsedTime = 0; // 직전에 물을 준 시간(물을 준 시간 간격)
startTime = time(NULL); // 현재 시간을 초 단위로 반환
// 총 경과 시간 계산
totalElapsedTime = time(NULL) - startTime;
// 물을 준 시간 간격 계산
time_t elapsedTime = totalElapsedTime - prevElapsedTime;
// 어항의 물을 감소시키기
decreaseWater(elapsedTime);
// 직전 경과 시간을 현재 총 경과 시간으로 업데이트
prevElapsedTime = totalElapsedTime;
차이점과 설명 (ChatGPT 기반)
시간 측정 방식 변경:
기존 코드: clock() 함수를 사용하여 프로그램의 CPU 시간을 측정하고, 이를 초 단위로 변환.
수정된 코드: time(NULL) 함수를 사용하여 시스템 시간을 초 단위로 직접 측정.
변수 업데이트 방식 변경:
기존 코드: prevElapsedTime 변수를 사용하여 경과 시간을 계산하고 업데이트.
수정된 코드: elapsedTime 변수를 사용하여 경과 시간을 계산하고, prevElapsedTime을 나중에 업데이트.
구체적인 동작 설명
기존 코드의 문제점:
시간 측정 방식: clock() 함수는 CPU 시간을 측정하기 때문에, 프로그램이 대기 상태일 때 시간이 정확히 측정되지 않을 수 있습니다. 이는 특히 프로그램이 짧은 시간 동안 실행될 때 문제가 됩니다.
변수 업데이트 문제: prevElapsedTime을 경과 시간 계산과 동시에 업데이트하기 때문에, 이후 계산이 부정확해질 수 있습니다. 이로 인해 물의 감소가 올바르게 작동하지 않을 수 있습니다.
수정된 코드의 개선점:
시간 측정 방식: time(NULL) 함수는 시스템 시간을 초 단위로 측정합니다. 이는 프로그램의 실제 경과 시간을 정확하게 반영합니다.
변수 업데이트 방식: elapsedTime 변수를 사용하여 경과 시간을 계산하고, 이후에 prevElapsedTime을 업데이트합니다. 이는 계산의 정확성을 높입니다.
수정된 코드의 정확한 동작 방식:
프로그램 시작 시간 저장:
startTime = time(NULL);는 프로그램이 시작된 시간을 초 단위로 저장합니다.
총 경과 시간 계산:
totalElapsedTime = time(NULL) - startTime;는 현재 시간과 시작 시간의 차이를 계산하여 총 경과 시간을 구합니다.
경과 시간 계산:
time_t elapsedTime = totalElapsedTime - prevElapsedTime;는 현재 경과 시간과 이전 경과 시간의 차이를 계산하여 실제 물을 준 시간 간격을 구합니다.
어항의 물 감소:
decreaseWater(elapsedTime);는 실제 물을 준 시간 간격을 기반으로 어항의 물을 감소시킵니다.
이전 경과 시간 업데이트:
prevElapsedTime = totalElapsedTime;는 다음 루프에서 사용할 수 있도록 prevElapsedTime을 현재 총 경과 시간으로 업데이트합니다.
결론
수정된 코드에서는 time(NULL)을 사용하여 실제 경과 시간을 측정하고, elapsedTime 변수를 사용하여 경과 시간을 정확히 계산함으로써, 프로그램이 의도한 대로 어항의 물을 감소시킵니다. 이러한 변경은 프로그램의 시간 계산을 더 정확하게 만들어주며, 특히 대기 상태에서도 정확하게 작동하게 만듭니다.