컴파일러가 너무 똑똑한 겁니까? 아님, 버그입니까?

seoleda의 이미지

include <stdio.h>

int* func(){
    static int a[3]={1,2,3};
    return a;
};
int main(int argc, char* argv[]){
    int *ret=func();
    printf("%d %d %d\n", *ret, *(ret+1), *(ret+2));
    printf("%d %d %d\n", *ret, *(ret++), *(ret++));
};

실행결과:
1 2 3
3 2 1

정말 신기합니다.

같은 *ret에 대해서도 참조하는 내용이 다르네요. ^^

이거 왜 그러는지 아시는분 계시나요?

그럼 이만.

eungkyu의 이미지

seoleda wrote:
include <stdio.h>

int* func(){
    static int a[3]={1,2,3};
    return a;
};
int main(int argc, char* argv[]){
    int *ret=func();
    printf("%d %d %d\n", *ret, *(ret+1), *(ret+2));
    printf("%d %d %d\n", *ret, *(ret++), *(ret++));
};

실행결과:
1 2 3
3 2 1

정말 신기합니다.

같은 *ret에 대해서도 참조하는 내용이 다르네요. ^^

이거 왜 그러는지 아시는분 계시나요?

그럼 이만.

제대로 실행된거 같은데, 어디가 문제되는 부분인가요?
ret값이 변하니까 당연히 참조하는 내용이 바뀌죠.

bugiii의 이미지

함수에 인자를 전달하는 순서 (컴파일러마다 다릅니다.)
포인터 증감 연산

두가지를 잘 생각해보시면 답이 나옵니다.

그런데, 위의 코드가 어디에서 나왔나요? 그냥 실험용이라면 몰라도 면접때 저런 코드를 제출한다면 바로 탈락의 지름길이라고 생각합니다만...

atdda의 이미지

이상하기까지 하지는 않은데요. :o

두번째 printf에서 직접 ret 를 찍어보세요. bugiii 님이 충분한 답을 해주신 듯 합니다.

May The Force Be With You.

dudungsil의 이미지

이런 나중에 해보니.. 컴파일러 특성을 타는군요. 그래서 삭제합니다. -_-;;

산넘어 산

sylphong의 이미지

printf("%d %d %d\n", *ret, *(ret++), *(ret++)); 이 부분은 문제가 좀 있군요.. 위와 같이 ++를 한라인에 같은변수에대해 중복해서 쓰는경우에 대해서는 undefined behavior입니다..따라서 컴파일러가 맘대로 할수있습니다.. 예상한 동작을 바라는게 문제인거죠... a=a++ + a++;과 비슷한거죠..이것의 결과는 컴파일러마다 다르게 나옵니다.. 일단 위의 경우를 보면 printf는 뒤에 인자부터 차례대로 스택에 푸쉬한후 호출됩니다..따라서 마지막 ret주소가 푸쉬되고 ret++가 되고 두번째에서 ret+1이 푸쉬되고 다시 ++이 되어 ret+2가 되어 결과가 거꾸로 나온거라고 보여집니다..

whitelazy의 이미지

끄응.. 저두 잘 모르겠군요 흐음 ;;
1 2 3
1 1 2가 나와야할듯한데 ;;
다른분들 답변 참고해서 연구해 봐야겠네요 ㅠ_ㅠ

hunkim의 이미지

하지만 두번째 printf 문에서는 *ret, *(ret++), *(ret++) 로 ret 값을 변화시키면서 출력했습니다.

그랬더니 결과가 3 2 1로 뒤집어져서 나왔습니다.

컴파일러에 따라 다르지만 대부분 맨 뒤에 있는 변수 부터 평가 합니다.
즉 *(ret++) 이 평가 되고 그다음 *(ret++) 그리고 *ret 순으로 평가 됩니다.

그래서 예제의 코드는 다음코드와 동등합니다.

a: printf("%d\n", *(ret++)); 
b: printf("%d\n", *(ret++)); 
c: printf("%d\n", *ret);

1
2
3

그런데 원래 소스는 c, b, a 순으로 출력하니
3 2 1

실 프로그램에서는 같은 문장에 같은 변수의 값 자체가 변하는 구문을 함께 사용하지 않는것이 좋습니다. 컴파일러의 방식에 따라 예상하지 못하는 결과가 나오니....

seoleda의 이미지

bugiii wrote:
함수에 인자를 전달하는 순서 (컴파일러마다 다릅니다.)
포인터 증감 연산

두가지를 잘 생각해보시면 답이 나옵니다.

그런데, 위의 코드가 어디에서 나왔나요? 그냥 실험용이라면 몰라도 면접때 저런 코드를 제출한다면 바로 탈락의 지름길이라고 생각합니다만...

허걱. 바로 탈락이요?

저러한 소스 코드는 없었는데요..

예전에 bnetd인가.. 프리베틀넷 소스코드를 보다가, 문자열을 char* 형으로 리턴해서 사용하는 것을 봤습니다.
간단히 예를 들면
char* func(){
static msg[]="aaaa";
return msg;
}
이런 식이라고 기억합니다. 그래서 저 코드를 응용해서 배열을 저런식으로 넘기면 편하겠다는 생각에 나름대로 가끔식 사용하는 방법인데요.. ㅋㅋ

이제까지 별 문제 없이 잘 썼는데..

이런 방식의 코드가 어떠한 점에서 문제가 되는지 알고 싶습니다.

아니면 다른 코드의 다른부분에 문제가 있는 것인가요?

whitelazy의 이미지

글 달고나니까 likesylph님께서 답을 올려주셨군요 그런거군요 아아 심오한 언어의 세계 ;;

dudungsil의 이미지

Quote:

이제까지 별 문제 없이 잘 썼는데..

이런 방식의 코드가 어떠한 점에서 문제가 되는지 알고 싶습니다.

처음 질문에서 사용하신 코드 printf (... *(ret++), *(ret++), ..)는 결과를 예측하기 어렵기 때문입니다. 코드를 보고 결과를 바로 예측할수 없다는건 에러를 포함하고 있을 확률이 높고 수정또한 어렵습니다.

필요에 의해서 저 코드를 작성하셨다고 하죠. 두어달 지난 후에 다시 코드를 봤을때 저 코드의 역할과 결과값을 바로 아실수 있겠어요? 그래서 좋지 않은 코드라는 겁니다.

아래쪽에 질문하신

char* func () {
  static str[] = "abcd" ;
  return str ;
}

은 실제로 많이 쓰이는 형식입니다. 여기에는 특별한 문제가 없어 보이네요.

무엇을 위한 코드냐에 따라 차이는 있겠지만 대부분의 경우 가장 좋은 코드는 의도와 결과가 명확한 코드입니다. 협업중이라면 그 의미가 더 커지겠지요.

산넘어 산

bugslife의 이미지

두번째 printf문의 인자 세개를 각각의 print문에 넣어서 출력하니 값이 또 변하는군요.

printf("%d ", *ret);
printf("%d ", *(ret++));
printf("%d ", *(ret++));

1 1 2로...

그리고..
printf("%d %d %d\n", *ret, *(ret++), *(ret++));

바로 아래에..
printf("%d %d %d\n", *ret, *(ret--), *(ret--)); 를 넣고 실행하니...

1 2 3이...

결국은.. 두번째 출력시에 값이 변하는 것은 확실한데.. 따로 출력할때는 최소한 *ret의 값은 변하지 않은 걸로 나오는군요.

음... --a

어느순간부터인가 하루살이의 하루를 알고싶다.

liongo의 이미지

#include <stdio.h>

#include <stdio.h>

int a() { printf("function A\n"); return 1;}
int b() { printf("function B\n"); return 2;}
int c() { printf("function C\n"); return 3;}

int main()
{
        printf("VALUE %d %d %d\n", a(), b(), c() );
        return 0;
}

해깔리시는분이 계시는가 본데 확인하시길 바랍니다..

' 형식이 내용을 규정한다. '

김충길의 이미지

c calling convention을 사용하는 함수 호출에서 파라메터가 전달될때 마지막
파라메터가 먼저 전달 됩니다. 즉

printf (fmt, A, B, C);

에서 C가 스택에 들어가고 B가 스택에 들어가고 A가 스택에 들어가고 fmt가 스택에
들어간 뒤 call printf 하게 됩니다.

따라서 파라메터를 push 할때 각 A, B, C가 expression이라면 계산역시
C -> B -> A 순으로 계산이 이루어 집니다. 그럼 원래 사용한 코드에서 보면
*(ret++) -> *(ret++) -> *ret 순으로 계산되니 3, 2, 1 로 출력되는게 맞습니다.

앞에 분들이 대답한것 처럼 저것에 호출해서 사용하는건 좋은 방법이 아닙니다.

screen + vim + ctags 좋아요~

seoleda의 이미지

전 처음에 static로 선언한 부분과 가변인자 전달방식에서 어떠한 식으로 교묘하게 작용하여 저러한 문제가 생겼으리라 생각했는데 이유는 다른 곳에 있었군요.

func(a(), b()); 이런 구문 있을때, a()가 수행되고 난 후에 b()가 수행될 것이라고 생각하는데 사실은 b()가 먼저 수행된다는 것 것때문에 일어난 결과 였군요.

그럼 한가지 더 궁금한 점이 생기는데요, 씨언어 에서는 위와 같은 경우에 항상 b()가 먼저 수행되는 것인지, 아님 컴파일러, 혹은 머신마다 다르게 수행되는 경우가 있는건지 궁금합니다.

항상 b()가 먼저 수행된다면,저런 특성을 잘 이용해서 프로그램을 쉽게 작성할 수도 있을 거란 막연한 생각이 듭니다. (재귀호출과 적절히 결합해서.. )

마지막으로 이 질문에 관심을 가지고 친절히 답해주신 KLDP 여러분께 감사 드립니다. ^^

eungkyu의 이미지

seoleda wrote:
전 처음에 static로 선언한 부분과 가변인자 전달방식에서 어떠한 식으로 교묘하게 작용하여 저러한 문제가 생겼으리라 생각했는데 이유는 다른 곳에 있었군요.

func(a(), b()); 이런 구문 있을때, a()가 수행되고 난 후에 b()가 수행될 것이라고 생각하는데 사실은 b()가 먼저 수행된다는 것 것때문에 일어난 결과 였군요.

그럼 한가지 더 궁금한 점이 생기는데요, 씨언어 에서는 위와 같은 경우에 항상 b()가 먼저 수행되는 것인지, 아님 컴파일러, 혹은 머신마다 다르게 수행되는 경우가 있는건지 궁금합니다.

항상 b()가 먼저 수행된다면,저런 특성을 잘 이용해서 프로그램을 쉽게 작성할 수도 있을 거란 막연한 생각이 듭니다. (재귀호출과 적절히 결합해서.. )

마지막으로 이 질문에 관심을 가지고 친절히 답해주신 KLDP 여러분께 감사 드립니다. ^^

어느것부터 실행할 지는 컴파일러에 따라 달려 있다고 알고 있습니다. 그러므로, 인자의 평가 순서에 따라 의미가 달리자는 프로그램은 짜지 말아야 합니다.

버려진의 이미지

추천 사항은 아닙니다 :( 착각의 가능성이 다분하기 때문에.. 지금은 확실히 알고 있어도 나중에 다시 봤을때 제대로 기억하고 있으리란 보장은 없죠.

위에 같은 경우엔 항상 b()가 먼저 호출됩니다. 스택의 특성 때문입니다. 스택이 LIFO(FILO)방식이지 않습니까? b()를 스택에 넣고, a()를 스택에 넣고, func()호출, 팝, 팝 하면 a()값, b()값이 나오죠.

간단한 프로그램을 작성한 후에 어셈으로 바꿔보시면 확실히 보이실겁니다.

좀 다른 주제의 아티클이지만 http://khdp.org/docs/common_doc/shellcode만들기_0912.txt 이 문서를 보시면 도움이 되실듯 합니다.

corba의 이미지

gcc로 하면

1 2 3
3 2 1

vc60의 cl로 하면

1 2 3
1 1 1

이 나오네요 @_@
이것이 맞는것 같기도 하고... _ _;

1 2 3
1 1 2

가 나와야 한다는 생각도 맞는거 같고...

헷갈리는군요.... 볼랜드C++로 해봐야 겠습니다.

1 2 3
3 2 1

이 맞나봅니다. 볼랜드C++도 그렇게 나오네요.

버려진의 이미지

corba wrote:
gcc로 하면

1 2 3
3 2 1

vc60의 cl로 하면

1 2 3
1 1 1

이 나오네요 @_@
이것이 맞는것 같기도 하고... _ _;

1 2 3
1 1 2

가 나와야 한다는 생각도 맞는거 같고...

헷갈리는군요.... 볼랜드C++로 해봐야 겠습니다.

1 2 3
3 2 1

이 맞나봅니다. 볼랜드C++도 그렇게 나오네요.

위쪽에 likesylph님께서 답변하신 내용을 보세요. 컴파일러 마음대로 나오는 것이 맞습니다.

pynoos의 이미지

http://pynoos.x-y.net/exam/

어줍잖은 제 문제사이트의 5번에 같은 내용이 있어서 올려봅니다.

sharefeel의 이미지

3 2 1 순서로 나오는 컴파일러가 가장 흔하지 않은지요?
제가 경험했던 컴파일러는 다 저렇게 나왔던 것 같습니다만..

===============
Vas Rel Por

M.W.Park의 이미지

K&R에 보면 이것과 비슷한(?) 코드를 써놓고 쓰지 말아야 될 코드라고 이야기했던 것같습니다.

옛 기억으론, calling convention을 PASCAL로 강제 해서 쓰는 경우도 있긴 하지만(left to right의 순서), 별로 추천하고 싶은 방법은 아니군요.

-----
오늘 의 취미는 끝없는, 끝없는 인내다. 1973 法頂

pynoos의 이미지

pynoos wrote:
http://pynoos.x-y.net/exam/

어줍잖은 제 문제사이트의 5번에 같은 내용이 있어서 올려봅니다.

심심해서 한 문제 추가해봤습니다. 다녀가신 분들 감사드리며...

그냥 정말 따분할때마다 하나씩 추가되므로... 자주 추가는 너무 기대 마세요. :)

http://pynoos.x-y.net/exam/

ㅡ,.ㅡ;;의 이미지

pynoos wrote:
pynoos wrote:
http://pynoos.x-y.net/exam/

어줍잖은 제 문제사이트의 5번에 같은 내용이 있어서 올려봅니다.

심심해서 한 문제 추가해봤습니다. 다녀가신 분들 감사드리며...

그냥 정말 따분할때마다 하나씩 추가되므로... 자주 추가는 너무 기대 마세요. :)

http://pynoos.x-y.net/exam/

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