switch 문으로 변형
지난 번에 작성했던. 코드의 연장선 이 코드를 확장시켜서 swtich문의 형태로 만들어보자.
문제정의
//알파벳 개수 입력 받아 //임의 대문자알파벳들을 저장
//내가 한줄에 출력될 개수를 입력받아 출력…
//각 알파벳의 개수를 출력 //단 0개는 출력하지 않는다… //한줄에 5개씩 출력
//A : 00개 B : 00개 D : 00개 F : 00개 G : 00개 //H : 00개…..
메뉴설명 //1. 개수 설정 //2. 데이터 설정 //3. 출력 //4. 개수 출력 //5. 종료
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int input(const char * msg); // 상수화 변수. 데이터를 못 바꾸게 막아놓은 것. 문자열을 상수화 할 때 무조건 쓴다고 기억.
void setSpace(int * su, char ** dp);
void setData(char * pt, int su);
void disp(char * pt, int su);
void eaDisp(char * pt, int su);
int main(void) {
int su = 0;
char * pt = NULL;
srand((unsigned)time(NULL));
while (1) {
int select = input("1.개수 설정 2.데이터 설정 3.출력 4.개수 출력 5.종료");
switch (select) {
case 1:
setSpace(&su, &pt);
break;
case 2:
setData(pt, su);
break;
case 3:
disp(pt, su);
break;
case 4:
eaDisp(pt, su);
break;
case 5:
free(pt);
exit(0);// 프로그램 종료 함수
default:
printf("잘못된 입력!!!!\n");
}
}
return 0;
}
int input(const char * msg) {
int su;
printf("%s 입력 : ", msg);
scanf_s("%d", &su);
return su;
}
void setData(char * pt, int su) {
for (int i = 0; i < su; i++) {
pt[i] = rand() % 26 + 65;
}
}
void setSpace(int * su, char ** dp) {
*su = input("개수");
if (*dp != NULL) {
free(*dp);
} // 새롭게 동적할당 받으면 FREE를 안해주니까.
*dp = (char *)malloc(*su);
setData(*dp, *su);
}
void disp(char * pt, int su) {
int su1 = input("한줄에 출력될 개수");
printf("\n###대문자 출력###\n");
for (int i = 0; i < su; i++) {
printf("%-3c", pt[i]);
if (i % su1 == su1 - 1 || su - 1 == i) {
printf("\n");
}
}
}
void eaDisp(char * pt, int su) {
printf("\n####각 대문자 개수 출력####\n");
int tfk = 0;
for (char ch = 'A'; ch <= 'Z'; ch++) {
int cnt = 0;
for (int i = 0; i < su; i++) {
if (pt[i] == ch) {
cnt++;
}
}
if (cnt != 0) {
printf("%c : %d개 ", ch, cnt);
tfk++;
if (tfk % 5 == 0 || ch == 'Z') {
printf("\n");
}
}
else if (ch == 'Z' && tfk % 5 != 0) {
printf("\n");
}
}
}
각각의 함수에 대해서 분석해보자
1. int input(const char *msg);/ /
※const란? constant의 약자로 "변함없는"이란 뜻으로 변수 앞에 붙이면 값과 주소를 변경할 수 없도록 만든다. 즉 변수를 상수로 만든다.
포인터 변수에 const키워드를 사용할 경우
ex) const int* _Max = &Value; // const가 앞에 붙는 경우
int* const _Max = &Value; // const가 뒤에 붙는 경우
const가 앞에 붙는 경우에는 포인터 변수 안에 값을 변경할 수 없고, const가 뒤에 붙는 경우에는 포인터 변수의 주소값을 변경할 수 없다.
const에 대해서 제대로 설명하려면 1시간을 따로 강의해야 한다고 한다. 나중에 c++들을 때, 제대로 공부하려고 한다.
inpnut 함수는 char * msg를 매개변수로 받고, 이를 출력해주는 함수다. 또한 scanf해서 입력 받은 정수값을 리턴해주는 함수다 . 용도 : switch문에 들어갈 때, 인풋을 받기 위해 쓰이며 setSpace 함수에서도 공간을 설정해주기 위해서 값을 입력 받을 때 쓰인다. disp 함수에서도 한줄에 출력을 입력받을 때, input 함수가 쓰인다. 즉 input에 관한 함수를 정의해놓고 다른 곳에서도 계속 사용했다는 점을 확인할 수 있다.
2. void setData(char * pt, int su)
이 함수는 setdata 즉 데이터를 세팅하는 함수다. 즉 랜덤함수를 통해 동적할당한 pt에 값을 집어넣는 것을 말한다. ★여기서 주의해야 할 점
int main(void) {
int su = 0;
char * pt = NULL;
srand((unsigned)time(NULL));
while (1) {
int select = input("1.개수 설정 2.데이터 설정 3.출력 4.개수 출력 5.종료");
switch (select) {
case 1:
setSpace(&su, &pt);
break;
case 2:
setData(pt, su);
break;
위 코드를 보면 srand((unsigned)time(NULL))가 while 윗 부분에 선언이 되었다는 점. 그리고 setData가 2번에서 실행되었다는 점이다. 2번을 누를 때마다, 초기화가 다시 되서 출력이 새롭게 되어야 하기 때문에 이 함수를 만들어준 것이다. 그리고 이 함수는 setSpace에서도 활용이된다.
3. void setSpace(int * su, char **dp)
void setSpace(int * su, char ** dp) {
*su = input("개수");
if (*dp != NULL) {
free(*dp);
} // 새롭게 동적할당 받으면 FREE를 안해주니까.
*dp = (char *)malloc(*su);
setData(*dp, *su);
}
매개변수로 int * su와 char **dp가 쓰인다. int *su에는 input의 리턴값을 넣어주고. 넣어준 값을 setData에 넣어준다.
// Q) 여기서 왜 int 인데 int형을 반환해주나? 이래도 괜찮나?** **su라고 선언한 뒤 *su에 넣어줬기 때문에 상관없다.-> 내가 착각한거다.
if문은 2번을 한번 더 눌렀을 때, 즉 setspace를 두번 해줄 때,dp에 해당하는 값이 이미 있다면 free(dp)를 해줘야 데이터가 쌓이지 않는다. 그 뒤 dp = (char * )malloc(su); 를 통해서 데이터를 동적 할당해준다.
** // Q) 여기서도 int * su이기 때문에 malloc을 할 때도 sizeof(int)가 아닌 포인터를 넣어줘었다. ** 여기서는 sizeof(int)즉 메모리크기를 넣어주는거다. 하지만 여기서 포인터를 넣어준 이유는 char 형에다가 넣어주기 떄문에 su만 넣어줘도 1바이트이므로 문제가 되지 않는다.
더블포인터를 넣어준 이유는. *dp의 주소에 대해서 동적할당을 해줘야 하기 때문이다.
그리고 setData를 해줌으로써, 난수를 발생시킨다.
따라서 case 1번의 경우 setSpace를 해주기만하면 된다. case 2번의 경우 데이터만 새로 설정해주면 되는 것이다.
4. void disp(char * pt, int su)
한 줄에 추력될 개수를 input으로 받아 su1에 저장하고 그 뒤 출력을 해준다. 알파벳들을 출력될 개수에 맞게끔 출력해준다.
5. edDisp(char * pt , int su)
대문자 개수를 출력해주는 함수이다.
내 코드와 비교
이전 게시물에 올린 나의 코드와 비교하여 깨달은 점
동적할당한 값을 공유해서 쓰기 위해서는 더블포인터를 사용하여 포인터를 넘겨줘야 한다는 것. 그리고 int를 계속 생성해서 return 해주기보다 int *를 선언하여 주소값만 매개변수로 받아서 사용할 수 있다는 점. 마지막으로 free에 대해서 신경써야한다는 점이 되겠다.
#Define
#define 매크로 대체문자열 ex) #define MAX 4
- define은 전처리기이다. 컴파일 하기 이전에 매크로를 대체 문자열로 치환하는 전처리기이다.
- 프로그램 상에서 의미를 가지고 있는 데이터로써 그 데이터가 변하지 않는 고정값인 경우 데이터를 구분하기 위해서 사용한다.
- 매크로는 대문자를 권장한다.
매크로 함수를 추가적으로 알려주지 않는 이유는, 이를 발전 시킨 inline함수를 c++에서 배우기 때문이다.
Buffer
데이터의 처리 속도, 처리 단위, 사용 시간 등이 다른 서로 다른 두 장치나 프로그램에서 데이터를 사용하기 위한 임시 기억 장소
#include<stdio.h>
int main(void) {
int age;
//char bt;
char name[10];
printf("나이 입력 : ");
scanf_s("%d", &age);
//fflush(stdin);//2010
getchar();//문자 하나를 입력받는 표준 입력 함수.
printf("이름 입력 : ");
gets_s(name, sizeof(name));
//문자열을 입력받는 표준 입력 함수
//공백문자를 기준으로 데이터를 가져오는 것이 아니라 오직 엔터값을 기준으로 데이터를 가져온다...
//scanf_s("%s", name, sizeof(name));
//printf("혈액형 입력 : ");
//scanf_s("%c", &bt, sizeof(bt));
printf("%d %s\n", age, name);
return 0;
}
위 코드를 사용하면서 버퍼에 대한 개념을 잡을 수 있다.
키보드에서 입력한 데이터를 컴퓨터로 넣어주겠다.
int su를 만들어놓고 scanf를 해서 받았다고 하자.
3을 입력하면 cpu는 3을 가지고 있을까? cpu는 데이터를 처리할 뿐, 데이터를 가지고 있지 않다. 변수 공간에 3을 넣고 6을 넣으면 사실상 6이 들어간다.
그래서 중간에 버퍼를 둔거다. 36이란 데이터를 입력했을 때, 그걸 가지고 와서 넣어주기 위해서 중간에 버퍼를 둔다. 키보드에서 데이터를 입력하면 버퍼에 들어간다.
123을 입력해서 엔터를 쳤다고 하면 이게 버퍼안에서 123이 입력이 되었고 (\n) 엔터가 들어온다. 버퍼 안에서는 공백문자(\n) 이런 것들을 기준으로 데이터를 구분 짓는다. 공백문자까지의 데이터를 cpu가 가지고 간다.
123을 입력한다고 123이 들어가진 않고 49505110이렇게 들어간다 (아스키코드)
키보드의 키값이 아스키 코드. scanf 에다가 %d를 적는 이유. 정수니까. 즉 아스키코드를 가져가서 그걸 정수값으로, %f는 그걸 실수로 바꿔주는 거다.
123입력하고 나서 엔터를 치면 버퍼안에 있던 데이터들은 사라진다. 아스키 코드 10번은 아직 살아있다. (즉 버퍼 안에 \n이 남는다는 얘기다.) 이 상태에서 또 어떠한 정보, 예를 들어 174를 입력하게 되면 174\n이 들어가게 되고, 첫 번째 있는 공백문자를 무시가 된다. 그리고 그 다음 공백문자까지의 데이터를 가져오게 된다. \n174 \n이 상황에서 \n만 남는 상황이다.
여기서 문자형을 입력 받았을 때 문제가 발생한다.
문자라는 건 아스키코드였기 때문에 \n만 놓고 보면 문자라는 것이다. 따라서 버퍼안에 데이터가 남아있으면 char 형의 경우 \n을 그냥 가져가버린다. 그래서 이 경우 문자 입력을 받을 때는 버퍼안에 있는 공백문자가 있던 없던 아스키코드 값에서 버퍼안 1바이트만큼의 데이터를 한번에 다 빼간다. 그래서 이게 입력 받을 떄 문제가 된다.
이걸 처리하기 위해서는 문자 받을 때 저걸 제거해야한다. 즉 버퍼를 비워줘야 한다는 점이다.
visual studio라고 한다면 2010년 까지는 fflush(stdin)을 썼었다. 2017에 쓰면 기능이 상실되서 써도 효과가 없다. 2012 이상에서는 getchar()를 쓰도록 한다. //문자 하나를 입력받는 표준입력함수이다.
문자열을 입력받는 표준함수로는 gets_s(name, sizeof(name));을 사용한다. 이 함수는 공백 문자를 기준으로 데이터를 가져오는 것이 아니라 오직 엔터값을 기준으로 데이터를 가져온다.
참고자료
kg아이티뱅크학원 (자료구조 수업) const키워드 분석한 티스토리 블로그 참고