구조체 배열을 이용하여 책 3권을 저장할 수 있는 프로그램이다.
/*도전 프로그래밍 4-1 간단한 도서관리용 프로그램*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct {
char writer[20];
char title[20];
int page;
}BOOK;
int main(void) {
BOOK arr[3];
int i,j;
printf("저자, 제목, 페이지 수 순으로 총 3권을 입력하세요.\n\n");
for (i = 0; i < 3; i++) {
printf("\n%d번째 책 입력\n", i+1);
scanf("%s %s %d", arr[i].writer, arr[i].title, &(arr[i].page));
}
printf("\n\n-----------------도서 정보--------------------\n");
for (j = 0; j < 3; j++) {
printf("\n%d번째 책 정보\n", j+1);
printf("저자: %s\n제목: %s\n페이지 수: %d\n\n", arr[j].writer, arr[j].title, arr[j].page);
}
return 0;
}
도전 2는 도전1에서 구현한 프로그램의 다른 버전인데, 구조체 배열을 선언하는 것이 아닌 구조체 포인터 배열을 선언하고 구조체 변수를 동적으로 할당하는 형태로 만드는 문제였다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char writer[20];
char title[20];
int page;
}BOOK;
int main(void) {
int i, j;
BOOK* ptr[3];
printf("저자, 제목, 페이지 수 순으로 총 3권을 입력하세요.\n\n");
for (i = 0; i < 3; i++) {
ptr[i] = (BOOK*)malloc(sizeof(BOOK));//포인터 배열의 한 블럭 단위로 동적 할당해줘야 한다 ??
printf("\n%d번째 책 입력\n", i + 1);
scanf("%s %s %d", ptr[i]->writer, ptr[i]->title, &ptr[i]->page);
}
printf("\n\n-----------------도서 정보--------------------\n");
for (j = 0; j < 3; j++) {
printf("\n%d번째 책 정보\n", j + 1);
printf("저자: %s\n제목: %s\n페이지 수: %d\n\n", ptr[j]->writer, ptr[j]->title, ptr[j]->page);
}
for(i=0; i<3; i++)
free(list[i]);
}
return 0;
}
포인터 배열의 각 요소에 대해 개별적으로 malloc 함수를 사용하여 메모리를 할당했는데 이것에 대한 이유를 chatGPT에 물어보았다.
"포인터 배열을 사용할 때, 각 요소는 메모리를 가리켜야 하며, 이 메모리는 각각 다른 데이터를 저장하는 데 사용됩니다. 포인터 배열의 각 요소에 메모리를 할당하지 않고 단일 메모리 블럭으로 할당하면 모든 요소가 같은 메모리를 가리키게 되므로 데이터를 올바르게 저장하고 접근할 수 없습니다."
사실 나는 처음에 포인터 배열을 선언과 동시에 한번에 단일메모리블럭으로 할당하였다. 이해해보니 어렵지 않았지만, 포인터에 대해 아직 부족하다는 것을 느꼈다.
#include <stdio.h>
#include <stdlib.h>
void ClearLineFromReadBuffer();
#define LEN 30
typedef struct _book {
char author[LEN];
char title[LEN];
int page;
} book;
int main(void)
{
int i;
book* list[3];
printf("도서 정보 입력\n");
for(i=0; i<3; i++) //데이터 입력
{
book * pBook=(book*)malloc(sizeof(book)*1);
printf("저자 : ");
fgets(pBook->author, LEN, stdin);
printf("제목 : ");
fgets(pBook->title, LEN, stdin);
printf("페이지 수 : ");
scanf("%d", &(pBook->page));
ClearLineFromReadBuffer();
list[i]=pBook;
}
printf("\n도서 정보 출력\n");
for(i=0; i<3; i++) // 데이터 츨력
{
printf("book %d \n", i+1);
printf("저자 : %s", list[i]->author);
printf("제목 : %s", list[i]->title);
printf("페이지 수 : %d \n", list[i]->page);
}
for(i=0; i<3; i++) // 할당된 메모리 해제.
{
free(list[i]);
}
return 0;
}
void ClearLineFromReadBuffer(void)
{
while(getchar() != '\n');
}
입출력 함수를 잘 사용하였다. 나도 더 적극적으로 사용하도록 노력해야겠다.
도전 1에서 구현한 방법보다 도전 2에서 구현한 방법이 지니는 장점이 무엇일까?
나의 생각 : 동적할당을 했기 때문에, 외부 함수에 만약 책에 대한 정보를 담은 배열을 선언했을 때, 소멸되거나 중복되지 않을 것이다. 또한, 쉽게 realloc 함수를 이용해 책의 개수를 늘릴수 있을것이다.(솔직히 잘 모르겠음 아직까진)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
double real;
double imagine;
}Complex;
void ForComplexNumCal(Complex, Complex);
int main(void) {
printf("두 개의 복소수 정보를 입력 받아 두 복소수의 덧셈과 곱셈의 결과를 출력하는 프로그램 입니다\n\n");
Complex a;
Complex b;
printf("복소수 입력1[실수 허수]: ");
scanf("%lf %lf", &a.real, &a.imagine);
printf("복소수 입력2[실수 허수]: ");
scanf("%lf %lf", &b.real, &b.imagine);
ForComplexNumCal(a, b);
return 0;
}
void ForComplexNumCal(Complex a, Complex b) {
printf("합의 결과] 실수 : %f 허수 : %f\n", (a.real + b.real), (a.imagine + b.imagine));
printf("곱의 결과] 실수 : %f 허수 : %f\n", (a.real * b.real) - (a.imagine * b.imagine),(b.real * a.imagine) + (a.real * b.imagine));
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main(void) {
char word[20];
int startA=0, startP=0;
FILE* fp = fopen("D:\\text.txt", "rt");
int ret;
while (1) {
ret=fscanf(fp, "%s", word);
if (ret == EOF)
break;
if (word[0] == 'a'|| word[0] == 'A')
startA++;
else if (word[0] == 'p'||word[0] == 'P')
startP++;
else
continue;
}
printf("a로 시작하는 단어의 수: %d\np로 시작하는 단어의 수: %d", startA, startP);
return 0;
}
fscanf 함수는 scanf 함수와 마찬가지로 공백을 기준으로 문자열을 구분하기 때문에 그 특성을 이용하여 word 배열의 첫번째 인덱스를 if문을 이용해 a, p가 처음으로 들어간 문자열을 골라냈다.
fclose함수를 통해 스트림을 닫아주는 것을 깜빡했다. 습관화 들이자.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main(void) {
FILE* d1 = fopen("D:\\d1.txt", "rt");
FILE* d2 = fopen("D:\\d2.txt", "rt");
char comp1[300];
char comp2[300];
int cmp;
while (1) {
fgets(comp1, sizeof(comp1), d1);
fgets(comp2, sizeof(comp2), d2);
if (feof(d1) && feof(d2) != 0)
break;
}
cmp=(strcmp(comp1, comp2));
if (cmp == 0)
puts("같습니다");
else
puts("다릅니다");
fclose(d1);
fclose(d2);
return 0;
}
feof함수로 d1와 d2 스트림이 두 개다 완료했는지 확인을 한 후 strcmp함수로 비교를 하였다.
/*전화번호 관리 프로그램*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char name[10];
char pnum[20];
}PERINF;
void insert(PERINF* a, int*used,int* max_size) {
if (*used >= *max_size) {
puts("더 이상 데이터 삽입이 불가합니다.");
}
puts("[INSERT]"); printf("\n");
printf("이름을 입력하세요 : ");
scanf("%s",a[*used].name);
printf("전화번호를 입력하세요 : ");
scanf("%s",a[*used].pnum);
puts("데이터가 삽입되었습니다."); printf("\n");
++(*used);
}
void delete(PERINF* a, int*used) {
char sear[10];
int count = 0;
puts("[DELETE]"); printf("\n");
for (int i = 0; i <*used; i++) {
printf("%d번\n\n%s\n%s\n\n",i+1, a[i].name, a[i].pnum);
}
printf("삭제할 사람의 이름을 입력하세요 : ");
scanf("%s", sear);
for (int j = 0; j < *used; j++) {
if (strcmp(sear, a[j].name) == 0) {
++count;
for (int k = j; k < *used; k++)
a[j] = a[j + 1];//벨로그에 설명
puts("삭제하였습니다.");
--(*used);
break;
}
}
if(count<=0)
puts("찾을 수 없습니다."); printf("\n");
}
void search(PERINF* a, int * used) {
char sear[10];
int count=0;
puts("[SEARCH]"); printf("\n");
printf("검색할 사람의 이름을 입력하세요 : ");
scanf("%s", sear);
for (int j = 0; j <= *used; j++) {
if (strcmp(sear, a[j].name) == 0) {
printf("\n%s\n%s\n\n", a[j].name, a[j].pnum);
++count;
break;
}
}
if (count <= 0)
puts("검색 결과가 없습니다."); printf("\n");
}
void printAll(PERINF* a, int * used) {
puts("[Print All]"); printf("\n");
if (*used>0) {
for (int i = 0; i < *used; i++)
printf("%d번\n\n%s\n%s\n\n", i + 1, a[i].name, a[i].pnum);
}
else
puts("데이터가 없습니다"); printf("\n");
}
int main(void) {
int sel, max_size = 30 , used = 0;
PERINF* mem = (PERINF*)malloc(sizeof(PERINF)*max_size);
puts("--------------전화번호 관리 프로그램입니다.----------------");
puts("사용하실 기능을 선택하세요.");
while (1) {
puts("***** MENU *****");
puts("1. Insert");
puts("2. Delete");
puts("3. Search");
puts("4. Print All");
puts("5. Exit");
printf("Choose the item : ");
scanf("%d", &sel);
if (sel == 1)
insert(mem, &used, &max_size);
else if (sel == 2)
delete(mem, &used);
else if (sel == 3)
search(mem, &used);
else if (sel == 4)
printAll(mem, &used);
else if (sel == 5)
break;
else
puts("다시 입력하세요 ");
}
free(mem);
return 0;
}
전화번호 관리 프로그램은 대용량 배열을 한 번에 동적할당하는 방식을 사용했고, 도서 정보 입력 프로그램은 각 요소를 반복적으로 동적할당하는 방식을 사용했어. 왜 각자 다른 방식을 사용한거지?
"전화번호 관리 프로그램"과 "도서 정보 입력 프로그램"은 각각 다른 요구 사항과 목적을 가지고 있기 때문에 다른 동적 할당 방식을 사용했을 가능성이 높습니다. 여기에는 몇 가지 이유가 있을 수 있습니다:
데이터 크기 예측에 따라서: "전화번호 관리 프로그램"은 일반적으로 사용자가 제한된 수의 연락처를 관리하는 데 사용됩니다. 데이터 크기가 예측 가능하고, 최대 연락처 수가 상대적으로 작을 것으로 예상되므로 대용량 배열을 한 번에 동적으로 할당하는 방식을 선택할 수 있습니다.
데이터 변경 빈도에 따라서: "전화번호 관리 프로그램"의 경우, 사용자가 연락처를 추가하거나 삭제하는 빈도가 낮을 수 있습니다. 따라서 데이터 크기가 고정되고 변화가 적을 것으로 예상되므로 한 번에 할당된 배열을 사용할 수 있습니다.
메모리 효율성: "도서 정보 입력 프로그램"은 동적 크기 조정이 필요한 경우가 많을 수 있습니다. 각 도서 정보가 서로 다른 크기를 가질 수 있으며, 새로운 도서가 추가되거나 삭제될 수 있으므로 각 요소를 반복적으로 동적으로 할당하는 방식을 통해 메모리를 효율적으로 활용할 수 있습니다.
용도 및 데이터 구조에 따라서: 각 프로그램은 다른 데이터 구조를 다루며, 이에 따라 동적 할당 방식이 달라질 수 있습니다. "전화번호 관리 프로그램"은 간단한 구조로 고정된 필드 크기를 가지고 있으므로 대용량 배열을 사용할 수 있습니다. "도서 정보 입력 프로그램"은 구조가 복잡하고 각 항목의 크기가 다를 수 있으므로 각 요소를 반복적으로 할당하는 방식이 더 적합할 수 있습니다.
요약하면, 프로그램의 목적, 데이터 크기 예측, 데이터 변경 빈도, 메모리 효율성 및 데이터 구조 등 다양한 요인이 각각의 동적 할당 방식을 결정하는 데 영향을 미칠 수 있습니다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char name[10];
char pnum[20];
} PERINF;
void insert(PERINF* a, int* used, int* max_size) {
if (*used >= *max_size) {
puts("더 이상 데이터 삽입이 불가합니다.");
return;
}
puts("[INSERT]");
printf("\n");
printf("이름을 입력하세요 : ");
scanf("%s", a[*used].name);
printf("전화번호를 입력하세요 : ");
scanf("%s", a[*used].pnum);
puts("데이터가 삽입되었습니다.");
(*used)++;
}
void delete(PERINF* a, int* used) {
char sear[10];
int count = 0;
puts("[DELETE]");
printf("\n");
for (int i = 0; i < *used; i++) {
printf("%d번\n\n%s\n%s\n\n", i + 1, a[i].name, a[i].pnum);
}
printf("삭제할 사람의 이름을 입력하세요 : ");
scanf("%s", sear);
for (int j = 0; j < *used; j++) {
if (strcmp(sear, a[j].name) == 0) {
count++;
for (int k = j; k < *used - 1; k++) {
strcpy(a[k].name, a[k + 1].name);
strcpy(a[k].pnum, a[k + 1].pnum);
}
(*used)--;
puts("삭제하였습니다.");
break;
}
}
if (count <= 0)
puts("찾을 수 없습니다.");
printf("\n");
}
void search(PERINF* a, int used) {
char sear[10];
int count = 0;
puts("[SEARCH]");
printf("\n");
printf("검색할 사람의 이름을 입력하세요 : ");
scanf("%s", sear);
for (int j = 0; j < used; j++) {
if (strcmp(sear, a[j].name) == 0) {
printf("\n%s\n%s\n\n", a[j].name, a[j].pnum);
count++;
break;
}
}
if (count <= 0)
puts("검색 결과가 없습니다.");
printf("\n");
}
void printAll(PERINF* a, int used) {
puts("[Print All]");
printf("\n");
if (used > 0) {
for (int i = 0; i < used; i++)
printf("%d번\n\n%s\n%s\n\n", i + 1, a[i].name, a[i].pnum);
}
else
puts("데이터가 없습니다");
printf("\n");
}
void saveDataToFile(PERINF* a, int used) {
FILE* fp = fopen("D:\\phonebook.txt", "wt");
if (fp != NULL) {
for (int i = 0; i < used; i++)
fprintf(fp, "%s %s\n", a[i].name, a[i].pnum);
fclose(fp);
}
else {
puts("데이터 저장에 실패하였습니다.");
}
}
void loadDataFromFile(PERINF* a, int* used, int* max_size) {
FILE* fp = fopen("D:\\phonebook.txt", "rt");
int count = 0;
if (fp) {
while (count < *max_size && fscanf(fp, "%s %s", a[count].name, a[count].pnum) != EOF) {
count++;
}
/*if (feof(fp) != 0)
puts("파일 열기 완료");*/
*used = count;
fclose(fp);
}
else {
puts("파일을 열 수 없습니다.");
}
}
int main(void) {
int sel, max_size = 30, used = 0;
PERINF* mem = (PERINF*)malloc(sizeof(PERINF) * max_size);
loadDataFromFile(mem, &used, &max_size);
puts("--------------전화번호 관리 프로그램입니다.----------------");
puts("사용하실 기능을 선택하세요.");
while (1) {
puts("***** MENU *****");
puts("1. Insert");
puts("2. Delete");
puts("3. Search");
puts("4. Print All");
puts("5. Exit");
printf("Choose the item : ");
scanf("%d", &sel);
if (sel == 1)
insert(mem, &used, &max_size);
else if (sel == 2)
delete(mem, &used);
else if (sel == 3)
search(mem, used);
else if (sel == 4)
printAll(mem, used);
else if (sel == 5) {
saveDataToFile(mem, used);
break;
}
else
puts("다시 입력하세요 ");
}
free(mem);
return 0;
}
#include <stdio.h>
#include <string.h>
#define NAME_LEN 20
#define TEL_LEN 20
#define MAX_PERSON_NUM 100
typedef struct _Person
{
char name[NAME_LEN];
char phone[TEL_LEN];
} Person;
void ShowMenu(void); //메뉴 출력
void InsertTelInfo(Person *, int *); //데이터 입력 입력
void RemoveTelInfo(Person *, int *); //이름으로 데이터 삭제
void SearchTelInfo(Person *, int); //데이터 검색
void PrintAll(Person *, int); //전체 데이터 출력
void LoadData(Person *, int *); //파일로부터 데이터 읽음.
void StoreData(Person *, int); //파일에 데이터 저장.
int main(void)
{
int choice;
Person perArr[MAX_PERSON_NUM]; // Data 저장을 위한 배열
int perNum=0; // 저장된 인원 수
LoadData(perArr, &perNum);
while(1)
{
ShowMenu();
printf("Choose the item : ");
scanf("%d", &choice);
switch(choice)
{
case 1 :
InsertTelInfo(perArr, &perNum);
break;
case 2 :
RemoveTelInfo(perArr, &perNum);
break;
case 3 :
SearchTelInfo(perArr, perNum);
break;
case 4 :
PrintAll(perArr, perNum);
break;
case 5 :
StoreData(perArr, perNum);
return 0;
default :
printf("Illegal selection.. \n");
break;
}
}
return 0;
}
void ShowMenu(void)
{
printf("\n---------Menu--------\n");
printf(" 1. Insert \n");
printf(" 2. Delete \n");
printf(" 3. Search \n");
printf(" 4. Print All\n");
printf(" 5. Exit\n");
}
void InsertTelInfo(Person * parr, int * pnum)
{
char name[NAME_LEN], phone[TEL_LEN];
printf("[ INSERT ] \n");
printf("Input Name : "); scanf("%s", name);
printf("Input Tel Number : "); scanf("%s", phone);
strcpy(parr[*pnum].name, name);
strcpy(parr[*pnum].phone, phone);
(*pnum)++;
printf("--------> Data Inserted......\n");
}
void RemoveTelInfo(Person * parr, int * pnum)
{
int i, j;
char name[NAME_LEN];
printf("[ Remove ] \n");
printf("Input Name for Removing : "); scanf("%s", name);
for(i=0; i < *pnum; i++)
{
if(strcmp(parr[i].name, name)==0)
{
for(j=i+1; j < *pnum; j++)
{
strcpy(parr[j-1].name, parr[j].name);
strcpy(parr[j-1].phone, parr[j].phone);
}
(*pnum)--;
return;
}
}
printf("-------> Data Not Found......\n");
}
void SearchTelInfo(Person * parr, int num)
{
int i;
char name[NAME_LEN];
printf("[ Search ] \n");
printf("Input Name for Searching : "); scanf("%s", name);
for(i=0; i<num; i++)
{
if(strcmp(parr[i].name, name)==0)
{
printf("---> Name: %s / Tel : %s \n",
parr[i].name, parr[i].phone);
return;
}
}
printf("--------> Data Not Found......\n");
}
void PrintAll(Person * parr, int num)
{
int i;
printf("[ Print All Data ] \n");
for(i=0; i<num; i++)
printf("Name : %s / Tel : %s \n", parr[i].name, parr[i].phone);
}
void LoadData(Person * parr, int * pnum) //data Load
{
FILE * file = fopen("data.dat", "rt");
if(file==NULL)
{
return;
}
while(1)
{
fscanf(file, "%s %s", parr[*pnum].name, parr[*pnum].phone);
if(feof(file)!=0)
break;
(*pnum)++;
}
}
void StoreData(Person * parr, int num) //data store
{
int i;
FILE * file = fopen("data.dat", "wt");
if(file==NULL)
{
printf("file open error!\n");
return;
}
for(i=0; i<num; i++)
fprintf(file, "%s %s ", parr[i].name, parr[i].phone);
}
내 코드와 해설지 코드의 차이는 일단 데이터 저장 방식이 달랐다. 정적이냐 동적이냐가 다른데, 나는 이렇게 생각했다.
동적 할당을 사용하면 필요한 메모리만 할당하므로 메모리를 더 효율적으로 관리할 수 있다. 해설지 코드에서는 최대 개수를 제한하고 정적 배열을 사용하므로, 메모리 사용량이 항상 최대 크기를 차지한다.
동적 할당을 사용하면 데이터 크기가 변경될 때 더 쉽게 대응할 수 있다. 해설지 코드에서는 최대 개수를 정의하고 배열을 할당하므로, 만약 데이터 크기가 예상보다 많이 증가하면 문제가 발생할 수 있다.
정적 할당은 간단하고 안전하지만 메모리 사용량과 데이터크기에 제한이 있을 수 있다는 생각을 하였다.