디버깅 중 너무나도 고통받아 질문 올립니다.(세그먼트 오류)

Era의 이미지

	char *ptr2 = strtok(b, " ");
        char **scd_input = (char**)malloc(sizeof(char*) * size);
	for(int i = 0; i<size; i++){
		strcpy(scd_input[i], ptr2);//어차피 size변수로 길이 제한해줄거라 input새로 할당할 필요 없긴한데 그냥 해보고 싶어서 해봤는데 왜 안되나...
		ptr2 = strtok(NULL, " ");
	}
	calculate(&stack, scd_input, size);a

다른 부분의 문제인지는 잘 모르겠지만, 디버깅을 계속 해본 결과 strcpy(scd_input[i], ptr2); 부분에서 세그먼트 오류가 발생하는 것 같습니다. 다른 동일한 형식의 부분에서는 정상작동 하는데 왜 이러는 지 모르겠습니다. 동적할당을 잘못한 건지... 부디 저에게 정답을 알려주세요..

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INF	99999999
 
typedef struct {	//stack 구현에 필요한 NODE
	int data[100];
	struct Node *next;
} Node;
 
typedef struct {	//stack 그자체
	Node *top;
} Stack;
 
 
void push(Stack *stack, char* data){
	Node *node = (Node*)malloc(sizeof(Node));
	strcpy(node->data, data);
	node->next = stack->top;
	stack->top = node;
}
 
char* getTop(Stack *stack){
	Node *top = stack->top;
	return top->data;
}
 
char* pop(Stack *stack){//top 위치에서 데이터 하나를 쏙 빼간다
	if(stack->top == NULL){
		printf("stack underflow\n");
		return -INF;
	}
	Node *node = stack->top;	//선언
	char* data = (char*)malloc(sizeof(char) * 100);//하나의 노드에 들어있는 data의 offset이 100이므로 100을 곱한다.
	strcpy(data, node->data);//data에 문자열 복사
	stack->top = node->next;	//다음걸로 top 바꾸기
	free(node);
	return data;
}
 
// 중위 표기법 to 후위 표기법
// 1. 피연산자 바로 출력
// 2. 연산자 : 자신보다 우선순위 높거나 같은 것을 제외(은 출력), 자신을 스택에 푸시
// 3. 여는 괄호 ( : 스택에 푸시
// 4. 닫는 괄호 ) : ( 만날때까지 스택에서 팝
// 그리고 스택에 남은 연산자를 모조리 출력한다.
 
int getPriority(char* i){	//우선순위 함수
	if(!strcmp(i, "(")) {return 0;}
	if(!strcmp(i, "+") || !strcmp(i, "-")) {return 1;}
	if(!strcmp(i, "*") || !strcmp(i, "/")) {return 2;}
	return 3;
}
 
 
//이 함수의 stack은 연산자와 괄호를 임시 저장하는 역할이다.
//s는 문자를 담는 이중 포인터, 37 + 5가 "37" "+" "5"로 들어오게 할 것이다.
//즉 주소들의 배열로 만든다.
//size는 들어오는 각 문자의 개수 - 공백으로 센다.
char * transition(Stack *stack, char ** s, int size){
	char res[1000] = {};//후위표기법의 결과물
    char *resptr = res;//이걸 넣어서 해결했다... 배열 그대로 리턴하면 NULL로 리턴된다. 왜지? 컴파일러 문젠가?
	for(int i = 0; i<size; i++){
		//조건문 시작
		if(!strcmp(s[i],"+") || !strcmp(s[i],"-") || !strcmp(s[i],"*") || !strcmp(s[i],"/")){//연산자가 들어왔을 때
			while(stack -> top != NULL && getPriority(getTop(stack)) >= getPriority(s[i])){
				strcat(res, pop(stack));//자신보다 우선순위 높은 경우, pop/print.
				strcat(res, " ");//앞선 연산자를 처리해줘야하기 때문이다.
			}
			push(stack, s[i]);	//우선순위보다 높든 낮든 자신을 스택에 넣음
		}
		else if (!strcmp(s[i], "(")) {push(stack, s[i]);}	//(는 스택에 담아 연산자를 보관하는 역할
		else if (!strcmp(s[i], ")")) { 
			while (strcmp(getTop(stack), "(")) { 
				strcat(res, pop(stack)); strcat(res, " ");//)가 나오면 (가 나올때까지 스택에서 pop한다.
			}//보관한 연산자를 꺼내는 역할이다.
			pop(stack);
		}
		else {strcat(res, s[i]); strcat(res, " ");}	//숫자는 바로 입력시킨다.
		//조건문 끝
	}
	while(stack->top != NULL){	// 나머지 연산자를 res에 붙이는 과정
		strcat(res, pop(stack));
		strcat(res, " ");
	}
	return resptr;
}
 
//후위 표기법에 대한 연산 함수
void calculate(Stack *stack, char** s, int size){
	int x,y,z;
	for (int i =0; i < size; i++){
		if(!strcmp(s[i], "+") || !strcmp(s[i], "-") || !strcmp(s[i], "*") || !strcmp(s[i], "/")){
			x = atoi(pop(stack));//연산자가 나오면 앞선 숫자 두개를 꺼낸다.
			y = atoi(pop(stack));
			if(!strcmp(s[i], "+")) z = y + x;
			if(!strcmp(s[i], "-")) z = y - x;
			if(!strcmp(s[i], "*")) z = y * x;
			if(!strcmp(s[i], "/")) z = y / x;
			char buffer[100];
			sprintf(buffer, "%d", z);	//z값 buffer에 문자열로서 저장
			push(stack, buffer);	//연산 결과 buffer를 stack에 push
		}
		else{push(stack, s[i]);}	//숫자는 stack에 push
	}//반복하면 연산결과가 나온다.
	printf("%s\n", pop(stack));
}
 
int main(void){
	Stack stack;
	stack.top = NULL;
	char a[100] = "( ( 3 + 4 ) * 5 ) - 5 * 7 * 5 - 5 * 10";
	int size = 1;
	for(int i =0; i < strlen(a); i++){//공백 개수 세면 size가 나온다.
		if(a[i] == ' ') size++;
	}
 
	char *ptr = strtok(a, " ");	//공백 기준으로 잘라낸 앞부분 연산자/숫자
	char **input = (char**)malloc(sizeof(char*) * size);//주소를 저장하는 배열을 만들려고 이중포인터 선언
 
	for(int i = 0; i<size; i++){						//size 개수만큼의 길이의 배열이 필요하므로 *size
		input[i] = (char*)malloc(sizeof(char)*100);//주소를 저장하는 배열.주어진 크기는 의미 없음
	}
 
	for(int i = 0; i<size; i++){
		strcpy(input[i], ptr);//ptr을 input으로 복사  
		ptr = strtok(NULL, " ");//다음 복사할 것 얻어옴. NULL은 하던거 마저 한다는 뜻이다. 왜인지 괄호와 연산자는 잘리지가 않는다...
	}
 
	char b[1000] = {};//후위표기방식이 저장될 string
    char* res ;
    res = transition(&stack, input, size); 
	strcpy(b, res);   
	printf("후위 표기법 %s\n", b);
 
    ////////
 
	size = 1;
	for (int i =0; i < strlen(b) -1 ; i++){
		if(b[i] == ' ') size++;	//후위표기 결과 size 재측정. 괄호가 사라졌으므로
	}
 
	char *ptr2 = strtok(b, " ");
    char **scd_input = (char**)malloc(sizeof(char*) * size);
 
	for(int i = 0; i<size; i++){
		strcpy(scd_input[i], ptr2);//어차피 size변수로 길이 제한해줄거라 input새로 할당할 필요 없긴한데 그냥 해보고 싶어서 해봤는데 왜 안되나...
		ptr2 = strtok(NULL, " ");
	}
	calculate(&stack, scd_input, size);a
 
 
	return 0;
}
ktd2004의 이미지

잠깐 코드를 봐서는

* char** scd_input에 대한 메모리는 할당이 되었는데.
* char* scd_input[0] ..에 대한 메모리는 할당이 되어있지 않는것 같네요.

다음과 같은 코드가 필요하지 않나 싶습니다.

for (int ..) {
scd_input[i] = malloc(strlen(ptr2));
strcpy(...)
}

라스코니의 이미지

디버그를 해 보세요. 주요 변수에 watch 걸고 line-by-line 으로 넘어가다 보면 포인터가 null 값인 부분이 있을 겁니다.

익명 사용자의 이미지

코드를 올려주신 덕분에 궁금증이 생겨서 돌려봤습니다..
segfault로 너무나 고통받는데 오류라인 근처에서는 도저히 원인을 모르겠으면 valgrind도 한번 돌려보면 좋습니다.
gdb는 147라인에서 죽지만, valgrind돌려보니 아래 133 라인에서 첫번째 오류가 나오네요

거기는

//size는 들어오는 각 문자의 개수 - 공백으로 센다.
char * transition(Stack *stack, char ** s, int size){
	char res[1000] = {};//후위표기법의 결과물
    char *resptr = res;//이걸 넣어서 해결했다... 배열 그대로 리턴하면 NULL로 리턴된다. 왜지? 컴파일러 문젠가?
...
   return resptr;

이렇게 해서는 그 이후 코드가 돌아갈 것 같지 않네요..
배열 그대로 리턴하면 왜 NULL이 되는지 고민해보셔야할 듯.

익명 사용자의 이미지

코드를 올려주신 덕분에 궁금증이 생겨서 돌려봤습니다..
segfault로 너무나 고통받는데 오류라인 근처에서는 도저히 원인을 모르겠으면 valgrind도 한번 돌려보면 좋습니다.
gdb는 147라인에서 죽지만, valgrind돌려보니 아래 133 라인에서 첫번째 오류가 나오네요

거기는

//size는 들어오는 각 문자의 개수 - 공백으로 센다.
char * transition(Stack *stack, char ** s, int size){
	char res[1000] = {};//후위표기법의 결과물
    char *resptr = res;//이걸 넣어서 해결했다... 배열 그대로 리턴하면 NULL로 리턴된다. 왜지? 컴파일러 문젠가?
...
   return resptr;

이렇게 해서는 그 이후 코드가 돌아갈 것 같지 않네요..
배열 그대로 리턴하면 왜 NULL이 되는지 고민해보셔야할 듯.

익명 사용자의 이미지

아.. 중복해서 올라갔군요..
지금 딱 오류가 나는 이유는 위에 ktd2004님이 말씀하신 것이 직접적인 원인일 가능성이 높아보이네요..
그런데 전반적으로 다시 살펴보셔야 하겠습니다.

라스코니의 이미지

transition() 함수에서 resptr = res; 도 문제가 있습니다.
char res[1000]은 지역 변수이고 그 주소값을 return해도 아무 의미가 없는 주소값을 보내게 됩니다.

그걸 가지고 strcpy(b, res); 를 호출하게 되면 오류가 나게 되겠죠.

지역 변수의 값을 return하는 것은 되지만 그 번지 자체를 return 해서는 안됩니다.

Era의 이미지

동적할당을 한 번 더해야한다는 점
그리고 지역 변수 번지 리턴하면 안되는 점 알려주셔서 정말 감사합니다.
많은 깨달음 얻고 갑니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.