C언어 문자열 선택정렬에 관해서 질문드립니다.

lhs8421478의 이미지

안녕하세요 C언어 공부를 하고 있는 청년입니다..

우선 배열 main에서 배열선언과 호출은 아래와 같은 소스로 했고요

char string_arr[MAX_STRING][MAX_LENGTH] ={"programer", "joy", "root", "super", "sound", "he"};
select_sort_string(string_arr);

이렇게 해서 배열을 넘겨 주고 선택정렬 하는 함수는 아래와 같이 코딩 했습니다.

/**
	문자열 선택정렬 함수
	인자값으로 받은 이차원 배열에 저장된 문자열을
	알파벳 순으로 선택정렬 한다.
**/
void select_sort_string(char (*arr)[20])
{
	int num1;
	int num2;
	char (*min)[20];
	char tmp[20];
 
	memset(tmp, 0x00, sizeof(20));
	for (num1 = 0; num1 < 5 ; num1++) {
		min = &arr[num1];
		for (num2 = num1 + 1; num2 < 6; num2++) {
			if (strcmp(min[num1], arr[num2]) > 0) {
				min = &arr[num2];
			}
		}
 
		if (arr[num1] != *min) {
			strcpy(tmp, arr[num1]);
			strcpy(arr[num1], *min);
			strcpy(*min, tmp);
		}
	}
}

프로그램을 돌렸을 경우

제가 생각한 완성은 he joy programer root super sound
라고 출력이 되는거였습니다.

그러나 현재 출력상황은

he programer joy super sound root 라고 출력이 됩니다.

단계별로 확인해보니 joy가 programer랑 strcmp로 비교할때 joy이가 programer보다 커서 결과값이 0보다 큰값이 리턴이 됩니다.

이 부분이 전혀 이해가 안가는데 도움 부탁드립니다.

ymir의 이미지

char (*min)[20]; ... 정말 의도를 알기 어렵군요..;;

진짜 min 값은 아마도 min[0] 에 있을텐데, min[num1] 에서 참조가 잘못 일어나는걸로 보입니다.
그 코드 그대로 쓰신다면 strcmp(min[num1], arr[num2]) 부분에서 *min 이나 min[0] 로 바꾸시면 되겠네요.

근데.. 그냥 char *min 으로 바꾸시거나, int min_index; 정도로 바꾸시는게 나을것 같습니다.

void select_sort_string(char arr[][MAX_LENGTH])
{
    int num1;
    int num2;
    char *min;
    char tmp[20];
 
    memset(tmp, 0x00, sizeof(20));
    for (num1 = 0; num1 < 5 ; num1++) {
        min = arr[num1];
        for (num2 = num1 + 1; num2 < 6; num2++) {
            if (strcmp(min, arr[num2]) > 0) {
                min = arr[num2];
            }
        }
 
        if (arr[num1] != min) {
            strcpy(tmp, arr[num1]);
            strcpy(arr[num1], min);
            strcpy(min, tmp);
        }
    }
}

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

lhs8421478의 이미지

흠..(*min)[20]을 사용한 이유는 이차원 배열이라 배열을 가르키는 포인터도 이차원 배열을 가르칠수 있는 포인터를 사용해야 된다고 생각해서 사용해 보았습니다.

소스코드 다시 한글자 한글자 비교해보니 min값이 이상한 값을 가르키고 교환을 하더군요...

min[num1][0]과 arr[num2][0]을 비교해본결과 첫번째 바퀴에서
min[num1][0]이 가르키는 값은 p가 맞고요.. 그리고 j로 변경되고

두번째 바퀴에서는 r을 가르키고 있어서... 이 문제점을 찾아보고 있습니다... 크... 머리아프네요 ㅠㅠ

ymir의 이미지

min 에 저장되어야 하는 값은 arr 의 특정 element 이고, 얘는 그냥 char array (equiv. char *) 입니다.
그러니 min 은 그냥 char * 로 쓰는게 맞는거죠. 즉, 개별 단어의 주소만 min 에 저장하는겁니다.

올리신 코드를 보면 min[0] 에만 최소값이 저장되는데 min[num1] 처럼 써버리면, 최소값이 아닌 문자열과 arr[num2] 를 비교해 버리게 됩니다.
엉뚱한 단어와 비교하고, 그 결과를 반영하게 되는 셈이니 오동작 하게 되는 거죠.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

raymundo의 이미지

strcmp 하기 직전에, 내가 지금 비교하는 원소 두 개가 뭔지 min[num1]과 arr[num2] 를 출력시켜보세요.
의도한 것을 비교하고 있지 않을 겁니다.

그리고 for 루프 끝나기 전에 배열의 현재 상태를 출력시켜보시고요.

지금 작성하신 코드는 num1 이 0일 때는 잘 동작할 겁니다.
그러나 두번째 루프에서 num1이 1이 되는 순간

		min = &arr[num1];           // min 이 arr[1] 을 가리키게 되는데
 
			if (strcmp(min[num1], arr[num2]) > 0) {
// min[1] 은 min 에서 다시 한 칸(char [20]만큼) 뒤에 있는 것이니, 결과적으로 arr[2]와 arr[2]를 비교하게 되겠군요.
 
				min = &arr[num2];
// 여기서 min 이 갱신되기라도 하면, 그 다음번 strcmp에서는 그 갱신된 위치에서 또 한 칸 뒤의 것을 비교할 거고요.
			}
		}
 
		if (arr[num1] != *min) {
			strcpy(tmp, arr[num1]);
			strcpy(arr[num1], *min);
			strcpy(*min, tmp);
		}
	}

즉 엉뚱한 두 원소를 비교하며 최소를 찾고 있고, 그렇게 찾은 것을 arr[num1]과 스왑하고 있으니 정렬이 엉망이 됩니다.

좋은 하루 되세요!

lhs8421478의 이미지

현재 말씀하신대로... strcmp하기 직전 출력해보니...
말씀하신 그대로 되네요...
마지막에는 이상한 저장공간에 있는걸 끌어다 쓰고 비교까지 하고 있네요...
이제 이걸 고치는일만 남았군요... 돌머리좀 굴려보겠습니다..
답변 감사합니다 ^^

lhs8421478의 이미지

min의 값이 완전 이리저리 왔다 갔다 거리네요...

void select_sort_string(char (*arr)[20])
{
		int num1;
		int num2;
		char (*min)[20];
		char tmp[20];
 
    min = &arr[0];
		for (num1 = 0; num1 < MAX_STRING - 1; num1++) {
				for (num2 = num1 + 1; num2 < MAX_STRING; num2++) {
						printf("%s\t%s\t\n", min[num1], arr[num2]);
						if (strcmp(min[num1], arr[num2]) > 0) {
								min = &arr[num2];
								printf("%s\n", min);
						}
						printf("pass\t%d\t%d\n", num1, num2);
						printf("===========\n");
				}
 
				if (arr[num1] != *min) {
						memset(tmp, 0x00, sizeof(20));
						strcpy(tmp, arr[num1]);
						strcpy(arr[num1], *min);
						strcpy(*min, tmp);
				}
 
				printf("%s\t%s\t%s\t%s\t%s\t%s\n", 
				arr[0],arr[1],arr[2],arr[3],arr[4],arr[5]);
				printf("=======================================\n");
		}
}

min값을 초기에 arr[0]에 놓고...

if문 안에서 min값이 변경이 될텐데.. 이게 완전 삼천포로 튀어버리네요...

런맨의 이미지

계속 같은 문제로 고민하시는거 같아서 약간에 도움을 드리면
아마도 포인터와 배열에 대한 개념이 아직 덜 잡히신거같습니다.
저도 말주변이 없어서 잘 설명을 하지 못하는데

min = &arr[0]; 이 초기화는 char (*min)[20]; 에서 (*min)[0];만 초기화 된겁니다.
즉 선언에서 포인터를 20개 선언하고 그중 하나만 초기화했는데
아마도 나머지 뒷부분은 배열을 따라 초기화 된거라고 생각하신거같은데
참 글로 설명하기 힘드네요 ㅠㅠ

근데 사용을
if (strcmp(min[num1], arr[num2]) > 0) {
이런식으로 초기화가 안된 데이터 min[num1] 사용하니깐 꼬이기 시작한 겁니다.
생각 하신대로 동작되게할려면 초기화를
min[i] = &arr[i]; 루프로 하면 되겠죠^^

포인터에 대해 저도 많이 고생했습니다. 아직도 힘듭니다.ㅋㅋㅋ 수고하세요

인생은 도박이다.

lhs8421478의 이미지

답변 감사합니다.

에고... 포인터에 대해 이제 좀 알겠네... 싶어서 좀더 보다보면

산덩이처럼 불어나버리네요....

정말 어려운거 같네요....

이제 소스코드는

void select_sort_string(char (*arr)[20])
{
	int num1;
	int num2;
	char (*min)[20];
	char tmp[20];
 
	for (num1 = 0; num1 < MAX_STRING - 1; num1++) {
		min = &arr[0];
		for (num2 = num1 + 1; num2 < MAX_STRING; num2++) {
			printf("%s\t%s\t\n", min[num1], arr[num2]);
			if (strcmp(min[num1], arr[num2]) > 0) {
				min = &arr[num2];
				printf("%s\n", min);
			}
			printf("pass\t%d\t%d\n", num1, num2);
			printf("===========\n");
		}
 
		if (arr[num1] != *min) {
			memset(tmp, 0x00, sizeof(20));
			strcpy(tmp, arr[num1]);
			strcpy(arr[num1], *min);
			strcpy(*min, tmp);
		}
 
		printf("%s\t%s\t%s\t%s\t%s\t%s\n", 
			arr[0],arr[1],arr[2],arr[3],arr[4],arr[5]);
		printf("=======================================\n");
	}
}

이렇게 바꿨는데...

실행 화면은 생각대로 또 안나오고 전혀 엉뚱하게 나오네요...

미추어 버리겠네요 정말 ㅎㅎㅎㅎ

더 공부를 해봐야겠어요 ㅠㅠ

ymir의 이미지

위에서 계속 strcmp(min[num1], arr[num2]) 에서 min[num1] 이 잘못 되었다고 설명드렸는데..
아직 이해를 못 하신 듯 하네요.

min = &arr[num2];

이건 arr[num2] 이후의 element 들을 min 에 저장하겠다는 뜻이죠?
min[0] 은 arr[num2] 가 맞고, 최소값을 갖고 있지만..
min[num1] 은 단지, arr[num2] 이후의 element 중의 하나 일 뿐입니다.
num1 이 0 이 아니라면, 내가 저장한 min 이 아닌, 전혀 의도하지 않은 문자열을 가지고 비교하게 되는 겁니다.

더군다나 num1 이 MAX_STRING - num2 보다 크면 array 의 index 를 초과하는 문제도 있죠.

실제로 필요한 건 min 값에 해당하는 문자열의 pointer 나 array 의 index 뿐입니다.
char (*min)[20] 처럼 array 의 pointer 로 선언해서 쓸 이유가 없고..
그렇게 쓰더라도 실제 min[0] 또는 *min 에 해당하는 문자열만 min value 로 처리해야 합니다.

#define MAX_STRING      6
#define MAX_LENGTH      20
 
static void sel_sort1(char (*arr)[MAX_LENGTH], int n)
{
        int num1, num2;
        char (*min)[20];
        char tmp[20];
 
        for (num1 = 0; num1 < n - 1 ; num1++) {
                min = &arr[num1];
                for (num2 = num1 + 1; num2 < n; num2++)
                {
                        if (strcmp(*min, arr[num2]) > 0)
                                min = &arr[num2];
                }
 
                if (arr[num1] != *min)
                {
                        strcpy(tmp, arr[num1]);
                        strcpy(arr[num1], *min);
                        strcpy(*min, tmp);
                }
        }
}
 
static void sel_sort2(char arr[][MAX_LENGTH], int n)
{
        int i, j;
        char *min;
        char tmp[MAX_LENGTH];
 
        for (i=0; i<n-1 ; ++i) {
                min = arr[i];
                for (j=i+1; j<n; ++j) {
                        if (strcmp(min, arr[j]) > 0)
                                min = arr[j];
                }
 
                if (arr[i] != min) {
                        strcpy(tmp, arr[i]);
                        strcpy(arr[i], min);
                        strcpy(min, tmp);
                }
        }
}
 
static void sel_sort3(char arr[][MAX_LENGTH], int n)
{
        char tmp[MAX_LENGTH];
        int i, j, min;
 
        for (i=0; i<n-1; ++i)
        {
                min = i;
                for (j=i+1; j<n; ++j)
                {
                        if (strcmp(arr[min], arr[j]) > 0)
                                min = j;
                }
 
                if (i != min)
                {
                        strncpy(tmp, arr[i], sizeof(tmp));
                        strncpy(arr[i], arr[min], sizeof(arr[i]));
                        strncpy(arr[min], tmp, sizeof(arr[min]));
                }
        }
}

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

lhs8421478의 이미지

흠.. 뭔가 복잡하네요... 리눅스에서 GCC로 돌리다보니 warning도 좀 많고요...
warning 없이 처리한다고 이것저것 많이 해봤네요...

그냥 결국 간단하게....
min 을 2차원배열포인터로 안하고... 간단하게.... 걍 포인터 사용해서 쉽게 처리해버렸습니다 ㅡㅡa
2차원 배열포인터는 아직 뭔가 어렵네요 ㅠㅠ 더 공부좀 해봐야겠어요...
계속 min값을 이동시키니 이놈의 시작 위치가 달라져서 배열포인터 안에 담고 있는 값도 틀려져서 계속 삽질만 한거 같았네요 ㅠㅠ
도움 주셔서 감사합니다 (__)

익명 사용자의 이미지

ctrl+f 브라우져에서 찾기를 해보세요
그럼 다음

min[num1]

이걸 검색해보세요. 님이 자꾸 바꿔봤다는 소스에보면

min[num1] 이걸 잘못 사용하고 있다고 계속 말해도 그대로 사용하면서

엉뚱한거만 바꾼거 같네요.

왜 min[num1] 이걸 사용하면 안되는지 공붓해보세요.

lhs8421478의 이미지

답글 감사합니다

리눅스 vi환경이라 ctrl + f 가 없는게 아쉽네요 ㅠㅠ

님 말씀데로... 2차원 배열포인터인데 min(num1)은... min이란놈이 이동하면서 새로 가르켜서 가르키고 있는 값이 틀린데 엉뚱한걸 제가 계속 끌어다 썻네요....

그래서 간단하게 그냥.... 1차원 포인터로 해결 봤습니다 ㅠㅠ

답글 감사했습니다 (__)

댓글 달기

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