[완료] unsigned char * 주소에서 integer로 값 읽기

sosuri78의 이미지

byte stream에서 interger 로 값을 읽는 경우에 byte 포인터 주소가 4의 배수가 아닌 경우에는 PC에서는 정상 동작하지만 ARM이나 다른 프로세서에서는 원하는 대로 동작하지 않습니다.

예제 코드

int main(void)
{
    unsigned char *buf;
    unsigned int val;
    int i;
 
    buf = (unsigned char *)malloc(4096);
 
    for (i=0;i<20;i++)
        *(buf + i) = i+1;
 
    for (i=0;i<7;i++) {
        val = *(unsigned int *)(buf + i);
        printf("val[%d] = %#x\n", i, val);
    }
    free(buf);
    return 0;
}

PC 에서 결과
val[0] = 0x4030201
val[1] = 0x5040302
val[2] = 0x6050403
val[3] = 0x7060504
val[4] = 0x8070605
val[5] = 0x9080706
val[6] = 0xa090807

ARM 에서 결과

val[0] = 0x4030201
val[1] = 0x1040302
val[2] = 0x2010403
val[3] = 0x3020104
val[4] = 0x8070605
val[5] = 0x5080706
val[6] = 0x6050807

그래서 ARM과 PC에서 모두 정상 동작하도록 아래같이 수정 했는데 더 좋은 방법이 없을까요?

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
 
inline int get_4(unsigned char *addr)
{
    int val;
    unsigned long remainder;
 
    remainder = (unsigned long )addr % 4;
 
    switch(remainder) {
        case 0:
            val = *(int *)addr;
            break;
        case 1:
            val = *(int *)(addr-1);
            val >>= 8;
            val +=  *(addr + 3) << 24;
            break;
        case 2:
            val = *(int *)(addr-2);
            val >>= 16;
            val += *(unsigned short int *)(addr + 2) << 16;
            break;
        case 3:
            val = *(int *)(addr-3);
            val >>= 24;
            val += *(unsigned int *)(addr + 1) << 8;
            break;
    }
    return  val;
}
 
int main(void)
{
    unsigned char *buf;
    unsigned int val;
    int i;
 
    buf = (unsigned char *)malloc(4096);
 
    for (i=0;i<20;i++)
        *(buf + i) = i+1;
 
    for (i=0;i<7;i++) {
        val = get_4(buf+i);
        //val = *(unsigned int *)(buf + i);
        printf("val[%d] = %#x\n", i, val);
    }
    free(buf);
    return 0;
}
jick의 이미지

int i;
char *p;
 
memcpy(&i, p, sizeof(int));

이렇게 하시면 가장 짧습니다.

sosuri78의 이미지

감사합니다.

익명 사용자의 이미지

ARM을 안써봐서 살짝 구글링해봤더니 alignment에 맞지 않은 memory access는 자동으로 LSB를 기준으로 rotate해서 읽게 되어 있네요. 신기합니다.
해결책 중의 하나로 gcc 옵션중 -mno-alignment-traps 라는게 보이더군요. 성능상의 문제가 있지 않을까 막연히 추측해봅니다. (되나 안되나 저도 궁금합니다;;;;)

익명 사용자의 이미지

기억이 나진 않지만 alignment맞춰주는 옵션은 있고
HW가 맞춰 주는게 아니고 코드로 맞추기 때문에
내부적으로 꽤 많은량의 코드가 추가됩니다.
가능한 경우라면 data구조를 잘 설계하는게 좋습니다.

익명 사용자의 이미지

sun 이야기였습니다. 저런 경우 bus에러가..
arm은 자동으로 읽나보군요.

sosuri78의 이미지

invalid option `no-alignment-traps' 라고 나오네요^^
gcc version 은 3.4.3 입니다.

컴파일 옵션으로 할수 있는지 찾아봤는데 잘 안보이네요...

현재 문제 되는 코드가 여러군데 있는데, 컴파일 옵션으로 해결되면 좋은데 ㅠㅠ

위와 같은 문제 때문에

struct {
  char val;
  int gg;
} test;

자동으로 compiler 가 val 뒤에 3byte padding 을 하는 것 같네요

coremaker의 이미지

C에서.. Struct로 각 타입을 묶어줄때..
생각했던 size로 묶이지 않는경우가 발생합니다..
이유는 Compiler가 최적화를 해버리는 것입니다...
사용자 의도랑은 상당히 다를 가능성이 있는 부분이죠..

현재 기억이 정확히 나지는 않지만...
메모리를 읽는 방법때문에.. word size 단위로 struct 를 생성하는 것이
읽거나 수정할때 오버헤드가 적어서 Compiler가 중간코드..
최적화 과정에서 저렇게 생성하는 것으로 알고 있습니다...

이걸 해결하는 방법으로는
전처리기에서
#pragma 를 이용하거나..
copiler 옵션중에 padding 관련 옵션을 조정해 주시면 됩니다..

익명 사용자의 이미지

메모리 정렬제한 문제입니다.

컴퓨터 구조상, 4의 배수가 아닌 주소에 접근하기 위해서는
성능상의 손실이 불가피합니다.
32비트 컴퓨터라는 말 자체가 32비트(=4바이트) 단위로 자료를 접근한다는 말이니까요.
CPU에 따라서는 아예 불가능하거나 오동작을 하기도 하며,
x86처럼 되더라도 성능상에서 손해가 발생하는게 보통입니다.

굳이 뚫고 들어갈 방법을 찾지 마시고
CPU의 구조적인 한계를 인정하여 정렬제한을 지키는 쪽으로 해결을 보시는 것이
성능면에서 바람직한 방법입니다.

inline int get_4(unsigned char *addr)
{
int r;    // remainder
int v1, v2;
int *p;
 
r = (unsigned long)addr % 4;
p = (int *)(addr - r);
int v1 = p[0];
int v2 = p[1];
 
if ( r )
{
    v1 >>= r * 1;
    v1 += (v2 << ((4 - r) * 8));
}
return v1;
}

원래 코드도 좋지만 제가 좀 손을 봤습니다. 근데 더 나을지 어떨지는 모르겠습니다. 컴파일러의 최적화라는게 워낙에 알수없는 놈이라서;;;

메모리에서 값을 읽어들일때는 미리 한꺼번에 읽어들이는 쪽이 더 자연스럽지 않을까 합니다. 컴파일러가 최적화하기 나름이겠지만, 여러번에 걸쳐 메모리에 접근하는 것 보다는 한꺼번에 레지스터로 읽어오는 쪽이 더 좋을 테니까요.

sosuri78의 이미지

WinCE에서는 위 와 같이 misalign data access를 하는 경우 에러가 발생합니다.

물론 디버거에서 datatype misalignment 가 체크되었을 경우 입니다.

관련된 링크를 첨부합니다.
http://blog.nworkers.net/201
http://www.troot.co.kr/trbbs/zboard.php?id=newgal&no=2916
http://msdn2.microsoft.com/en-us/library/aa448550.aspx

댓글 달기

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