C 스택 질문입니다.
글쓴이: ccs****@Naver / 작성시간: 금, 2018/08/10 - 10:48오전
안녕하십니까? 입사 2개월차 초보 직장인 개발자입니다.
스택의 Pop을 구현중에 질문사항이 생겨 이곳에 올려 보게 되었습니다.
** 답글 주신 분들의 도움과 2일에 걸친 밤샘 끝에
코드를 완성하여 보았습니다.
프로그램의 목표와 소스의 중요 부분을 올리지 않아
의도치 않게 답답함을 느끼게 해드려 죄송스럽습니다. ㅜㅜ
우선 프로그램의 목표는
중위 표기법으로 연산자를 입력하면(예 : 12/6+4*7-3)(문자열로 입력 받습니다.)
이를 숫자, 문자를 구분하여 스택에 담고(스택은 문자열 배열입니다.)
후위 표기법으로 변환하여 배열에 담아 이를 스택을 사용하여 연산하는 것이 목표입니다.
전체 소스를 올립니다.
// @brif // step 1. 중위 표기법으로 수식을 입력받은 후 스택을 이용하여 후위 표기법으로 변환합니다. // step 2. 후위 표기법으로 변환된 수식을 스택에 담아 연산합니다. #define _CRT_SECURE_NO_WARNINGS #include<string.h> #include<stdio.h> #include<stdlib.h> // 스택의 각 요소들(숫자 or 연산자)의 크기 // 몇 자리의 숫자가 들어올지 알 수 없으므로, // 255로 지정하였습니다. #define MAX_SIZE 255 // 스택을 문자열 배열로 선언합니다.(전역변수) char *g_pchStack[MAX_SIZE]; int g_nSize; int g_nTop; // 필요한 함수들을 선언합니다. void InitStack(int nSize); // 스택 초기화 함수 int IsFull(); // 스택이 꽉 찼는지 확인하는 함수 int IsEmpty(); // 스택이 비었는지 확인하는 함수 void Push( char *chdata); // push 함수 char *Pop(); // Pop 함수 void DisposeStack(); // 스택에 할당한 메모리 해제 함수 int main(void) { // ================================================================= // step 1. 중위 표기법을 후위 표기법으로 변환하기 int i; // 입력할 값들을 담을 변수 char chInput[MAX_SIZE] ={0,}; char pchtempinput[MAX_SIZE] = {0,}; // 스택을 초기화 할 때 스택의 크기를 결정하기 위해 사용할 변수 // 연산자의 개수 int nOperationNum = 0; // 연산자 + 숫자의 개수 int nFullNum = 0; // PUSH 할 값을 입력받습니다. scanf("%s", chInput); //----------------------------------------------------- // 입력한 값을 숫자, 연산자로 나누기 위해 공백이라는 구분자를 넣어 // pchtempinput 이라는 문자 배열에 넣어줍니다. // 예를 들어, 12+34라는 값이 입력되었다면 // pchtempinput[0] = 1 // pchtempinput[1] = 2 // pchtempinput[2] = " "(공백) // pchtempinput[3] = + // pchtempinput[4] = " "(공백) // pchtempinput[5] = 3 // pchtempinput[6] = 4 // 와 같은 값이 들어갑니다.. for (i = 0; i<strlen(chInput); i++) { // 입력된 값이 숫자라면, if (chInput[i] > 47 && chInput[i] < 58) { // 숫자가 담깁니다. pchtempinput[strlen(pchtempinput)] = chInput[i]; } else { // 연산자가 나오면 "공백" 연산자 "공백" 으로 처리하여 배열에 담깁니다. pchtempinput[strlen(pchtempinput)] = 0x20; pchtempinput[strlen(pchtempinput)] = chInput[i]; pchtempinput[strlen(pchtempinput)] = 0x20; // 연산자의 개수를 세기 위한 변수 ++ nOperationNum++; } } //------------------------------------------------------- //------------------------------------------------------- // 스택 초기화(동적) // 연산자가 담길 스택의 크기는 (연산자의 개수)로 초기화 합니다. // 연산자가 다 담길 일이 거의 없지만, // 다 담길 경우도 배재할 수 없으므로 연산자의 개수로 초기화 InitStack(nOperationNum); //------------------------------------------------------- //------------------------------------------------------- // 구분자인 공백을 이용하여 숫자, 연산자를 분리합니다. // 이후 숫자는 배열에, 연산자는 스택에 넣습니다. // 숫자는 배열에 순차적으로 담기고, 수식은 스택에 일정한 규칙대로 담기게 됩니다. // 1. +,- 는 스택에 있는 모든 기호를 꺼내어 배열에 보관한 후 자신을 스택에 Push // 2. *,/ 는 스택에 있는 것을 꺼내어 자신과 우선순위를 비교하여 순위가 낮으면 // 꺼낸 연산자를 스택에 다시 Push하고 자기 자신도 Push합니다. // 연산자의 우선순위는 "*" = "/" > "+" = "-" // 잘린 문자열이 담길 변수 char *ptr = strtok(pchtempinput, " "); // 연산자가 임시로 담겨질 문자열(char*) 변수 선언 char *pchTempOperation; pchTempOperation = (char*)malloc(MAX_SIZE); memset(pchTempOperation, 0, MAX_SIZE); // 후위표기법으로 변환된 수식이 담겨질 문자열 배열(char*[]) 변수 선언 // 이 배열의 크기는 숫자 + 연산자 모두가 담길수 있는 크기로 메모리가 할당되어야 함 // 전체 개수를 구하려면 (연산자의 개수 *2) +1 을 하면 된다. nFullNum = (nOperationNum*2) +1; char *chPostfixBuf[MAX_SIZE]; for (int i = 0; i < nFullNum; i++) { chPostfixBuf[i] = (char*)malloc(MAX_SIZE); memset(chPostfixBuf[i], 0, MAX_SIZE); } // 후위표기법 수식이 담겨질 배열의 크기를 알기 위해 선언하는 변수 int nPostfixBufSIze = 0; while (1) { // 수식이 끝일 경우.. if(ptr == NULL) { // 스택의 연산자 전부 꺼내어 배열에 보관 while(1) { // 스택이 비어있는 경우 반복문 빠져나가기 if(IsEmpty() == 0) { break; } pchTempOperation = Pop(); chPostfixBuf[nPostfixBufSIze] = pchTempOperation; nPostfixBufSIze++; } break; } //printf("%s\n", ptr); // 자른 문자열 출력(잘 잘렸는지 테스트용) if (strcmp(ptr, "*")==0 || strcmp(ptr, "/")==0 ) { // 스택이 비어있을 경우 그냥 담는다. if(IsEmpty() == 0) { Push(ptr); } // 스택에 값이 있을 경우 else { // 꺼낸다. pchTempOperation = Pop(); // 값의 우선순위가 낮은 경우 if(strcmp(pchTempOperation, "+")==0||strcmp(pchTempOperation, "-")==0) { // 꺼낸 것을 다시 넣고, 자기 자신도 넣는다. Push(pchTempOperation); Push(ptr); } // 값의 우선순위가 같은 경우 else if(strcmp(pchTempOperation, "*")==0||strcmp(pchTempOperation, "/")==0) { // 꺼낸 것을 배열에 넣고, 자기 자신을 스택에 넣는다. chPostfixBuf[nPostfixBufSIze] = pchTempOperation; Push(ptr); nPostfixBufSIze++; } } } else if(strcmp(ptr, "+")==0 || strcmp(ptr, "-")==0 ) { // 스택이 비어있을 경우 그냥 담는다. if(IsEmpty() == 0) { Push(ptr); } // 스택에 값이 있을 경우 else { // 어떤 연산자가 들어있던 전부 꺼내어 배열에 보관하고, // 자기 자신을 스택에 넣는다. while(1) { // 스택이 비어있는 경우 자기 자신을 넣는다. if(IsEmpty() == 0) { Push(ptr); break; } pchTempOperation = Pop(); chPostfixBuf[nPostfixBufSIze] = pchTempOperation; nPostfixBufSIze++; } } } // 숫자인 경우 바로 배열에 담는다. else { //strcpy(chPostfixBuf[nPostfixBufSIze],pchTempOperation); chPostfixBuf[nPostfixBufSIze] = ptr; nPostfixBufSIze++; } ptr = strtok(NULL, " "); // 다음 문자열을 잘라서 포인터를 반환 } //----------------------------------------------------------------- //----------------------------------------------------------------- // 스택 사용이 끝났으므로, 스택에 할당한 메모리 해제 // 및 사용한 변수들의 메모리 해제 DisposeStack(); //----------------------------------------------------------------- // step 1 끝 // ================================================================= // ================================================================= // step 2. 후위 표기법으로 변환된 수식을 스택에 담아 연산. // 연산을 위해 스택을 한번 더 사용합니다. // 이번엔 연산자의 개수만큼 담기는 것이 아닌 // 숫자의 개수 + 연산자의 개수 만큼 담겨야 합니다. //------------------------------------------------------- // 스택 초기화(동적) // 마지막 계산을 위한 스택의 크기는 (연산자의 개수*2) +1 로 초기화 합니다. // 이 곳에는 모든 숫자와 연산자가 다 담깁니다.. // 예를 들어 1+2+3+4+5 라는 값이 들어오면 9의 크기로 선언해야 합니다. // 연산자 = 4개, 숫자 = 5개 이므로 // (4*2)+1 총 9개의 스택을 생성 nFullNum = (nOperationNum * 2) + 1; InitStack(nFullNum); //------------------------------------------------------- //------------------------------------------------------- // 후위 표기법 연산 // 연산을 위해 Pop된 값(숫자)pchTempNum 임시로 담길 변수 char *pchTempNum = (char *)malloc(MAX_SIZE); memset(pchTempNum, 0, MAX_SIZE); // 계산을 위한 변수들 int num1, num2, opresult; // 배열에 들어있는 값을 앞쪽부터 연산합니다. for(i =0;i<nPostfixBufSIze;i++) { // 값이 연산자이면? // + 연산 if(strcmp(chPostfixBuf[i], "+")==0) { // 스택에 보관되어있는 2개의 숫자를 꺼내어 연산합니다. pchTempNum = Pop(); num2 = atoi(pchTempNum); pchTempNum = Pop(); num1 = atoi(pchTempNum); opresult = num1 + num2; // 연산한 값은 다시 넣어줍니다.. sprintf(pchTempNum,"%d",opresult); Push(pchTempNum); } // - 연산 else if(strcmp(chPostfixBuf[i], "-")==0) { // 스택에 보관되어있는 2개의 숫자를 꺼내어 연산합니다.. pchTempNum = Pop(); num2 = atoi(pchTempNum); pchTempNum = Pop(); num1 = atoi(pchTempNum); opresult = num1 - num2; // 연산한 값은 다시 넣어줍니다.. sprintf(pchTempNum,"%d",opresult); Push(pchTempNum); } // * 연산 else if(strcmp(chPostfixBuf[i], "*")==0) { // 스택에 보관되어있는 2개의 숫자를 꺼내어 연산합니다.. pchTempNum = Pop(); num2 = atoi(pchTempNum); pchTempNum = Pop(); num1 = atoi(pchTempNum); opresult = num1 * num2; // 연산한 값은 다시 넣어줍니다.. sprintf(pchTempNum,"%d",opresult); Push(pchTempNum); } // / 연산 else if(strcmp(chPostfixBuf[i], "/")==0) { // 스택에 보관되어있는 2개의 숫자를 꺼내어 연산합니다.. pchTempNum = Pop(); num2 = atoi(pchTempNum); pchTempNum = Pop(); num1 = atoi(pchTempNum); opresult = num1 / num2; // 연산한 값은 다시 넣어줍니다.. sprintf(pchTempNum,"%d",opresult); Push(pchTempNum); } // 숫자의 경우.. else { // 스택에 넣는다.. pchTempNum = chPostfixBuf[i]; Push(pchTempNum); } } // 후위 표기법 수식 출력 printf("후위 표기법 수식 = "); for(i=0;i<nFullNum;i++) { printf("%s ",chPostfixBuf[i]); } printf("\n"); //결과를 출력합니다. pchTempNum = Pop(); printf("result = %s",pchTempNum); //------------------------------------------------------- //----------------------------------------------------------------- // 스택 사용이 끝났으므로, 스택에 할당한 메모리 해제 // 및 사용한 변수들의 메모리 해제 DisposeStack(); free(pchTempNum); free(pchTempOperation); // 연산까지 전부 잘 되는데 마지막에 이 후위표기법 // 수식이 담겼던 변수의 메모리를 해제할 때 오류가 발생합니다....ㅜㅜ // //for (int i = 0; i < nFullNum; i++) //{ // free(chPostfixBuf[i]); //} //----------------------------------------------------------------- // step 2 끝 // ================================================================= return 0; } void InitStack(int nSize) { // 스택 메모리 동적 할당 for (int i = 0; i < nSize; i++) { g_pchStack[i] = (char*)malloc(MAX_SIZE); memset(g_pchStack[i], 0, MAX_SIZE); } // 스택의 크기 g_nSize = nSize; // 스택 초기화시 top을 -1로 설정 g_nTop = -1; } int IsFull() { // Top이 스택의 크기보다 크면 꽉 찬 상태 if (g_nTop >= g_nSize - 1 ) { // 스택이 꽉 찬 경우 return 0; } return -1; } int IsEmpty() { // Top이 -1이면 빈 상태 if (g_nTop == -1) { // 스택이 비어있는 경우 return 0; } return -1; } void Push(char *chdata) { if (IsFull() == 0) { printf("Stack Overflow!!! \n"); return; } // Top을 1 증가 g_nTop++; // 데이터를 char* 형 배열에 보관 strcpy(g_pchStack[g_nTop], chdata); } char *Pop() { char *chTemp = (char *)malloc(MAX_SIZE); // 스택이 비었을 경우 if (IsEmpty() == 0) { printf("Stack is empty!!! \n"); return 0 ; } // chTemp 변수의 스택의 값을 할당합니다. strcpy(chTemp, (g_pchStack[g_nTop])); // Top을 1 감소 g_nTop--; return chTemp; } void DisposeStack() { // 스택에 할당한 메모리 해제 for (int i = 0; i < g_nSize; i++) { free(g_pchStack[i]); } }
너무 길지만 이대로 실행하시면 바로 실행 되실 겁니다..
한가지 제가 마무리를 못한 부분이
마지막에 동적 메모리 할당한 것을 해제할 때
한 부분이 해제 시 오류가 발생합니다.. 이부분도 한번 봐주시면 감사하겠습니다..
부족한 부분이나 이상하게 만든 부분 지적해주시면 정말 감사하겠습니다.
저같은 초보에게 댓글 남겨주셔서 감사합니다!
Forums:
참고해보세요.
잘되는거 참고해보세요.
stl vector 사용하셔도 됩니다. 구글 네이버 검색하면 예제 나올겁니다.
[C] 스택(Stack) 배열형
http://hyeonstorage.tistory.com/345
[C] 스택(Stack) LinkedList
http://hyeonstorage.tistory.com/346
C언어 자료구조 - 스택 (연결리스트를 이용한 구현)
http://milvus.tistory.com/20
웹 컴파일러
http://codepad.org
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
코드가 너무 조금밖에 안 올라와서 문제를 파악할 수
코드가 너무 조금밖에 안 올라와서 문제를 파악할 수 없습니다.
(e.g., push하는 코드, pop 이후 출력하는 코드 등은 어디있죠?)
게다가 stack 구조체엔 nBuf라는 필드가 없어 보이는데 Pop 함수에서 사용하고 있군요.
지적 감사합니다.!
Push 함수와 오타 수정 하였습니다. 한번만 다시 봐주시면 감사하겠습니다!
대체 무슨 프로그램을 만들고 싶은 건지 아직도
대체 무슨 프로그램을 만들고 싶은 건지 아직도 명확하지 않습니다.
매 push마다 stack에 들어가는 건 임의 길이의 문자열인가요, 아니면 그저 문자 하나인가요?
Push에서 strcpy를 사용했다는 건 전자를 암시합니다. nTop을 1만 증가시켰다는 건 후자를 암시하고요.
질문글 내용에서도 어느 쪽이라고 확실히 명시하지 않았군요. 아마도 깜빡하셨거나, 따로 명시하지 않아도 문제없으리라 생각하셨겠지요.
이건 프로그래머로서 C언어 미숙보다 더 심각한 문제입니다. 귀하께서는 만들고 싶은 프로그램이 무엇인지 정확히 이해하고 있어야 하고, 그걸 다른 사람에게 명확하게 설명할 수 있어야 합니다. 그러지 못한다면 결코 프로그램을 만들 수 없을 뿐만 아니라, 온라인에서 도움을 받기도 어렵습니다.
일단 전자(문자열 Push)일 경우는 현재 코드와 너무 거리가 멉니다. 전부 새로 짜는 수준으로 고쳐야 합니다. 프로그램에 대한 요구 조건에 따라 아래 raymundo님께서 나열하신 테크닉(배열 포인터, 이중 포인터 등)을 사용해야 합니다. C언어 초보자에게는 난이도가 좀 있는 편입니다.
후자(문자 Push)의 경우는 훨씬 간단하고, 지금 코드에서 조금만 고치면 됩니다.
댓글 감사합니다.
프로그램의 목표와 전체 소스를 올려보았습니다.
저의 경우에는 전자에 해당되었습니다.
지적해 주신 대로 문자열 배열 포인터를 사용하여 문자열로 값을 입력받아 보았습니다.
말씀하신 대로 수정해야 할 부분이 많아져 이를 최대한 깔끔하게 정리 했습니다.
한번만 더 봐주시면 감사하겠습니다!
nBuf는 pchBuf를 옮겨적느라 그렇다쳐도, 윗분
nBuf는 pchBuf를 옮겨적느라 그렇다쳐도, 윗분 말씀처럼 push코드나 nBuf에 실제로 할당되는 게 뭔지도 보여주시는 게 좋겠네요.
스택에 저장되는 개별 데이터가 char 값이라면('a', 'b', 등)
strcpy를 쓸 게 아니라
로 하셔야 하겠습니다.
좋은 하루 되세요!
지적 감사 합니다.!
스택에 저장되는 데이터는
Push(&stack, "1234");
와 같이 문자열입니다!
이 경우에는 어떤게 좋은 방법일까요?
그니까... 동적할당합니다라고 글 쓰신 곳에 그냥 그
그니까... 동적할당합니다라고 글 쓰신 곳에 그냥 그 코드가 있으면 훨씬 좋을 텐데요. 문자'열'의 스택인 것 같은데 왜 char * 로 저장 공간을 선언했는지가 제 관심사였거든요. 그래서 '문자'의 스택인가보다 했었습니다.
첫 질문글에 바로 답이 달릴 수 있는데 자꾸 스무고개가 되는 것 같습니다.
(물론 반대로 문제와 전혀 무관한 부분의 코드까지 죄다 올려도 답변하는 사람이 불편하긴 합니다만...)
그냥 코드 전체와, 문제를 재현할 수 있는 메인 함수까지 합쳐서 올려주시죠.
그리고 글 쓸 때 아래 나온 설명대로 code 태그를 쓰면 훨씬 읽기 좋고요.
예를 들어
지금 써주신 것만 보면 pchBuf 가 char * 타입이니까, 여기에 인덱스를 붙이면 pchBuf[0]과 pucBuf[1]은 단 1바이트밖에 떨어져 있지 않습니다.
따라서
이런 식으로 앞의 값을 덮어쓰면서 진행하겠네요.
문자열의 스택이라면 최소한 char 의 2차원 배열 형태여야 할 것이고(제가 졸려서 아래 코드에 오류가 있을 수 있습니다)
정도가 있겠네요.
근데 회사에서 쓰실 거라면 제대로 된 라이브러리를 찾아보시는 것이...?
좋은 하루 되세요!
댓글 감사합니다!!
회사에서 사용하긴 하지만 제가 입사 초기라 저희 회사 차장님께서
저에게 C를 공부하라고 과제를 내주셨던 문제입니다.
code태그를 사용하여 프로그램 소스 전체를 한번 올려 보았습니다.
말씀해 주신 내용중 2중 포인터(char **pchBuf;)를 이용하여
스택 크기를 가변적으로 할당하고자 하였으나 아직 이해력이 부족하여
입력받은 사이즈를 계산해 스택 크기를 할당하는 방법으로 만들어 보았습니다.
또한 댓글로 달아주신 내용 덕에
strcpy를 사용하기 위한 메모리 할당에 대해서도
이해할 수 있게 되었습니다.
마지막으로 제가 올린 전체 소스 내용도 한번만 더 봐주시고, 지적해 주신다면 정말 감사하겠습니다!
제가 남의 코드를 지적할 만한 수준은 못 됩니다만,
제가 남의 코드를 지적할 만한 수준은 못 됩니다만,
표기법 변환 부분은 관심사가 아니라서 스택 부분만 잠깐 훑어봤는데...
MAX_SIZE가 지금 "스택의 크기(원소를 몇 개까지 저장할 수 있는지)"의 최대값으로도 사용되고, 스택에 저장되는 문자열의 최대 길이로도 사용되고 있는데, 의도하신 건지 아니면 뭔가 작성 도중에 착각하신 건지 애매하고요.
제일 눈에 띄는 게 Pop()인데,
좋은 하루 되세요!
댓글 달기