C언어 스택 구현 중... 힙과 데이터 메모리에 관련한 질문이 생겼습니다.

hgg2468의 이미지

C언어로 스택을 구현하고 있습니다.

스택 푸쉬, 팝 등등은 잘 됩니다. 근데 이걸 main함수 밖에다가 따로, 스택에 있는 데이터를 print하는 함수를 만들어서 출력을 하려고 보니 문제가 생겼습니다.

아래는 제 코드입니다.

#include <stdio.h>
#include <stdbool.h>
#define MAX_SIZE 100
 
typedef struct 
{
	int key;
	int data;
} element;
 
 
 
typedef struct 
{
	element* item;
	int top;
} Stack;
 
void CreateStack(Stack* sp)
{
	element ele[MAX_SIZE];
	sp->item = ele;
	sp->top = -1;
}
 
bool Push(Stack* sp, element ele)
{
	if (sp->top == 100)
	{
		printf("error : stack is full");
		return false;
	}
	else
	{
		sp->top++;
		sp->item[sp->top].data = ele.data;
		sp->item[sp->top].key = ele.key;
		return true;
	}
}
 
element Pop(Stack* sp)
{
	if (sp->top == -1)
	{
		printf("error : stack is empty\n");
	}
	else
	{
		element temp;
		temp = sp->item[sp->top];
		sp->top--;
		return temp;
	}
}
 
bool IsFull(Stack* sp)
{
	if (sp->top == 100)
	{
		printf("Stack is full\n");
		return true;
	}
	else
	{
		printf("Stack is not full\n");
		return false;
	}
}
 
bool IsEmpty(Stack* sp)
{
	if (sp->top == -1)
	{
		printf("Stack is empty\n");
		return true;
	}
	else
	{
		printf("Stack is not empty\n");
		return false;
	}
}
 
void PrintStack(Stack* sp)
{
	for (int i = 0; i < sp->top; i++)
	{
		printf("%d %d\n", sp->item[i].key, sp->item[i].data);
	}
}
 
int main()
{
	element el1;
	el1.data = 10;
	el1.key = 1;
	element el2;
	el2.data = 20;
	el2.key = 2;
	element el3;
	el3.data = 30;
	el3.key = 3;
	element el4;
	el4.data = 40;
	el4.key = 4;
 
	Stack st1;
	Stack* stp;
	stp = &st1;
	CreateStack(stp);
 
	Push(stp, el1);
	Push(stp, el2);
	Push(stp, el3);
	Push(stp, el4);
	Push(stp, el1);
	Push(stp, el2);
	element tmp1 = Pop(stp);
 
	for (int i = 0; i < stp->top; i++)
	{
		printf("%d %d\n", stp->item[i].key, stp->item[i].data);
	}
 
	PrintStack(stp);
	IsFull(stp);
	IsEmpty(stp);
}

여기서 이상한 점이...
main함수 안에서 스택을 출력하는 건 잘 됩니다. 순서대로 1 10, 2 20, 3 30, 4 40 이렇게 잘 나옵니다.
근데 그 아래 따로 함수로 선언한 PrintStack으로 출력을 하면

1 10
2832919 2938281
2948382 2938374

이런식으로 처음 값만 제대로 나오고 그 뒤로부터는 포인터 값? 으로 추정되는 이상한 값들이 나옵니다.

생각을 해 봤는데

1. 혹시 PrintStack함수에 인자로 들어가는 Stack* 의 element 어레이가 사라진 건가.. 싶기도 하고 아니면
2. 애초에 CreateStack에서, element 어레이를 생성하고, element의 포인터를 리턴을 해 줘도 그 함수를 빠져 나오면 함수 안에서 생성된 element 어레이는 다시 사라져서 결국 이상한 곳을 가리키게 된 것인가 싶기도 합니다..

element ele[MAX_SIZE]를 CreateStack 밖에다가 그냥 전역변수로 선언하면 또 PrintStack은 문제없이 실행 됩니다.

2 가 문제라면 PrintStack 말고 다른 모든 함수나 main문 안의 Printf문에서도 오류가 생겨야 할 것 같은데 그러지는 않고...
처음 탐색하는 값만 제대로 나오고 그 후부터는 이상한 포인터값이 나오는 것도 이상하고...(맨 처음에 출력을 2번쨰 데이터로 하면 2번째 데이터는 잘 나옵니다. 근데 또 그 후 부터 이상하게 나옵니다.

구현이 되긴 하지만.. 좀 더 정확히 공부하고 싶어서 이렇게 질문 올립니다.

감사합니다.

hgg2468의 이미지

매우 보기 나쁘네요 .. ㅜ

김정균의 이미지

글 올릴 때 보시면.. 코드는 <code>~</code> 블럭으로 감싸라고 알려 드리고 있는데요.

hgg2468의 이미지

근데 자동으로 된건가... 제가 안했는데 이쁘게 처리되었네요

hgg2468의 이미지

1 10
2 20
3 30
4 40

1 10
1395198872 3930052
3930104 3930112
3930096 3930108

raymundo의 이미지

문제의 원인은 (높은 확률로) 2번이 맞습니다.

CreateStack 함수가 종료되면서 지역 변수인 ele 는 더 이상 유효하지 않게 되고, 다른 함수가 호출되면서 스택프레임이 덮어쓰여서 값이 바뀌었겠죠.

처음에 잘 나온 것은, 그저 운이 좋았을 뿐입니다. 덮어쓰이지 않은 상태에서 읽으면 원래 썼던 값이 나오겠죠.

지금 코드의 경우 ele 배열의 원소의 개수가 100개이고 그래서 800바이트 정도를 차지하는데, 다른 함수가 호출될 때 스택 프레임은 높은 주소에서부터 쌓이다보니 가장 낮은 주소에 있는 ele[0]까지는 미처 덮어쓰지 못하고 끝났던 거죠.

제가 지금 리눅스의 gcc로 컴파일하고 실행했을 때는 PrintStack 함수마저도 올바른 결과를 냈습니다. 하지만

void foo() {
    int arr[196];     // 저 ele 배열을 전부 덮어쓸만큼 크게 잡고
    int i;
    for ( i=0; i<200; i++) {
        arr[i] = 1000+i;
    }
}
 
// main 에서
    foo();   // 출력 전에 실행하면
 
    for (int i = 0; i < stp->top; i++)
    {
        printf("%d %d\n", stp->item[i].key, stp->item[i].data);
    }

덮어쓰인 결과가 나오죠.

$ gcc -O0 -std=c99 t.c
$ ./a.out
1000 1001
1002 1003
1004 1005
1006 1007
 
1000 1001
1002 1003
1004 1005
1006 1007
Stack is not full
Stack is not empty

좋은 하루 되세요!

shint의 이미지

함수 안에 지역 변수. 사용 문제점

함수를 나가면. 배열 데이터'가 사라지거나. 남더라도. 변경 된다고 알고 있습니다.
https://ideone.com/FtxpXu

씹어먹는 C 언어 - <17. 변수의 생존 조건 및 데이터 세그먼트의 구조
http://itguru.tistory.com/83

윈도우10 64비트에서 해보니. 잘못된 값이 출력 됩니다.
운이 좋은건지. 확인이 안된건지. 잘못된 메모리 접근 메시지는 보이지 않네요.
해당 주소'는 제대로 얻어서. 접근 및 변경은 가능한가 봅니다.
동적 메모리 할당을 하거나. 전역 변수 설정. static 변수를 만드는 방법. 등에 해결방법이 있습니다.

#include <stdio.h>
#include <stdlib.h>
 
#include <stdbool.h>
 
#define MAX_SIZE 100
 
typedef struct 
{
    int key;
    int data;
} element;
 
typedef struct 
{
    element* item;
    int top;
} Stack;
 
void CreateStack(Stack* sp)
{
    element ele[MAX_SIZE];       //<-------- 함수안에서 지역 변수를 사용한것이 문제 ★★★★★
 
    int i=0;
    for(i=0; i<100; i++)
    {
        ele[i].data = i+1000;
        ele[i].key = i+1000;
    }
 
    //함수 내부에 정적변수'는 함수 밖으로 나가면. 소멸 되거나.
    //남더라도. 변경이 될 수 있을겁니다.
    sp->item = ele;
    sp->top = -1;
    printf("CreateStack() ele %x\n", ele);
    printf("CreateStack() sp->item %x\n", sp->item);
    printf("CreateStack() sp->item->data %d\n", sp->item->data);
    printf("CreateStack() sp->item->key %d\n", sp->item->key);
}
 
bool Push(Stack* sp, element ele)
{
    if (sp->top == 100)
    {
        printf("error : stack is full");
        return false;
    }
    else
    {
        sp->top++;
        printf("Push() sp->item[sp->top].data %d\n", sp->item[sp->top].data);
        printf("Push() sp->item[sp->top].key %d\n", sp->item[sp->top].key);
 
        sp->item[sp->top].data = ele.data;
        sp->item[sp->top].key = ele.key;
        printf("Push() sp->top %d\n", sp->top);
        return true;
    }
    return true;
}
 
element Pop(Stack* sp)
{
    if (sp->top == -1)
    {
        printf("error : stack is empty\n");
    }
    else
    {
        printf("Pop() sp->top %d\n", sp->top);
        element temp;
        temp = sp->item[sp->top];
        sp->top--;
        return temp;
    }
}
 
bool IsFull(Stack* sp)
{
    if (sp->top == 100)
    {
        printf("Stack is full\n");
        return true;
    }
    else
    {
        printf("Stack is not full\n");
        return false;
    }
}
 
bool IsEmpty(Stack* sp)
{
    if (sp->top == -1)
    {
        printf("Stack is empty\n");
        return true;
    }
    else
    {
        printf("Stack is not empty\n");
        return false;
    }
}
 
void PrintStack(Stack* sp)
{
    for (int i = 0; i < sp->top; i++)
    {
        printf("%d %d\n", sp->item[i].key, sp->item[i].data);
    }
}
 
int main()
{
    element el1;
    el1.data = 10;
    el1.key = 1;
 
    element el2;
    el2.data = 20;
    el2.key = 2;
 
    element el3;
    el3.data = 30;
    el3.key = 3;
 
    element el4;
    el4.data = 40;
    el4.key = 4;
 
    Stack st1;
    Stack* stp;
    stp = &st1;
 
    CreateStack(stp);
    printf("main() stp->item %x\n", stp->item);
    printf("main() stp->top %d\n", stp->top);
 
    for (int i = 0; i < 10; i++)
    {
        printf("main() %d %d\n", stp->item[i].key, stp->item[i].data);
    }
 
    Push(stp, el1);
    Push(stp, el2);
    Push(stp, el3);
    Push(stp, el4);
    Push(stp, el1);
    Push(stp, el2);
 
    element tmp1 = Pop(stp);
 
    for (int i = 0; i < stp->top; i++)
    {
        printf("%d %d\n", stp->item[i].key, stp->item[i].data);
    }
 
    PrintStack(stp);
    IsFull(stp);
    IsEmpty(stp);
    return 0;
}
 
//idone 출력 결과
CreateStack() ele 1e954ae0
CreateStack() sp->item 1e954ae0
CreateStack() sp->item->data 1000
CreateStack() sp->item->key 1000
main() stp->item 1e954ae0
main() stp->top -1
main() 1000 1000
main() 1001 1001
main() 1002 1002
main() 1003 1003
main() 1004 1004
main() 1005 1005
main() 1006 1006
main() 1007 1007
main() 1008 1008
main() 1009 1009
Push() sp->item[sp->top].data 1000
Push() sp->item[sp->top].key 1000
Push() sp->top 0
Push() sp->item[sp->top].data 1001
Push() sp->item[sp->top].key 1001
Push() sp->top 1
Push() sp->item[sp->top].data 1002
Push() sp->item[sp->top].key 1002
Push() sp->top 2
Push() sp->item[sp->top].data 1003
Push() sp->item[sp->top].key 1003
Push() sp->top 3
Push() sp->item[sp->top].data 1004
Push() sp->item[sp->top].key 1004
Push() sp->top 4
Push() sp->item[sp->top].data 1005
Push() sp->item[sp->top].key 1005
Push() sp->top 5
Pop() sp->top 5
1 10
2 20
3 30
4 40
1 10
2 20
3 30
4 40
Stack is not full
Stack is not empty
 
//윈도우10 64비트 - Visual Studio 2017 Community 출력 결과
CreateStack() ele d8eff520
CreateStack() sp->item d8eff520
CreateStack() sp->item->data 1000
CreateStack() sp->item->key 1000
main() stp->item d8eff520
main() stp->top -1
main() 1000 1000
main() 1001 1001
main() 1002 1002
main() 1003 1003
main() 1004 1004
main() 1005 1005
main() 1006 1006
main() 1007 1007
main() 1008 1008
main() 1009 1009
Push() sp->item[sp->top].data 1000
Push() sp->item[sp->top].key 1426214296
Push() sp->top 0
Push() sp->item[sp->top].data 0
Push() sp->item[sp->top].key 0
Push() sp->top 1
Push() sp->item[sp->top].data 0
Push() sp->item[sp->top].key 0
Push() sp->top 2
Push() sp->item[sp->top].data 32767
Push() sp->item[sp->top].key 1425063638
Push() sp->top 3
Push() sp->item[sp->top].data 32767
Push() sp->item[sp->top].key 1426214296
Push() sp->top 4
Push() sp->item[sp->top].data 1005
Push() sp->item[sp->top].key 1005
Push() sp->top 5
Pop() sp->top 5
1426214296 32767
0 0
-2145599704 54275
1425063638 32767
1426214296 32767
0 0
-2145599704 54275
1425063638 32767
Stack is not full
Stack is not empty

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.