수강한 개념
변수
- 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기 학습 기록으로 작성되었습니다.
'교육관련 > 한화비전 VEDA 수강일지' 카테고리의 다른 글
[VEDA 1기 수강일지] 6일차 - C언어 (임베디드/리눅스) (1) (0) | 2024.07.22 |
---|---|
[VEDA 1기 수강일지] 5일차 - C언어 기초 (5) : 파일입출력, 전처리, 비트연산, volatile (0) | 2024.07.19 |
[VEDA 1기 수강일지] 4일차 - C언어 기초 (4) : 구조체 포인터, 연결 리스트 (0) | 2024.07.18 |
[VEDA 1기 수강일지] 2일차 - C언어 기초 (2) : 함수, 배열, 포인터 (0) | 2024.07.16 |
[VEDA 1기 수강일지] 1일차 - C언어 기초 (1) : 변수, 입출력, 제어문 (0) | 2024.07.15 |
댓글