C++ 에서 offsetof 사용시 warning

B00m의 이미지

struct C
{
    int a;
    int b;

    C ():
        a(100),
        b(200) {}
};

int off = offsetof(C, b);

위와 같은 코드를 g++ 에서 컴파일하게 되면 다음과 같은 warning 이 납니다.

warning: invalid access to non-static data member `main()::C::a' of NULL object
warning: (perhaps the `offsetof' macro was used incorrectly)

struct C 에 생성자를 만들지 않으면 괜찮지만 생성자 A() 를 만들게 되면 non-POD 데이타로 인식하여 warning 을 내는것 같습니다.

그렇다면 위와 같이 offsetof 에서 non-POD 데이타를 사용하는 것은 정의 되지 않은 행동인가요?

그렇다면 추가해서

	C c;
	char* buf = new char[sizeof(C)];
	memcpy(buf, &c, sizeof(C));

	int* pa1 = (int*)(buf + ((char*)&c.a - (char*)&c));
// or
	C* pd = 0;
	int* pa2 = (int*)(buf + (size_t)&pd->a);

	assert(*pa1 == c.a);
	assert(*pa2 == c.a);

위와 같은 코드는 문제가 있는 코드 인가요?

doldori의 이미지

B00m wrote:
struct C 에 생성자를 만들지 않으면 괜찮지만 생성자 A() 를 만들게 되면 non-POD 데이타로 인식하여 warning 을 내는것 같습니다.

그렇다면 위와 같이 offsetof 에서 non-POD 데이타를 사용하는 것은 정의 되지 않은 행동인가요?


표준에 따르면 offsetof는 POD 형에만 써야 한다고 되어 있습니다. non-POD에
쓴 결과가 정의되지 않는지는 명시되지 않았지만 잘못임에는 틀림이 없습니다.

B00m wrote:
그렇다면 추가해서
	C c;
	char* buf = new char[sizeof(C)];
	memcpy(buf, &c, sizeof(C));

	int* pa1 = (int*)(buf + ((char*)&c.a - (char*)&c));
// or
	C* pd = 0;
	int* pa2 = (int*)(buf + (size_t)&pd->a);

	assert(*pa1 == c.a);
	assert(*pa2 == c.a);

위와 같은 코드는 문제가 있는 코드 인가요?


표준을 엄격하게 적용한다면 문제가 있다고 하겠습니다. char* --> int* 변환의
결과에 대해서는 보장을 하지 않기 때문입니다.
또한 널 포인터의 역참조(pd->a) 문제도 있습니다. & 연산자로 멤버의 주소를
취할 때는 pd를 실제로 역참조하지 않는다는 가정을 하신 듯한데, 이 역시 보장할
수 없는 사항입니다.
코드의 의도를 알 수 없어 더 이상의 답변은 드리기 힘들군요.
익명 사용자의 이미지

Quote:
표준을 엄격하게 적용한다면 문제가 있다고 하겠습니다. char* --> int* 변환의
결과에 대해서는 보장을 하지 않기 때문입니다.

결과를 보장 안한다는게 정확히 어떤 뜻인지 모르겠습니다.

Quote:
또한 널 포인터의 역참조(pd->a) 문제도 있습니다. & 연산자로 멤버의 주소를
취할 때는 pd를 실제로 역참조하지 않는다는 가정을 하신 듯한데, 이 역시 보장할
수 없는 사항입니다.

실제로 offsetof 가 다음과 같은 식으로 구현되어 있는데
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
이것은 어떻게 되는 건가요?

Quote:
코드의 의도를 알 수 없어 더 이상의 답변은 드리기 힘들군요.

제가 예제 코드를 넘 짧게 만들었네요 :D
의도는 위와 같은 경우에 assert 가 항상 성공 하리라는것을 보증 할수 있냐는 것입니다. 위의 설명대로라면 문제가 있다는 말씀이신데 그렇다면 assert 가 실패할수도 있다는 말씀이신가요?
B00m의 이미지

아구~ 로긴을 안하고 썼네요.

위글 쓴사람은 질문한 사람입니다.

doldori 님의 조언 부탁 드리겠습니다.

doldori의 이미지

Anonymous wrote:
결과를 보장 안한다는게 정확히 어떤 뜻인지 모르겠습니다.

말 그대로 어떤 결과를 보일지 표준은 명시하지 않으며 각 구현체에서 알아서 하라는 뜻입니다.
Quote:
5.2.10/7
A pointer to an object can be explicitly converted to a pointer to an object
of different type. [...] the result of such a pointer conversion is unspecified.

Anonymous wrote:
실제로 offsetof 가 다음과 같은 식으로 구현되어 있는데
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
이것은 어떻게 되는 건가요?

이것은 stddef.h에 있는 코드일 것입니다. 즉 특정 구현체가 제공하는 것입니다.
이 코드의 작성자는 이 코드가 어느 환경에서 사용될지, 이 코드가 어떤 결과를
보일지 미리 알고 있으며, 다른 환경에서 쓰일 것(이식성)은 고려하지 않는다는
뜻이지요. 그렇다면 작성자는 자신이 타겟으로 삼는 환경에서만 동작하는 hack을
마음껏 써도 됩니다.
하지만 자신의 코드가 이식성을 갖기 원하는 프로그래머들의 입장은 다릅니다.
우리들은 hack이 아니라 offsetof를 쓰지요. 그러면 어떤 환경에서도 동작할
테니까요. (해당 환경에서 offsetof를 어떻게 구현하는지, 무슨 hack을 쓰는지는
우리가 걱정할 문제는 아니지요.)

Anonymous wrote:
의도는 위와 같은 경우에 assert 가 항상 성공 하리라는것을 보증 할수 있냐는 것입니다. 위의 설명대로라면 문제가 있다는 말씀이신데 그렇다면 assert 가 실패할수도 있다는 말씀이신가요?

네, 그렇습니다. 하지만 이것은 어디까지나 표준을 있는 그대로 해석할 때 그렇다는
것이지 실제로 assert가 실패하는 구현체가 존재한다는 뜻은 아닙니다. (저는
그런 구현체가 존재하는지 어떤지도 모릅니다.) 어찌 보면 무책임한 얘기라고
받아들이실지는 몰라도 implementation-defined, unspecified, undefined
투성이인 표준의 특성상 그럴 수밖에 없다는 점도 이해해 주시기 바랍니다.

ps. 이들 용어의 뜻은 http://bbs.kldp.org/viewtopic.php?p=182642#182642을 참고하십시오.

B00m의 이미지

감사합니다~

portable 한 코드를 짜는게 쉽지는 않군요 :)

neryong의 이미지

warning 은 'warning: invalid access to non-static data member `main()::C::a' of NULL object' 라고 나와있고, 해석하면 'null object 의 non-static 멤버에 접근하려고 한다' 입니다.
컴파일러가 친절하게도 이러한 것을 알려주네요
offsetof 의 정의를 보면

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

명백히 NULL object 의 member 에 접근하도록 되어 있습니다. 물론 실제로는 접근하지 않고 offset 만 계산하므로 동작시에 문제는 없는 거죠.
즉, offsetof 를 위와 같이 사용해도 아무 문제가 없고, 컴파일러는 그 사실을 모르니 warning 을 주는 것입니다.
꼼수로 피해가는 방법은 null object 가 아니게 하면 됩니다.

#define offsetof(TYPE, MEMBER) (((size_t) &((TYPE *)16)->MEMBER)-16)

Warning 도 없고, 실제로도 문제없는 메크로입니다.

댓글 달기

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