void pointer 관련 해서 다시 질문드립니다.

mindon의 이미지

아래 코드에서 질문을 드립니다.


typedef struct {
    char    x;
    short   y;
    int     z;
} TEST;


int main(void)
{

    TEST   ppp;
    void    *ptr = NULL;
    
    ppp.x = 2;
    ppp.y = 10;
    ppp.z = 99;

    ptr = &ppp;
    printf("ptr cont(x) : 0x%01x\n",  *((char*)ptr));

    ptr += 1;
    printf("ptr cont(y) : 0x%02x\n",  *((short *)ptr));

    ptr += 2;
    printf("ptr cont(z) : 0x%04x\n",  *((int*)ptr));
}


이것을 실행 해 보면 두번째 short type인 y를 찍을때 코아덤프가 납니다.
환경은 솔라리스 2.8이구 gcc 버젼은 3.3.2 입니다.

위의 코드가 제 환경에서는 컴파일이 됩니다.
그런데 vc++6.0에서는 error가 나더군요.
error가 나는 부분은 ptr을 증가시키는 부분입니다.

(char *) ptr += 1; 과 (short *) ptr +=2; 하면 vc에서 error가 나지 않고 죽지도 않습니다.

위의 코드에서 void 포인터인 ptr을 증가시키면 더한 byte 수 만큼만 pointer가 증가하는 것은 확인했습니다.

물론 위와 같이 하면 좋겠지만, 제가 지금 만들고 있는 프로그램에서는 이렇게 하지 못하는 구조라 그렇습니다.

제가 원하는 것은 구조체 안에 여러변수들이 있을때, 구조체 중간의 어떤 변수의 포인터를 알기위해 구조체의 base pointer에서 그 변수의 위에 위치하는 변수들의 size만큼 offset값을 더해가면서 pointer를 계산하고,
그 pointer에 가서 그 변수의 type에 따라서 1byte, 2bytes, 4bytes등으로 출력하고자 하는 겁니다.  출력하고자 하는 변수의 type은 알고 있거든요.

위와 같이 했을때 코아 덤프가 나는 원인이 무언지 아시는 분은 좀 답변 부탁드립니다. 그리고 해결책이 있으면 그것도 부탁드리고요...
eminency의 이미지

아마도 포인터 값 계산이 제대로 안되었기 때문인 것 같습니다만...
저런 경우의 에러는 처리 방법이 정해진 것이 없고 컴파일러에 따라 달라서 결과가 다른 것이 아닌가 싶군요.

구조체 안의 변수들은 word boundary에 따라 변수 주소값이 정렬되게 되어 있으므로 컴파일시 pack 관련 옵션을 주지 않았다면 코드 자체가 틀린 셈입니다.

*p.s. : ptr의 각각의 주소값과 &ppp.y, &ppp.z를 프린트해서 비교해보시는게 좋을 듯...

노루가 사냥꾼의 손에서 벗어나는 것 같이, 새가 그물치는 자의 손에서 벗어나는 것 같이 스스로 구원하라 -잠언 6:5

mindon의 이미지

#pragma pack(1)
typedef struct {
char x;
short y;
int z;
} TEST;
#pragma pack(4)

int main(void)
{

TEST ppp;
void *ptr = NULL;

ppp.x = 2;
ppp.y = 10;
ppp.z = 99;

ptr = &ppp;
printf("ptr[%p] &ppp.x[%p]\n", ptr, &ppp.x);
printf("ptr cont(x) : 0x%01x\n", *((char*)ptr));

ptr += 1;
printf("ptr[%p] &ppp.y[%p]\n", ptr, &ppp.y);
printf("ptr cont(y) : 0x%02x\n", *((short *)ptr));

ptr += 2;
printf("ptr[%p] &ppp.z[%p]\n", ptr, &ppp.z);
printf("ptr cont(z) : 0x%04x\n", *((int*)ptr));
}

위와 같이 pack을 했을때도 마찬가지고요, pointer 값이 같은 것은 체크했습니다.

참고로 위의 코드를 실행한 결과입니다.

ptr[ffbef988] &ppp.x[ffbef988]
ptr cont(x) : 0x2
ptr[ffbef989] &ppp.y[ffbef989]
버스 오류(Bus Error) (메모리가 덤프됨)

doldori의 이미지

void* 형의 산술 연산이 정의가 되는지 잘 모르겠습니다.
제 생각에는 다음처럼 하면 될 것 같습니다.

#include <stddef.h>

int main(void) 
{ 

    TEST   ppp; 
    char* head = (char*)&ppp;
    char    *ptr = NULL; 
    
    ppp.x = 2; 
    ppp.y = 10; 
    ppp.z = 99; 

    ptr = head + offsetof(TEST, x); 
    printf("ptr cont(x) : 0x%01x\n",  *(char*)ptr); 

    ptr = head + offsetof(TEST, y); 
    printf("ptr cont(y) : 0x%02x\n",  *(short *)ptr);

    ptr = head + offsetof(TEST, z); 
    printf("ptr cont(z) : 0x%04x\n",  *(int*)ptr); 
}

제가 C++에 익숙해져 있기 때문인지는 몰라도 이 코드처럼 포인터를
이리저리 캐스팅하는 건 무척 꺼려집니다. 깔끔한 방법을 쓸 수 없다는
구조가 어떤 것인지 궁금하군요.

ssehoony의 이미지

void* 가 가르키는 타입에 사이즈가 없기 때문에 void* 는 산술 연산을 할 수 없습니다.

산술연산을 하고 싶으시다면 doldori님의 설명 처럼 하시는 것이 좋습니다.

gcc 에 -Wpointer-arith 이 옵션을 주시면 컴파일때 경고 문구를 보실 수 있을 겁니다.

liebeym의 이미지

글올신 분이 부득이 이렇게 포인터 참조로 접근을 하신다고 하니 이렇게라도 해보심이 어떨지 하고 올리지만.. 구조체의 패딩문제나 구조체의 메모리가 반드시 연속적이다는 보장은 없으므로 그런식의 포인터 접근은 위험합니다.
올리신 것을 살짝 변형해서 올렸습니다.

     20           ptr = &ppp;
     21         printf("ptr cont(x) : %d\n",  *((char*)ptr)); 
     22         printf("ppp.x = %p , ptr = %p\n",&ppp.x,(void*)ptr);
     23         ptr=(short*)ptr+1;
                  /* 구조체가 총 8  byte이므로 1byte패딩이 됐습니다. 그리고 
                       char 쪽에 1byte가 패딩이되어 기존의 (char*)해도 엉뚱한 곳을
                       가르킵니다.  그래서 (short*)만큼 포인터 연산을 해줘서 값을 얻어냈                          
                      습니다.*/
     24           
     25         printf("ptr cont(y) : %d\n",  *((short *)ptr));
     26         printf("ppp.x = %p , ptr = %p\n",&ppp.y,(void*)ptr);
     27
     28         ptr=(short*)ptr+1;
     29         printf("ptr cont(z) : %d\n",  *((int*)ptr));
     30         printf("ppp.x = %p , ptr = %p\n",&ppp.z,(void*)ptr);

코드에서도 이렇듯 구조체에 포인터 연산을 한다는것은 프로그래머가 구조체 패딩또는 그이상의 메모리 구조를 알고 연산을 해야한다는 것입니다.
간단한 구조체여서 이런것이 가능하지만 만약 좀더 복잡한 구조체일 경우 프로그래머에겐 많은 책임이 뒤따릅니다.

AMule--->A si bal Mule

lsj0713의 이미지

위에 올라온 글들을 살펴보던 도중에 의문이 생겨서 질문을 올립니다.

((char *)ptr) += 1;

캐스트 연산자의 결과값은 lvalue가 아니므로 대입 연산자의 왼쪽에 올 수 없을텐데 어째서 Visual C++, gcc 모두 아무런 에러나 경고 메시지도 표시하지 않는 것입니까? 설마 위의 수식이 올바른 수식입니까? 물론 에러나 경고 메시지가 나오지 않는다고 해서 올바른 수식인 것은 아니지만 너무나도 기본적인 것이라서 잘 이해가 안갑니다. 또한

1 += 1;

위의 경우에는 좌항이 lvaule가 아니라고 분명히 에러 메시지를 출력해 주는데 말입니다.

ixevexi의 이미지

warning 띄울텐데요..

이 예제를 컴파일 해보진 않았지만,
전에 비슷한 문제로 제가 질문을 한적이 있습니다.

GCC extension으로 워닝수준으로 그친다고 합니다.
또 C++은 옳은 표현이라고 합니다.

http://bbs.kldp.org/viewtopic.php?p=176714

C++, 그리고 C++....
죽어도 C++

doldori의 이미지

ixevexi wrote:
warning 띄울텐데요..

이 예제를 컴파일 해보진 않았지만,
전에 비슷한 문제로 제가 질문을 한적이 있습니다.

GCC extension으로 워닝수준으로 그친다고 합니다.
또 C++은 옳은 표현이라고 합니다.

http://bbs.kldp.org/viewtopic.php?p=176714

+= 연산자의 좌변은 modifiable lvalue이어야 하는데
(char*)ptr은 그렇지 못하므로 에러입니다.

댓글 달기

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