[C언어] 구조체 bit fields에 대한 질의입니다.

gafani의 이미지

안녕하세요 대학교 이후로 4년만에 C를 잡아서 공부하는 갓초보입니다.

제목 그대로 비트필드에 대한 질의입니다. 우선 소스 부터 보여드리겠습니다.

----------------------<소스>--------------------------------

#include <stdio.h>
 
#pragma pack( push,1 )
 
struct t{
  unsigned char a:1,
                b:1,
                c:1,
                d:1,
                e:1,
                f:1,
                g:1,
                h:1;
};
 
#pragma pack ( pop )
 
int main(){
 
  struct t test_t ;
  //memset(&test_t,0x00,sizeof(struct t));
 
  test_t.a=1;
  test_t.b=1;
  test_t.c=1;
  test_t.d=1;
  test_t.e=1;
  test_t.f=1;
  test_t.g=1;
  test_t.h=1;
  printf( "%d%d%d%d %d%d%d%d  \n ", test_t.a, test_t.b, test_t.c, test_t.d, test_t.e, test_t.f, test_t.g, test_t.h );
  printf( "%2x\n", test_t );
  printf( "%d\n", sizeof(test_t) );
 
  return 0;
}

결과는

1111 1111
400100ff
1

입니다. memset을 하면 두번째 값이 ff로 정상적으로 나옵니다.

제가 궁금한것은 memset을 하지 않았을 때 400100ff입니다. 패딩 문제가 아닌거 같은데

왜 저렇게 나올까요? 알려주시면 감사하겠습니다.^^

익명 사용자의 이미지

전 memset안해도 동일하게 ff로 나오는데요...무슨 차이일까요?^^;;

gafani의 이미지

pajaebeo님의 말씀대로 환경이 걱정이되어

커널 2.6 대의 리눅스 환경에서 컴파일한 결과....

익명님의 말씀대로 잘 나오네요 ㅡ.ㅡ;;;;;;

조급해 하지마라. 나는 조금씩 발전하고 있다.

pajaebeo의 이미지

%x 는 unsigned integer 로 해석하기 때문에 저런 결과가 나오는 것 아닐까요..
memset 했을 경우와 안했을 경우의 차이는 precision을 2로 주었기 때문이라고 생각됩니다.
printf는 precision이 2 이라도 넘치는 부분에 대해서는 모두 출력하는게 원칙입니다.

위에 익명분의 경우, 해당 메모리의 상위 bit들이 (우연히) 0으로 되어 있어서 그렇다고 생각됩니다.

ps. 하지만, struct 에 대한 pack 은 implementation dependent 한 부분이므로,
ganfani 님이 사용하는 환경과 printf의 구현에 따라 답은 달라 질 수 있다고 생각됩니다.

gafani의 이미지

pajaebeo님의 말씀대로 컴파일환경이 커널 2.4대여서 2.6대로 옮겨서 컴파일해본 결과

정상적으로 ff가 나옵니다.

ps 말대로 이네요.!

감사합니다. 하면할수록 의문점이 생기지만 열심히 알아보고 가슴이 답답할 때 한번더 여쭙겠습니다.!ㅎㅎ

조급해 하지마라. 나는 조금씩 발전하고 있다.

익명 사용자의 이미지

결론부터 말하자면 위 코드는 undefined behavior를 발생시키고, 잘못된 코드입니다.

문제는 요기입니다.

printf( "%2x\n", test_t );

포맷 문자열의 x는 unsigned int를 요구하는데 막상 넘어가는건 구조체 t형의 변수지요. 이렇게 포맷 문자열의 변환 지정자(d,i,o,u,x같은거)가 뒤의 실제로 넘어가는 매개변수의 타입과 일치하지 않으면 undefind behavior를 일으킵니다. 아무 일도 없었던건 그야말로 요행수입니다.

왜 문제가 되는가? 는 가변 인자 함수를 한번이라도 직접 va_start, va_end, va_arg 등을 이용해서 구현을 해보시면 이해가 갑니다. C언어는 가변 인자 함수의 매개변수에 대해 아무런 안전장치도 제공하지 않으며, 모든 책임은 프로그래머가 집니다. 따라서 가변 인자 함수를 사용할 때는 굉장히 주의해서 써야 합니다.

그리고 비트필드엔 unsigned char를 쓰면 안되고 unsigned int를 써야 합니다.

pajaebeo의 이미지

ANSI C 에서 규정하는 strictly conforming mode 에서는 bit-field 를 'boolean / signed or unsigned integer' 로 사용하라고 제한하고 있네요..
하지만, 일반적인 Native 환경에서의 프로그램에서는 다른 타입의 bit-field를 사용하여도 상관 없을 듯 합니다.
물론 8bit-wide ISA 가 아니라면, char 를 사용하는 코드가 얻는 이득은 아주 적긴 하지만, 위의 예를 든 코드를 보면, 고작 bit-field 하나에 pack 을 사용하는 것을 볼 수 있습니다. 이는 program size에 아주 민감한 코드일 여지도 있고... 기타 여러가지 이유로 char type을 사용할 수도 있다고 생각합니다.
물론 이의 사용에 대한 책임은 프로그래머가 지는 것이고, 프로그래머는 compiler 스펙을 확인하고, 실제 시스템에서 검증하여 사용하여야 하겠지요..

ps. '~~는 쓰면 안된다'라는 말은 매우 조심해서 사용하는 것이 좋을 것 같습니다. 세상은 요지경~~

익명 사용자의 이미지

원칙상 int를 쓰는게 맞는건 제껴두고서라도, 비트필드에서 int를 char로 바꾼다고 결과가 달라지나요? 비트 필드가 뭐하는 건지부터 생각을 해 보세요.

익명 사용자의 이미지

ANSI C에서 규정하는 strictly conforming mode 같은건 없습니다. 제발 특정 컴파일러의 옵션이나 에러메시지만 가지고 대충 추측해서 말하지 마세요.

pajaebeo의 이미지

어떤 환경에서 작업하시는지 몰라도, 저는 제 프로그램에 한해, 제가 쓰는 Compiler의 스펙을 자주 확인하며 쓰고 있습니다.
제가 지금 사용하는 버전은 codesourcery의 g++ lite 버전이고, gcc 3.6.1 기반입니다.
해당 스펙을 참조하면 strictly conforming mode 에서만 위와 같이 '권장'하고, 다른 type을 쓰지 말라는 제한은 없습니다.
님께서 추측한다라고 생각하시는 특정 컴파일러는 위의 버전입니다.

익명 님이 쓰시길...
'ANSI C에서 규정하는 strictly conforming mode 같은건 없습니다.'

ISO/IEC 9899:TC2 에서 발췌합니다.
"A strictly conforming program shall use only those features of the language and library
specified in this International Standard.2) It shall not produce output dependent on any
unspecified, undefined, or implementation-defined behavior, and shall not exceed any
minimum implementation limit."
해당 문서에서 'strictly conforming program' 라는 문구는 이탤릭체로 되어 있으므로, 해당 문서의 'definition'에 속하는 것이 아닌지요?

또 익명님이 말씀하시길..
'제발 특정 컴파일러의 옵션이나 에러메시지만 가지고 대충 추측해서 말하지 마세요'

라고 하셨는데, 그러면 이러한 내용의 문제는 도대체 무엇을 기준으로 얘기하면 되는지요?
저는 제가 사용하는 환경을 기준으로 (제 경험을 토대로) 도움을 주고자 답글을 달고 있으며,
프로그래밍에는 항상 환경적인 요소가 아주 중요한 이슈이므로, 타인의 이슈를 얘기할 때는 환경적인 요소에 대한 너그러움을 항상 염두해 두어야 한다고 생각하는 사람입니다 :)

제 지식이 짧아 여러분들께 오인을하게 하였다면 죄송하고,
다른 좋은 의견은 늘 감사히 받겠습니다. :)

pajaebeo의 이미지

gcc 3.6.1 기반입니다. --> 4.6.1 기반입니다 ㅠㅠ

익명 사용자의 이미지

해당부분은 strictly conforming program 이라는 개념이 무엇인지 설명하는 구절입니다.
C99를 따르는 C컴파일러가 무슨 특별히 엄격한 모드를 제공해야 한다고 말하는 부분이 아니에요.
표준이면 표준이고 아니면 아닌거지, 무슨 ANSI C에서 strictly confirming 'mode'를 규정하고
또 그 모드 아니면 원래 원칙 무시하고 아무거나 헐렁하게 써도 된답니까?
조금만 신경써서 인용하신 부분 앞뒤로 읽어보면 아실 텐데요.

제가 님이 특정 컴파일러의 옵션이나 에러메시지만 가지고 대충 추측해서 말한다고 생각하는게 바로 이런 부분입니다.
gcc에 ANSI C 모드가 있으니까 표준에서도 뭔가 엄격한 모드에 대한 서술이 따로 있을거라 생각하는거 같은데 그런건 없어요.
그건 그저 gcc라는 특정 컴파일러가 제공하는, 어디까지 표준을 따를 것인가,
경고와 에러를 어느 수준까지 내 줄건지에 대한 옵션에 불과해요.

> 이러한 내용의 문제는 도대체 무엇을 기준으로 얘기하면 되는지요?

표준이나 이식성 이슈에 대한 문제는 그때마다 답이 달라요. 표준이 어떤 내용인지는 표준문서 보면 되겠지만
어느 쪽을 따를 것인가는 이상과 현실의 절충이 필요한 부분이기 때문에...
하지만 비트 필드에 char 쓰는 문제에 한해서는 아주 간단한 답이 있습니다. '절대 그렇게 하지 마세요'

비트 필드에 int 대신 char 쓴다고 어떤 이득이 있습니까? 아무것도 없습니다.
그러면 어떤 손해가 있습니까? 표준을 어기게 되고 이식성이 떨어집니다.
그러면 char 대신 int 쓰도록 습관을 바꾸는게 대단히 어렵거나 까다로운 일입니까? 전혀.

http://www-903.ibm.com/kr/techinfo/pseries/download/xlc_bitfield_compile.pdf

pajaebeo의 이미지

늦은 시간인데, 좋은 의견 감사드립니다.

익명님의 의견은 충분히 이해하고 공감합니다.
하지만 저는 여전히 'char' 대신 'int'로 쓰라는 부분은 수긍할 수가 없습니다.

예를들어, 32-bit 머신에서 특정하드웨어의 (memory mapped) register가 8-bit만 선언되고,
나머지 24-bit은 unknown 이고, 실제로 Datasheet에는 unknown bit 에 대한 access의 결과는
unpredictable 이라고 적혀 있는 경우가 더러 있습니다.
이런 경우는 int로 선언했다가는 낭패를 볼 수 있습니다.
unknown 부분에 실제로 제조사가 넣어놓은 hidden 이 있을 수도 있고, 말그대로 'unpredictable'
할 수 있기 때문이지요.
( 제가 일하는 분야는 Hardware를 직접 다루는 코드를 많이 쓰기 때문에, 각 Register 별로 bit-field를 상당히 많이 쓰는 편입니다. )

또한, network 나, UART 등의 big-endian byte 전송 패킷을 다룰 경우는 bit-field 구현시 당연히 'char'로 명시하여 써야합니다.

하물며, ISA를 오가는 포팅의 경우 int를 사용했다고 호환성 문제가 해결되지도 않습니다.

익명님의 말씀 중:
>제가 님이 특정 컴파일러의 옵션이나 에러메시지만 가지고 대충 추측해서 말한다고 생각하는게 바로 이런 부분입니다.
>gcc에 ANSI C 모드가 있으니까 표준에서도 뭔가 엄격한 모드에 대한 서술이 따로 있을거라 생각하는거 같은데 그런건 없어요.
>그건 그저 gcc라는 특정 컴파일러가 제공하는, 어디까지 표준을 따를 것인가,
>경고와 에러를 어느 수준까지 내 줄건지에 대한 옵션에 불과해요
..
>표준이나 이식성 이슈에 대한 문제는 그때마다 답이 달라요. 표준이 어떤 내용인지는 표준문서 보면 되겠지만
>어느 쪽을 따를 것인가는 이상과 현실의 절충이 필요한 부분이기 때문에...
..

저는 또한 위에 님께서 쓰신 이유때문에 'bit-field를 무조건 int로 사용하는 것'에 수긍이 가지 않습니다.
표준조차 강권하지 않는데, 하물며 우리가 어떻게 이렇게 강권할 수 있는지요???

위에 댓글에도 말씀드렸다시피, 이러한 부분은 순전히 프로그래머의 몫(자유+책임)으로 남겨두는 것이 좋다는 생각하는 바입니다..

ps. Device Driver를 짜보면 bit-field 때문에 상당히 애를 먹을 때가 많습니다.
실제로 저는 gcc 4.4.3 에서 4.6.1 로 바꾸었을 때,
bit-field 해석이 변경되어 골치를 썩은 적도 있습니다.
( codesourcery g++ 기준으로, 4.5 ~ 부터는 선언되지 않은 나머지 부분의 bit들에 대해서는
컴파일러가 자동으로 minimum bit-width 로 최적화 하더군요 0_0;; )

또한, google에서 관련 thread를 찾아보면, bit-field 자체를 쓰지 말라는 의견도 많습니다.
차라리 제가 후임들에게 권유한다면, bit-filed를 쓰지 마라고 하고 싶군요.....

kukyakya의 이미지

아무리 곰곰히 생각해봐도 이유를 잘 모르겠어서 질문 드립니다.

레지스터 조작이나 big-endian 관련 작업을 할 떄에 꼭 char type bit field를 사용해야하는 이유가 무엇인가요?

planetarium의 이미지

bitfield에서 type을 바꿔 썼을때 구체적으로 어떤 효과가 나타나는지 먼저 설명해주세요.

pajaebeo의 이미지

오랜만에 들왔습니다 :)

BUS 측면에서 봤을 때, (예를들어 ARM의 AXI, AHB, APB)
volatile unsigned char *c_reg;
volatile unsigned int *ui_reg;

라고 선언하고, 동일한 register를 access 해보십시오.
이를 RTL Simulation으로 확인해보면, 실제로 c_reg 로 접근하면 하위 8-bit만 write가 일어나고,
ui_reg 로 접근하면 32-bit 모두 write가 일어납니다. (물론 이는 BUS Architecture에 따라 다르게 동작할 수도 있습니다.)

ARM의 AMBA bus를 예를 들면 아래와 같습니다.

bus type ||| AHB/AXI 의 경우 ||| APB 의 경우( APB 에는 byte write를 할 수 있는 protocol이 없습니다.)
*c_reg = 0xff; ||| 하위 8-bit 에만 0xFF write 함. ||| 32-bit 전체에 0x000000FF 를 write 함.
*ui_reg = 0xff; ||| 32-bit 전체에 0x000000FF 를 write 함. ||| 32-bit 전체에 0x000000FF 를 write 함.

위와 같이 선언에 따라 H/W 에 미치는 영향이 달라지게 됩니다.
( 물론 Compiler option 또는 버젼을 변경해에서 Assembler를 생성할 때부터,
volatile unsigned char를 strb 또는 str 로 해석을 바꿀 수도 있습니다. )

ARM 을 제외한 다른 BUS Architecture에 대해 문외한이라, ARM을 기반으로 설명드렸는데..
도움이 되었으면 좋겠습니다.

감사합니다~~

pajaebeo의 이미지

BIt-field 적용할 때도 위와 동일하게 적용이 됩니다~~

익명 사용자의 이미지

몇몇 경우는 bitfield를 안 쓰는게 진짜 낫겠네요.
말씀하신 레지스터 접근 같은 경우도, 8 bit access를 하고 bit mask를 하는게 좋겠고..

익명 사용자의 이미지

우선 사과부터 드립니다.

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348ak/Babjddhe.html

특정 구현체에선 비트 필드 내의 데이터형을 달리함으로써 의미 있는 차이가 생기는 것을 자료 뒤지다가 찾았습니다.

위의 예에선 컨테이너 라는 개념을 쓰는데 동작 방식이 상당히 복잡합니다.

되도록 비트 필드 쓰지 말라는 의견에 대해선 동의합니다.

pajaebeo의 이미지

좋은 글 감사합니다.
사과까지 할 필요가 있나요.. 의견이 다른것 뿐인데요..
필드가 다르면 생각이 다를수 밖에 없는거 같네요!

익명 사용자의 이미지

오랜만에 제 아이디로 검색하다가 들어오게 되었네요...

우선 답글을 달아주신

pajaebeo님
kukyakya님
planetarium님
다수의 익명님들께

감사드립니다.^^;

gafani의 이미지

오랜만에 제 아이디로 검색하다가 들어오게 되었네요...

우선 답글을 달아주신

pajaebeo님
kukyakya님
planetarium님
다수의 익명님들께

감사드립니다.^^;;;;

조급해 하지마라. 나는 조금씩 발전하고 있다.

댓글 달기

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