c에서 "~" 연산자의 (암시적) 타입변환에 대한 질문입니다.

ktd2004의 이미지

다음과 같은 소스를 테스트했습니다.

int main(void)
{
    unsinged char a = 0xF0;
    unsinged char b = 0x0F;
    if( a == ~b) {
        printf("equal\n");
    } else {
        printf("not equal\n");
    }
    return 0;
}

결과는 "not equal"입니다.
이 문제로 반나절을 보냈습니다.

제가 처음에 코딩을 할 때는
* "unsigned char" 타입을
* "~"연산자로 걸고
* "unsigned char"와 비교를 하면
"equal"이 출력될 것으로 예측했습니다.

"if( a == ~b)" 부분에 대한 정확한 분석/정보 부탁드립니다.
미리 답변 감사드립니다.

aero의 이미지

다음을 실행해보면 확인 가능할겁니다.

#include<stdio.h>
int main(void)
{
    unsigned char a = 0xF0;
    unsigned char b = 0x0F;
    printf("%x\n", a);
    printf("%x\n",~b);
    if( ((unsigned char)a) == ((unsigned char) ~b) ) {
        printf("equal\n");
    } else {
        printf("not equal\n");
    }
    return 0;
}

결과:
f0
fffffff0
equal

비교전에 미리선언한 변수선언형으로 casting해주면 equal로 결과가 나옵니다.
~ 연산자를 그냥 쓰면 사용하는 architecture의 기본형(위에서는 32bit 형식)로 형변환이 되는것 같네요.

전웅의 이미지

참고하시기 바랍니다.

http://groups.google.co.kr/group/han.comp.lang.c/msg/cb30ec78db03cf81

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

ktd2004의 이미지

참부해 주신 링크 잘 보았습니다.
링크를 보고 제가 이해한 중요한 점은 다음과 같습니다.
* ~ 연산자의 피연산자는 signed int로 형변환된다.

따라서 원문은 다음과 같은 순서로 해석된다고 이해했습니다.

if( 0xF0 == ~0x0F)            // 원문
if( 0xF0 == ~0x0000000F)      // "~" 연산자의 피연산자가 signed int로 형변환
if( 0xF0 == 0xFFFFFFF0)       // "~" 연산자 적용
if( 0x000000F0 == 0xFFFFFFF0) // "unsigned char"가 "unsigned int"로 형변환

* 제가 이해한 내용이 맞는지요?
* 그럼 원문을 다음과 같이 변경해서 사용하면 별 문제(이식성/표준)가 없는지요?

if( a == (unsigned char)~b)

답변 감사드립니다.

man777의 이미지

보통 int형은 해당 머신의 register의 사이즈와 같다고 봅니다.
당연히 register크기와 같은 자료형을 사용할 경우 속도면에서나 효율적이기 때문에 그렇게 만들었겠죠.

a == ~b 이구문에서는 2번의 연산이 일어납니다.
1번째 비트연산.
2번째 ==연산 ==연산은 내부적으로 a-b값이 0인지 판별하기 때문에 일반 산술연산으로 봐도 무방합니다.

a,b는 연산을 위해서 4바이트로 확장이 됩니다.
a -> 0x000000F0
b -> 0x0000000F
여기에서 만약 signed char이었다고 하면...
a -> 0xFFFFFFF0
b -> 0x0000000F
이런식으로 확장이 일어나겠죠 ^^;
이 이후에 연산을 하시면 설명이 되겠죠?

(unsigned char)a == (unsigned char)~b
(unsigned char)로 강제 캐스팅을 하게되면...
우항 b의 경우 int로 확장이 되어 ~연산을 한후 (unsigned char)로 강제 캐스팅이 되어 상위값을 짜릅니다.
그리고 == 연산을 위해
다시 a항과 b항이 모두 int로 확장되어야 겠죠?

ㅎㅎ 정확한건 아니니 비판적으로 수용하시길...

------------------------------
lol

전웅의 이미지

> (unsigned char)a == (unsigned char)~b
> (unsigned char)로 강제 캐스팅을 하게되면...
> 우항 b의 경우 int로 확장이 되어 ~연산을 한후 (unsigned char)로 강제 캐스팅이 되어 상위값을 짜릅니다.
>

http://groups.google.co.kr/group/han.comp.lang.c/msg/442779a7378e556a

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

전웅의 이미지

> 참부해 주신 링크 잘 보았습니다.
> 링크를 보고 제가 이해한 중요한 점은 다음과 같습니다.
> * ~ 연산자의 피연산자는 signed int로 형변환된다.
>

보다 정확히는 "integral promotion 이 적용된다"가 맞습니다.

대부분의 구현체에서 UCHAR_MAX <= INT_MAX 이기에 int 로 진급이 이루어
지지만, (일부 DSP 를 위한 freestanding 환경에서처럼) UCHAR_MAX >
INT_MAX 라면 int 가 아닌 u-int 로 진급이 이루어집니다. 예를 들어, ~
가 적용되는 피연산자의 type 이 u-char 가 아니라 u-short 이고,
USHRT_MAX == UINT_MAX 인 환경이라면 역시 int 가 아닌 u-int 로 진급
됩니다. 여기서는 문제가 되는 경우인 int 로 진급되는 경우만을 이야기
하도록 하겠습니다.

> 따라서 원문은 다음과 같은 순서로 해석된다고 이해했습니다.
>
> if( 0xF0 == ~0x0F) // 원문
> if( 0xF0 == ~0x0000000F) // "~" 연산자의 피연산자가 signed int로 형변환
> if( 0xF0 == 0xFFFFFFF0) // "~" 연산자 적용
> if( 0x000000F0 == 0xFFFFFFF0) // "unsigned char"가 "unsigned int"로 형변환
>

상수를 16진 정수 상수 형태로 쓸 경우 연산이 실제로는 "값"에 적용된다
는 사실이 가려질 수 있습니다. 보다 분명한 설명을 위해 아래와 같이
기술하겠습니다.

1. (a == ~b) 가 주어짐 => (u-char == ~u-char): (240 == ~15)
2. ~ 연산 적용 전에 b 에 integral promotion 적용되어 int 형으로 변환
    => (u-char == ~int): (240 == ~15)
3. int 형의 값 15 에 ~ 연산 적용되어 결과는 implementation-defined
    => (u-char == int): (240 == i.d. 값)
4. == 연산 적용을 위해 좌측 피연산자에 변환 적용되어 int 형으로 변환
    => (int == int): (240 == i.d. 값)
5. == 연산 적용되어 결과 나옴

> * 제가 이해한 내용이 맞는지요?

주요한 차이점은 int 형의 값 0x0000000F 에 ~ 를 적용한 결과가 모든
환경에서 0xFFFFFFF0 으로 나타낼 수 없다는 것(!)과, 그 이후 값을
비교할 때 좌측 피연산자는 u-int 가 아닌 int 로 변환된 이후 상등
비교 연산이 적용된다는 것입니다.

> * 그럼 원문을 다음과 같이 변경해서 사용하면 별 문제(이식성/표준)가 없는지요?
>
> if( a == (unsigned char)~b)
>

문제의 핵심은 비트 연산자를 항상 무부호 정수형에 적용되도록 하는 것
입니다. 이 경우 ~ 는 어차피 b 가 갖는 type (u-char) 의 진급된 type
(int) 에 적용되고 그 결과는 구현체 의존적인 값(b 의 값이 양수일 때
~ 는 sign bit 에도 영향을 주므로 ~ 연산 적용 결과는 음수)이 되며, 그
값을 다시 u-char 형으로 변환(음수를 u-char 로 변환하는 행동은 명확히
정의되어 있으나 그 음수값 자체에 이식성이 없기 때문에 u-char 로 변환
이후의 결과에도 이식성이 없음)해 a 와 비교하는 것에 불과합니다 - 제가
링크해드린 글 이외에 동일 thread 의 다른 글들도 한번 보시기 바랍니다.

http://groups.google.co.kr/group/han.comp.lang.c/browse_frm/thread/149c912a26aed543

이식성을 얻기 위해선 아래와 같이 수행해야 합니다.

unsigned char a = 0xF0, b = 0x0F;
...
if (a == (unsigned char)~(unsigned int)b)

이 경우 b 를 int 가 아닌 무부호 정수형으로 변환한 상태에서 ~ 를 적용
하며 이를 다시 unsigned char 로 변환하는 것은 모든 환경에서 상위 비트
를 잘라내는 결과를 보입니다.

다시 한번 강조드리지만, 핵심은 비트 연산 적용 시점에 피연산자의 type
을 더이상 implicit conversion 이 적용되지 않는 무부호 정수형으로
만들어 주는 것입니다.

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

댓글 달기

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