저의 간단한 테스트 코드좀 봐주세요 (SUN OS에서 실행하면 문제생기고, X86에서 실행하면 문제없음)

이형영의 이미지

고수님들 좀 도와주시겠습니까?

문제설명: Sun Computer 에 Unix(SunOS)가 깔려있는데서 아 래 code를 실행하면 세그먼트 에러가 납니다.
리눅스(X86)에서 컴파일하고 실행하면 문제가 전혀없습니다.
왜 그런가요?

#include <stdio.h>

typedef struct aaa
{
char a;
char b;
char c;
char d;
char dummy[4];
double e;
}AAA;

int main()
{
char *p = (char *)malloc(sizeof(AAA));

AAA *aa = (AAA *)&(p[4]);
aa->a = 1;
aa->b = 2;
aa->c = 3;
aa->d = 4;
printf("here is OK\n");
aa->e = 5; // 여기서 버스에러(세그먼트에러)
printf("Problem Here\n");
free(p);
}
:)

ero의 이미지

잘은 모르겠지만
리눅스에서 잘 돌아간다고 하셨는데
아닌거 같네여...

구조체가 char와 double로 구성되어있는데
malloc할당할때 double형까지 char형으로 할당되서
그런거 같네요...

Not sure~ 입니다..^^:

체스맨의 이미지

두가지 이유를 들 수 있는데요.

첫째 할당은 sizeof(AAA) 만큼했는데 처음에 &p[4] 주소를 취했기 때문에, 실제 사용할 수 있는 공간은 sizeof(AAA)-4 이므로, 마지막 멤버 e 에 대입할 때 오류가 나는 건 당연하지요. 이게 근본적인 문제이구요.

둘째 &p[4] 를 취한 이유로 aa->e=5 대입에서 e 의 옵셋이 sizeof(double) 주소로 정렬되어있지 않습니다.

x86 에서는 두번째 이유(주소정렬)로 죽지는 않습니다. malloc 이 지정된 크기보다 조금 더 할당할 수도 있기 때문에 분명히 잘못된 코드지만 시스템에 따라서 첫번째 이유로 죽지 않을 수 있습니다. 첫번째가 근본 원인이지만 스팍에서 죽은것은 두번째 이유일 수 있습니다.

* 덧붙여 섯불리 위와 같이 옵셋을 취하지 마세요. 전혀 호환성이 없어집니다.

Orion Project : http://orionids.org

익명 사용자의 이미지

많은 프로그래머들이 포인터를 이용한 핵(hack)에 지나치게 집착하는 경향이 있는 것 같습니다.

메모리 문제에 대해서는 엄밀한 제한조건들을 지키면서 프로그래밍 하는 편이 좋습니다. 몇가지 생각나는 주의사항을 적어보겠습니다.

* 구조체를 포함한, 모든 객체의 시작주소로는 이미 생성된 객체의 주소만을 취하거나 malloc에 의해 반환된 주소값만을 취한다. 프로그래머가 멋대로 계산해서는 안된다.
* 구조체 내부의 맴버변수 사이의 빈공간과, 구조체의 크기를 멋대로 추측하지 않는다. 보장된 것은, 맴버변수의 배치 순서가 선언된 순서 그대로라는 것과, 맨 처음의 맴버변수 앞에는 빈공간이 없다는 것 뿐이다.
* 포인터 변수의 형변환은, void* 혹은 char*형으로부터의, 형으로의 변환을 제외하고는 늘 피한다. 모든 타입들은 서로 다른 정렬제한 크기를 가질 수 있다. 특정 메모리 주소에 int형으로 접근이 가능해도 double형으로도 가능하다는 보장은 없다.
* 늘 미리 할당되지 않은 공간에 접근하지 않도록 주의한다.

익명 사용자의 이미지

Anonymous wrote:
많은 프로그래머들이 포인터를 이용한 핵(hack)에 지나치게 집착하는 경향이 있는 것 같습니다.

100% 동의 합니다.
포인터만 아니라 쓸데없는 데까지 핵을 사용하는 것이 프로그램 잘하는 것이라고 오해하는 것 이 안타깝습니다. 그리고 그런 핵을 배우기 위해서 시간을 낭비하는 것이 안타깝습니다.

간단 명료하게 작성하는 것이 훨씬 잘하는 것임을 많은 분들이 퍼트려 주셨으면 좋겠습니다.

이형영의 이미지

제가 메모리 할당할때 + 4 만큼 더 할당한다는 것을 깜빡했습니다.
따라서 이 문제는 메모리 할당 문제가 아닙니다.
제가 테스트 해보니.. 메모리를 200Byte정도로 크게 할당해도 똑같은 문제가 발생합니다.

#include <stdio.h>

typedef struct aaa
{
char a;
char b;
char c;
char d;
char dummy[4];
double e;
}AAA;

int main()
{
char *p = (char *)malloc(sizeof(AAA) + 4);

AAA *aa = (AAA *)&(p[4]);
aa->a = 1;
aa->b = 2;
aa->c = 3;
aa->d = 4;
printf("here is OK\n");
aa->e = 5; // 여기서 버스에러(세그먼트에러)
printf("Problem Here\n");
free(p);
}

체스맨의 이미지

제 답변을 끝까지 다 읽으셨는지요?

Quote:

둘째 &p[4] 를 취한 이유로 aa->e=5 대입에서 e 의 옵셋이 sizeof(double) 주소로 정렬되어있지 않습니다.

이 이유로 스팍에서 죽었을 가능성이 높다고 말씀드렸거든요...

Orion Project : http://orionids.org

익명 사용자의 이미지

답변 정말 감사드립니다.

님의 답변 : &p[4] 를 취한 이유로 aa->e=5 대입에서 e 의 옵셋이 sizeof(double) 주소로 정렬되어있지 않습니다.

체스맨님이 이렇게 답변해주셨는데, 그럼 Linux는 걍 자동으로 어떤 처리를 해줘서 이런 문제가 안생기고, sparc에서만 이런게 생기는지.. 왜 그런가요?
제가 몰라서 그러니 이해해주세요.. ^^

그리고 제 테스트 코드처럼 사용하면 안되겠지만, 사정상 저렇게 4바이트를 건너뛰고 사용해야하거든요..

저 4바이트에 어떤 정보를 저장하는 식으로 원천소스가 되어있어서 수정하기는힘든 상황입니다.

제가 SUNOS에서 컴파일 할때 지식이 짧아서 무턱대고 gcc로 컴파일 해서 이런 문제가 생긴거 같구요~ SUNOS에 더 적합하다는 CC로 컴파일 하면 이 테스트 코드는 잘 동작합니다.

cc 하고 CC 이렇게 두종류가 있던데. cc 는 주석 처리 // 을 인식을 못하고 에러처리를 해서 c++컴파일러인 CC를 사용하게 되었습니다. 그런데 ㅜ.ㅜ

자꾸 질문올려서 죄송한데요~
문제는 CC로 컴파일하면 별의 별것(gcc같으면 warning 정도로 처리될것)을 다 에러로 처리해버려서, 에러나는 것을 다 고칠수도 없는 상황입니다.
예를 들면, 한 파일에서 마지막에 엔터 안넣어줬다구 에러나고, 캐스팅 안되있다고 에러나고.. ㅜ.ㅜ

그럼..혹시 CC로 컴파일 할때도 어떤 컴파일 레벨같은거 설정해서, gcc처럼 똑똑(?)하게 컴파일 시킬수 있나요?

간단한것들은 error 안나고 그냥 넘어가게요~

:oops:

ixevexi의 이미지

이건 리눅스의 문제가 아니라

정렬제한이라는 하드웨어플랫폼에 관련된 문제인거 같습니다.

전웅님의 책을 한번 읽어보세요
아니면 align으로 검색하셔도 원하는 걸 얻으실수 있을 듯 합니다.

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

체스맨의 이미지

Anonymous wrote:

체스맨님이 이렇게 답변해주셨는데, 그럼 Linux는 걍 자동으로 어떤 처리를 해줘서 이런 문제가 안생기고, sparc에서만 이런게 생기는지..

이것도 이미 첫 답변에서 다음과 같이 말씀 드렸습니다. 잘 안 읽으셔서 그렇지요...

Quote:

x86 에서는 두번째 이유(주소정렬)로 죽지는 않습니다.

Quote:

그리고 제 테스트 코드처럼 사용하면 안되겠지만, 사정상 저렇게 4바이트를 건너뛰고 사용해야하거든요..

꼭 건너뛰어야 한다면 지원되는 모든 타입의 크기의 최소 공배수만큼 건너뛰세요. 예상컨데 지금 사용하시는 스팍 머쉰은 그 값이 8이어도 충분할 것 같습니다. 4바이트를 건너뛰어야 한다면, 8바이트를 건너뛰세요.

그리고 컴파일 옵션은 메뉴얼 페이지를 잘 찾아보시면 원하시는 게 있을 겁니다.

Orion Project : http://orionids.org

체스맨의 이미지

주소 정렬 문제입니다.
위에서 malloc 에 의해 리턴된 p 는 모든 타입에 대해 정렬된 주소입니다. 그러므로 리턴된 p 값은, ( 포인터에 대해 나머지 연산이 적용되지 않겠지만, 적절히 캐스팅한 것으로 간주하겠습니다. ) 다음도 만족할 겁니다.

Quote:

p%sizeof(double) == 0

그런데 옵셋을 4만큼 취했기 때문에,

(p+4)%sizeof(double)==0 이 아닌경우, 주소 정렬에 의해 버스 에러가 나게 됩니다.

예를 들어 리턴된 주소가 16 번지라면 4만큼 옵셋하면 20번지가 되고, 거기에 8바이트옵셋되어있는 double 멤버에 값을 대입하면, 28번지 ( 28%8!=0 )에 8바이트 값을 쓰게되어 주소 정렬되지 않아 버스 에러가 나는 것입니다.

* 답을 달려던 원글이 지워져버렸네요... 제 글은 그냥 두겠습니다.

Orion Project : http://orionids.org

crimsoncream의 이미지

아 제가 써놓고 보니까 잘못보고 쓴 거라서 지웠습니다.
저는 AAA안에 있는 dummy를 공백으로 두는 거로 보고
p의 캐스팅도 당연히 &((AAA *)p)[4]를 의도한거겠지 하고 쓰고보니 그냥 첫 4바이트를 띄우는 거라는 걸알고.. 지웠습니다 :)

오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

lovewar의 이미지

Anonymous wrote:
다.

제가 SUNOS에서 컴파일 할때 지식이 짧아서 무턱대고 gcc로 컴파일 해서 이런 문제가 생긴거 같구요~ SUNOS에 더 적합하다는 CC로 컴파일 하면 이 테스트 코드는 잘 동작합니다.

혹시 각 컴파일러(gcc, cc, CC)을 적용했을때의 struct의 메모리 크기를 알수 있을까요?

같을지 다를지 궁금하군요.

mastercho의 이미지

lovewar wrote:
Anonymous wrote:
다.

제가 SUNOS에서 컴파일 할때 지식이 짧아서 무턱대고 gcc로 컴파일 해서 이런 문제가 생긴거 같구요~ SUNOS에 더 적합하다는 CC로 컴파일 하면 이 테스트 코드는 잘 동작합니다.

혹시 각 컴파일러(gcc, cc, CC)을 적용했을때의 struct의 메모리 크기를 알수 있을까요?

같을지 다를지 궁금하군요.

sizeof 에다 struct명을 넣어주면 크기가 나옵니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

lovewar의 이미지

mastercho wrote:
lovewar wrote:
Anonymous wrote:
다.

제가 SUNOS에서 컴파일 할때 지식이 짧아서 무턱대고 gcc로 컴파일 해서 이런 문제가 생긴거 같구요~ SUNOS에 더 적합하다는 CC로 컴파일 하면 이 테스트 코드는 잘 동작합니다.

혹시 각 컴파일러(gcc, cc, CC)을 적용했을때의 struct의 메모리 크기를 알수 있을까요?

같을지 다를지 궁금하군요.

sizeof 에다 struct명을 넣어주면 크기가 나옵니다

SPARC 머신이 없는 관계로 물어봤습니다.

크기가 같게 나온면 메모리 접근문제(?)로 보일것이고
다르게 나온다면 또 다른 고민을 해볼 생각이었거든요.

dipole의 이미지

이렇게 한번 해 보시죠.

char *q = (char *)malloc(sizeof(AAA)*2);
char *p = &q[sizeof(AAA)-4];

.
.
.
.
.
free(q);

너는 누구냐?

익명 사용자의 이미지

#pragma packed 을 살펴보심이..

구조체의 메모리 할당은 CPU,Compiler에서 정렬하는
방법에 따라 다르게 할당됩니다.(위에서 언급했듯이)

익명 사용자의 이미지

스트럭쳐 size는 다 16바이트로 같습니다.(리눅스 Sun 동일)

음 답변글을 쭈욱 읽어보니...
스트럭쳐에 double형이 있으면, double형의 변수 주소 % 8 이렇게 난눠떨어져야되는가 보군요..

그러면.. 이걸.. 8바이트 얼라이징 한다..라고 보면 되는거죠? :)

lovewar의 이미지

Anonymous wrote:
스트럭쳐 size는 다 16바이트로 같습니다.(리눅스 Sun 동일)

음 답변글을 쭈욱 읽어보니...
스트럭쳐에 double형이 있으면, double형의 변수 주소 % 8 이렇게 난눠떨어져야되는가 보군요..

그러면.. 이걸.. 8바이트 얼라이징 한다..라고 보면 되는거죠? :)

SUN OS 에서 둘다 크기가 동일하다면
cc와 gcc간의 정렬이 다른것 같습니다.

cc의 경우는 4 byte로 정렬(+4했을때도 이상이 없었기 때문에)한것 같고,
gcc의 경우는 8 byte로 정렬(+4했는데 이상이 발생하여)한 것 같습니다.

이런 경우도 생각해 봅니다.

cc의 경우 8byte로 정렬을 하는데, 문제는 주소 체계가 안맞으니깐,
cc 구현 내부적으로 몇개의 명령어를 더 사용하여 그 형에 맞춰주는 그런 기법을 사용하지 않았을까 하는 엉둥한 추측도 해봅니다.

익명 사용자의 이미지

이형영 wrote:
그리고 제 테스트 코드처럼 사용하면 안되겠지만, 사정상 저렇게 4바이트를 건너뛰고 사용해야하거든요..

저 4바이트에 어떤 정보를 저장하는 식으로 원천소스가 되어있어서 수정하기는힘든 상황입니다.

위에 여러 사람이 이미 설명한 것처럼, 메모리의 정렬 제한 문제로 인해 직접 double형 맴버변수 e에 접근할 수는 없습니다. 이것은 하드웨어의 한계로 인한 문제이기 때문에 컴파일러에서 어떻게 처리할 수는 없는 문제입니다. x86은 정렬제한에서 상당히 자유로운 편이지만, SPARC는 그렇지가 않지요.

해결책은, char*형 포인터로 1byte씩 읽어와서, 정렬제한을 만족하는 double형 객체에 1byte씩 복사한 다음에, 복사본을 읽으면 됩니다. 정렬 제한이 있건 없건, 표준을 지키는 C 컴파일러라면 char형 또는 char*형을 통해 메모리에 1byte씩의 접근이 가능해야 합니다. 하드웨어에서 직접 지원하지 않는다면 shift연산을 통해서라도 구현을 해야 하지요.

원하시는 동작과 일치하는지는 모르겠지만, 다음과 같은 함수를 통해서, 구조체 aaa의 맴버변수 e에서 4byte 빗겨난 공간에서, double형 데이터를, 정렬제한 문제를 겪지 않고 읽어오는 것이 가능합니다.

double read_aaa_e(struct aaa *s)
{
    double d;
    char *to = (char *)&d;
    char *from = (char *)&(s->e) + 4;
    memcpy(to, from, 4);
    return d;
}

하지만, 제가 보기엔 이걸로 과연 끝일까? 하는 생각이 드는군요. 코드가 처음부터 다양한 환경을 가정해서 제대로 짜여진 코드가 아닌 것 같은데, 앞으로도 여러가지 이식성과 관련된 문제들이 튀어나올 것으로 예상되는군요.

댓글 달기

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