본문 바로가기
교육관련/한화비전 VEDA 수강일지

[VEDA 1기 수강일지] 3일차 - C언어 기초 (3) : 포인터 응용, 동적 할당, 구조체

by 김수효 2024. 7. 17.
수강한 개념

 

변수
- static 변수 : 전역변수와 같은 영역에 저장되지만, 전역변수처럼 모든 곳에서 쓸 수는 없다
- static 전역변수 : 외부 파일과 중첩되지 않기 위해 사용
- 레지스터 변수 : 메모리 대신 CPU 레지스터를 사용함. 일반변수보다 속도가 빠르다.
- main 함수 명령인수

ㄴint argc : main 함수에 전달되는 정보의 개수

ㄴchar * argv[] : 전달되는 실질적 정보, 문자열로 전달 (연산 시 형변환 필요함)

#include <stdio.h>
#include <stdlib.h>


//argc = 전달되는 인수 개수
int main(int argc, char* argv[])
{
	printf("argc : %d\n", argc);
	printf("argv[0] : %s\n", argv[0]);
	printf("argv[1] : %s\n", argv[1]);
	printf("argv[2] : %s\n", argv[2]);

	//if (argv[1] != NULL && argv[2] != NULL && argv[3] == NULL) -> 처음엔 이렇게 짰는데 번거로움
	if (argc == 3)
	{
		printf("sum = %d\n", atoi(argv[1]) + atoi(argv[2]));
	}
	else printf("인수가 잘못되었습니다.\n");
	
}


다중 포인터
- 이중포인터까지 개념 확실히 하기
- 주소를 넘기면 포인터로 받아야함

#include <stdio.h>

void swap_ptr(char** ppa, char** ppb);

int main()
{
	char* pa = "success";
	char* pb = "failure";

	printf("pa-> %p, pb-> %p\n", pa, pb); //pa, pb = 문자열의 주소
	swap_ptr(&pa, &pb);
	//주소가 변경이 된다
	printf("pa-> %p, pb-> %p\n", pa, pb);
}

void swap_ptr(char** ppa, char** ppb)//포인터를 전달 받는 포인터
{
	char* pt;

	pt = *ppa;
	*ppa = *ppb;
	*ppb = pt;
}


함수 포인터
int (* fp) (int);  : 리턴타입이 int이고 int 매개변수를 받는 함수
ㄴ int * fp (int);  : 리턴 타입이 포인터인 함수 fp
fp = 함수명
함수명 = 함수의 시작주소
- 용량 줄이기 / 확장성 등의 이유로 사용 (조건문 대신 사용가능)

#include <stdio.h>

void add(int a, int b);
void sub(int a, int b);
void mul(int a, int b);
void div(int a, int b);

int main()
{
	/*void (*fp[2]) (int, int); //함수 포인터 선언 //배열로도 가능하다

	fp[0] = add; //함수명으로 전달
	fp[0] (10, 50);
	fp[1] = sub;
	fp[1] (10, 50);*/

	void (*fp[4]) (int, int);
	fp[0] = add;
	fp[1] = sub;
	fp[2] = mul;
	fp[3] = div;

	
	while (1) //함수 포인터를 사용하면 반복문 등을 사용하지 않아도 됨 (용량줄이기 / 확장성 용이)
	{
		printf("연산 선택 : 1. 덧셈 | 2. 뺄셈 | 3. 곱셈 | 4. 나눗셈\n번호입력 : ");
		int num;
		scanf("%d", &num);

		if (num >= 1 && num <= 4)
			fp[num - 1](10, 50);

		else printf("잘못된 접근입니다\n");
	}
	

	return 0;
}

void add(int a, int b)
{
	printf("%d + %d = %d\n", a, b, a + b);
}

void sub(int a, int b)
{
	printf("%d - %d = %d\n", a, b, a - b);
}

void mul(int a, int b)
{
	printf("%d - %d = %d\n", a, b, a * b);
}

void div(int a, int b)
{
	if (a != 0 && b != 0)
		printf("%d - %d = %d\n", a, b, a / b);

	else printf("0은 나눗셈에 사용 할 수 없습니다.\n");
}


void 포인터
- 어떤 자료형이라도 받을 수 있음 (자료형 모름. 주소만 가지고 있는 상태)
- 참조할 때 자료형 정해줘야함
ㄴ ex. *(int*)p

#include <stdio.h>

int main()
{
	char ch = 'a';
	int inum = 10;
	float fnum = 1.234;

	void* vp; //라이브러리 함수에서 많이 사용 malloc, memcpy(void *)

	vp = &ch;
	printf("%c\n", *(char*)vp); //void는 자료형이 없기 때문에 캐스트 연산자 필요
	vp = &inum;
	printf("%d\n", *(int*)vp);
	vp = &fnum;
	printf("%.3f\n", *(float*)vp);

	return 0;
}


메모리 동적할당
- 힙 세그먼트에 할당 -> 개발자가 직접 할당/해제 -> 편한데 책임이 있음

- malloc : 메모리 할당. 할당할 때 자료형 지정 필요 (void 포인터 참고)

- realloc : 메모리 추가할당

- free : 메모리 해제. 할당 첫 주소 입력

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(10*sizeof(int)); //메모리 할당
	if (p == NULL)
	{
		printf("Memory 부족\n");
		return 1;		//exit(1); (반환이 아니고 종료)
	}

	for (int i = 0; i < 10; i++)
		p[i] = 100+i;

	for (int i = 0; i < 10; i++)
		printf("%p, %d\n", &p[i], p[i]);


	//메모리 추가할당
	p = (int*) realloc(p, 20 * sizeof(int));

	for (int i = 10; i < 19; i++)
		p[i] = 100 + i;

	for (int i = 0; i < 19; i++)
		printf("&p[%d] = %p, p[%d] = %d\n",i, &p[i], i, p[i]);



	free(p); //메모리 해제

	return 0;
}


사용자 정의 자료형(구조체)

#include <stdio.h>

struct member
{
	int age;
	char name[20];
};

int main()
{
	struct member person[5];

	//printf("struct size : %d\n\n", sizeof(person[i])); //패딩 데이터 확인해보려고

	
	for (int i = 0; i < 5; i++)
	{
		printf("age : ");
		scanf("%d", &person[i].age); getchar();
		printf("name : ");
		fgets(person[i].name, 20, stdin);
		person[i].name[strlen(person[i].name) - 1] = '\0';

	} printf("\n");

	for (int i = 0; i < 5; i++)
	{
		printf("%d, %s\n", person[i].age, person[i].name);
	} printf("\n");
	

	//나중에 입력 받은거 삭제하는걸로 바꿔보기
	for (int i = 2; i < 5; i++) //배열 삭제 - 뒤에있는거 끌어오기
	{
		person[i] = person[i + 1];
	}
	person[4].age = 0;
	strcpy(person[4].name, "");

	for (int i = 0; i < 5; i++) //삭제확인
	{
		printf("%d, %s\n", person[i].age, person[i].name);
	}


	//person[i] = person[i+1];
	// person2.age = person1.age <-- 되긴함

	//printf("%d, %s\n", person[i].age, person[i].name);
	//printf("%d, %s\n", person2.age, person2.name);



	return 0;
}



문제 구현 / 개선방안

 

1. 구조체를 함수에 포인터로 전달하기 : 성적 처리 시스템 

- 오류 관련 피드백

함수에 구조체를 전달하니 '식이 완전한 개체 형식에 대한 포인터여야 합니다' 라는 오류가 계속 떴다.

타입이나 전달하고 받는 형태에는 문제가 없는 듯 해서 고민을 오래하게 되었는데

원인은 그냥 구조체를 다루면서 해당 파일에는 구조체가 정의되어 있지 않아서 그런거였다.

파일 안에 구조체를 만들어 주거나 구조체가 정의 된 헤더파일을 포함시켜 주니 해결되었다

메데타시 메데타시

#pragma once

//score.h
struct score
{
	char name[20];
	int kor;
	int math;
	int eng;
	float avg;
};

void scoreEnter(struct score* stScore, int* stCount);
void scorePrint(struct score* stScore, int stCount);
void scoreSearch(struct score* stScore, int stCount);
void scoreRemove(struct score* stScore, int count, int* stCount);​
#include <stdio.h>
#include "score.h"
//오류원인 : 구조체가 있는 헤더파일 포함을 안하고 있었음

void scoreEnter(struct score *stScore, int* stCount)		//성적 입력
{
	while (*stCount < 30)
	{
		
		
		printf("이름 입력 : ");
		fgets(stScore[*stCount].name, 20, stdin);
		stScore[*stCount].name[strlen(stScore[*stCount].name) - 1] = '\0';

		if (!strcmp(stScore[*stCount].name, "end")) break;

		printf("국어 성적 : ");
		scanf("%d", &stScore[*stCount].kor);
		printf("수학 성적 : ");
		scanf("%d", &stScore[*stCount].math);
		printf("영어 성적 : "); 
		scanf("%d", &stScore[*stCount].eng); getchar();
		stScore[*stCount].avg = 
			((float)stScore[*stCount].kor + (float)stScore[*stCount].math + (float)stScore[*stCount].eng) / 3;
		printf("평균 %.2f점\n", stScore[*stCount].avg);
		
		(*stCount)++;
	}
}

void scorePrint(struct score* stScore, int stCount)		//성적 출력
{
	for (int i = 0; i < stCount; i++)
	{
		printf("[%s] : 국어 - %d점  수학 - %d점  영어 - %d점  평균 - %.2f점\n", 
			stScore[i].name, stScore[i].kor, stScore[i].math, stScore[i].eng, stScore[i].avg);
	}
		
}

void scoreSearch(struct score* stScore, int stCount)		//성적 검색 : 이름 기준
{
	char sName[20];
	printf("이름 입력 : ");
	scanf("%s", sName);
	int check = 1;

	for (int i = 0; i < stCount; i++)
	{
		if (!strcmp(stScore[i].name, sName))
		{
			printf("[%s] : 국어 - %d점  수학 - %d점  영어 - %d점  평균 - %.2f점\n",
				stScore[i].name, stScore[i].kor, stScore[i].math, stScore[i].eng, stScore[i].avg);
			check = 0;
		}
	}
	if (check) printf("찾으시는 학생이 없습니다.\n");

}

void scoreRemove(struct score* stScore, int count, int *stCount)
{	
	char sName[20];
	printf("이름 입력 : ");
	scanf("%s", sName);
	int check = 1;
	
	for (int i = 0; i < count; i++)
	{

		if (!strcmp(stScore[i].name, sName))
		{
			for (int j = i; i < count; i++)
			{
				stScore[i] = stScore[i + 1];
			}
			check = 0;
			(*stCount)--; //학생수 줄이기
		}

	}
	if(check) printf("삭제하려는 학생이 없습니다.\n");
	

	//삭제확인
	printf("\n[삭제 후 확인]\n");
	for (int i = 0; i < (*stCount); i++)
	{
		printf("[%s] : 국어 - %d점  수학 - %d점  영어 - %d점  평균 - %.2f점\n",
			stScore[i].name, stScore[i].kor, stScore[i].math, stScore[i].eng, stScore[i].avg);
	}
}
#include <stdio.h>
#include "score.h"

int main() {
	//do-while 입력/출력/검색 메뉴 프로그램

	struct score stScore[30];

	int menuNo;
	int stCount = 0; //학생수

	do {
		printf("\n----메뉴----\n");
		printf("1. 입력\n");
		printf("2. 출력\n");
		printf("3. 검색\n");
		printf("4. 삭제\n");
		printf("9. 종료\n");
		printf("선택 ---> ");

		scanf("%d", &menuNo); getchar();

		switch (menuNo) {
		case 1:
			printf("입력 선택하였습니다\n"); 
			scoreEnter(stScore, &stCount);			//성적 입력
			break;
		case 2:
			printf("출력 선택하였습니다\n"); 
			scorePrint(stScore, stCount);				//성적 출력
			break;
		case 3:
			printf("검색 선택하였습니다\n"); 
			scoreSearch(stScore, stCount);			//성적 검색 : 이름 기준
			break;
		case 4:
			printf("삭제 선택하였습니다\n");
			scoreRemove(stScore, stCount, &stCount);			//성적 삭제 : 이름 기준
			break;
		case 9:

			break;
		default:
			printf("잘못 선택하였습니다\n");
			break;
		}

	} while (menuNo != 9);
}
기타 사항

 

- 스택, 힙, 데이터, 코드 세그먼트 알아보기 -> 메모리 구조

ㄴ 코드 영역 : 프로그램의 코드가 저장되는 영역. 여기에 저장된 명령어를 처리함

ㄴ 데이터 영역 : 전역변수와 static 변수가 저장되는 영역. 프로그램 시작 시 할당, 종료시 소멸ㄴ 스택 영역 : 지역변수와 매개변수가 저장되는 영역. 함수가 호출될때 할당, 함수 호출 완료 시 소멸.ㄴ 힙 영역 : 사용자가 직접 관리하는 영역. 동적으로 할당되고 해제됨. 사용자에게 책임이 있음.

 

- 숫자 다음 문자를 입력 받을 때 생기는 문제 : getchar(); or " "안에 %d%*c (뒤에오는 문자 다 버리기)

 


VEDA 바로가기 : www.vedacademy.co.kr

VEDA(한화비전 아카데미) 영상으로 확인하기 : https://url.kr/zy9afd

본 후기는 VEDA(한화비전 아카데미) 1기 학습 기록으로 작성되었습니다.

댓글