C언어 널포인터상수를 함수에 넘길때 문제

gurumong의 이미지

안녕하세요 ^^
공부중 의문점이 생겼습니다

int*형의 포인터를 전달 받는 함수에 널포인터상수인 (void *)0 을 전달할경우 정의되지 않는 행동을 일으킨다고 합니다
같은 널포인터상수인 0은 되면서 왜 (void *)0 은 되지 않는지 의문스럽습니다
물론 함수에서 요구하는 포인터형은 int*이지만 둘다 같은 널포인터상수로써 아무런 문제가 없어 보이는데요

func((void *)0); /* wrong */
func(NULL);      /* 전처리기가 0, 0L이 아닌(void *)0으로 치환할경우에도 wrong!? */

그리고 다른 질문으로
NULL을 전처리기가 0이나 (void *)0이 아닌 long형의 0L으로 치환해주는 임플리멘테이션의 경우
0L의 어떤 이점이 있기에 선택한것인가요?

ㅜ.ㅜ

dcmru의 이미지

노력만이 살길이다.

gurumong의 이미지

알려주신 문서의 Q5.6과 Q5.7에서 제가 질문한 내용이 부분적으로 있기는 한데
제가 원하는 시원한 답변은 아닌거 같습니다 ㅜ.ㅜ

함수에서 int*형의 포인터를 요구하기 때문에 널포인터상수가 안된다고 하는거라면
함수 프로토타입에 근거하여 그냥 숫자 0을 사용할경우 자동적으로 int*형의 포인터로 캐스팅하여 되는것이라 약한 추측은 하는데 같은 널 포인터 상수인(널포인터상수 정의에 의해서 포인터가 아닌) (void *)0은 왜 안된다고 하는지에 대한 이유가 궁금합니다 ㅜ.ㅜ

그리고 정수보다 큰 크기를 가지는 시스템에서 NULL을 0L으로 정의하는것이 몇가지 에러를 잡는데 도움이 된다는데 아무리 생각을 해보아도 떠오르지 않네요..

ㅜ.ㅜ

blueskya의 이미지

The GNU C Library에 보시면 이러한 문장이 있습니다.

If you use the null pointer constant as a function argument, then for complete portability you should make sure that the function has a prototype declaration. Otherwise, if the target machine has two different pointer representations, the compiler won't know which representation to use for that argument. You can avoid the problem by explicitly casting the constant to the proper pointer type, but we recommend instead adding a prototype for the function you are calling.

-----------------------------
인생 뭐있어?

----------------------------------------------------------------------
인생 뭐있어? 백수로 사는거야~ 가는거야~

익명사용자의 이미지

널 포인터 상수를 함수의 인자로서 사용할때는, 완벽한 이식성을 위해 꼭 함수 원형 선언을 사용하는게 좋다.

함수 원형 선언의 예
int func(int i, char c, float f);

만약 해당 컴퓨터가 포인터의 타입에 따라 서로 다른 내부 표현법을 가질 경우, 컴파일러는 해당 함수인자를 위해 어떤 내부 표현법을 사용해야 될지 알지 못한다. (내부적으로 포인터 변수를 저장할 때 어떤 방식으로 저장해야 할 지 알 수 없다)

널 포인터 상수에 명시적으로 형변환을 해서 이 문제를 피할 수 있지만, 함수 원형을 제대로 적어주는 쪽이 더 나은 방식이다.

func((float *)NULL); // 명시적으로 형변환을 해준다.

gurumong의 이미지

평소에 영어 공부를 하지 않은게 문제네요 ㅜ.ㅜ
얼마전부터 회화 위주로 시작하긴 했는데
아직 저런 문장을 해석하기엔 어렵네요 ㅜ.ㅜ

doldori의 이미지

함수 원형을 선언하지 않았을 때만 문제가 됩니다.
int*와 void*의 내부 표현은 다를 수 있는데, 함수 원형을 선언했을 때는
void* --> int*로 적절히 변환되지만 그렇지 않으면 그러한 변환이 필요한지
컴파일러가 알 수 없기 때문입니다. 비슷한 예로 다음과 같은 것이 있습니다.

#include <stdio.h>
 
void f();
 
int main(void)
{
    f(1);
    return 0;
}
 
void f(double d)
{
    printf("%f\n", d); /* undefined */
}

gurumong의 이미지

함수를 호출할때에 매개변수로 널포인터상수를 넘겨줄수 없나요?

int *형의 널 포인터를 매개변수로 갖는 함수에
널포인터상수인 0 을 전달하면 함수원형이 제공하는 정보에 의해서 int *형의 포인터로 변환되어서 전달된다고 하고
널포인터상수인 (void *)0; 을 전달하면 void *형의 포인터가 전달되어 정의되지 않은 행동을 한다고 하고
위의 두 경우를 보면 널포인터상수 그자체가 아닌 포인터로써 해석되어 전달되는거 같은데

익명사용자의 이미지

위에서 언급된 함수 원형 선언이 없는 경우가 아니라면, 0L이나 (void *)0이나 똑같고, 둘 다 맞습니다.

일단 둘 다 널 포인터 상수로 사용할 수 있으며, void * 형으로부터 int * 형으로의 변환은 허용됩니다. 다만 참조시에 가리키고 있는 주소가 어디냐에 따라 문제가 생길 수 있습니다. 함수 원형 선언만 제대로 되어 있으면 문제가 생길 여지는 없습니다.

NULL을 ((void *)0)으로 정의하는 것과 0L로 정의하는 것의 차이는 데이터 타입의 차이입니다. 둘 다 널 포인터 상수로 사용할 수 있기에 올바로 사용할 경우에는 차이가 없지만, 잘못 사용할 경우에는 컴파일러가 받아들이는 방식에서 차이가 있을 수 있습니다.

float f = 0L; // OK!
double d = ((void *)0); // Error!

전웅의 이미지

제 책을 보고 계신 것으로 알고 있습니다. (아닌가요? ;-) 제가

"A인 경우 B입니다"

라고 말씀드리는 경우, 이를 "B입니다"만 취해서 이해하셔서는 안 됩니다.

설명이 지저분하다는 소리까지 들어가며 가능한 오해를 줄이기 위해
구구절절 수식어와 한정어를 달아놓은 책입니다. --;

> int*형의 포인터를 전달 받는 함수에 널포인터상수인 (void *)0 을 전달할경우 정의되지 않는 행동을 일으킨다고 합니다

그렇지 않습니다. 컴파일러가 null pointer constant 가 어떤 type 으로
변환되는지 분명히 알 수 있는 문맥이라면 전혀 문제가 되지 않습니다.
문제는 컴파일러가 어떤 type 의 null pointer 로 변환해야 하는지 도저히
알 수 없는 경우입니다. 예를 들어,

int func(struct type_info *, ...);
func(&foo, NULL);   /* ??? */
 
int func();
func(1, 3.14, 0);   /* 마지막 0 이 null pointer constant? 정수? */

이 두 문맥이 제가 null pointer constant 를 특정 type 으로 명시적으로
캐스트해야 할 필요가 있다고 말씀드렸던 경우에 해당합니다. 제 기억으론
분명 이와 같은 내용도 책에서 다루고 있습니다.

> 같은 널포인터상수인 0은 되면서 왜 (void *)0 은 되지 않는지 의문스럽습니다

??? 대체 어디에 그런 내용이 있나요???

> 물론 함수에서 요구하는 포인터형은 int*이지만 둘다 같은 널포인터상수로써 아무런 문제가 없어 보이는데요
>
> func((void *)0); /* wrong */
> func(NULL); /* 전처리기가 0, 0L이 아닌(void *)0으로 치환할경우에도 wrong!? */
>

이런 내용 또한 대체 어디에 있는 것인지요???

> 그리고 다른 질문으로
> NULL을 전처리기가 0이나 (void *)0이 아닌 long형의 0L으로 치환해주는 임플리멘테이션의 경우
> 0L의 어떤 이점이 있기에 선택한것인가요?
>

이 내용도 제 책에서 다뤘던 것으로 기억합니다. 올바른 프로그램에게
이점을 주기 위함이 아니라 올바르지 않은 프로그램이 오작동하는 것을
막았으면 하는 바람으로 0L 로 치환해 주는 것입니다. 책에 보다 자세한
내용이 나와 있습니다.

[...]
>
> 함수를 호출할때에 매개변수로 널포인터상수를 넘겨줄수 없나요?
>

그럴리가요? --;

> int *형의 널 포인터를 매개변수로 갖는 함수에
> 널포인터상수인 0 을 전달하면 함수원형이 제공하는 정보에 의해서 int *형의 포인터로 변환되어서 전달된다고 하고
> 널포인터상수인 (void *)0; 을 전달하면 void *형의 포인터가 전달되어 정의되지 않은 행동을 한다고 하고

이런 내용 역시 어디에 나오는지요???

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

gurumong의 이미지

책에 그대로 있는 내용이 아니라 제가 이해하고 그대로 그걸 질문으로 옮긴것이라;;
항상 그렇지만, 명쾌한 답변을 듣고 나면 그때 왜 그렇게 우둔하게 잘못된 생각으로 이해하고 있었을까 그런 --;;

댓글 달기

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