development

서명되지 않은 char을 char로 또는 그 반대로 바꿀 수 있습니까?

big-blog 2020. 12. 29. 08:20
반응형

서명되지 않은 char을 char로 또는 그 반대로 바꿀 수 있습니까?


다음과 같은 데이터를 기대하는 함수를 사용하고 싶습니다.

void process(char *data_in, int data_len);

그래서 그것은 실제로 몇 바이트를 처리하고 있습니다.

그러나 원시 바이트에 관해서는 "unsigned char"로 작업하는 것이 더 편안합니다 (양수 0에서 255 값만 처리하는 것이 더 옳다고 느껴집니다). 그래서 제 질문은 다음과 같습니다.

unsigned char *이 함수에를 항상 안전하게 전달할 수 있습니까 ?

다시 말해:

  • 정보 손실없이 char와 unsigned char간에 안전하게 변환 (캐스트) 할 수 있다는 것이 보장됩니까?
  • 정보 손실없이 char 및 unsigned char 포인터간에 안전하게 변환 (캐스트) 할 수 있습니까?

보너스 : 대답이 C와 C ++에서 동일합니까?


짧은 대답은 명시 적 캐스트를 사용하는 경우 예입니다. 그러나 자세히 설명하기 위해 살펴볼 세 가지 측면이 있습니다.

1) 변환의 합법성 소스 유형이 먼저 변환 될 수 있기 때문에 (일부 유형의 경우 ) (일부 유형의 경우 )
변환 이 일반적으로 가능 하며, 다음 을 사용하여 대상 유형으로 변환 할 수 있습니다. 명시 적 (§5.2.9 / 13) :signed T*unsigned T*Tvoid *void *static_cast

static_cast<unsigned char*>(static_cast<void *>(data_in))

이는 다음과 같이 축약 될 수 있습니다 (§5.2.10 / 7).

reinterpret_cast<unsigned char *>(data_in)

이는 char표준 레이아웃 유형 (§3.9.1 / 7,8 및 §3.9 / 9)이고 서명이 정렬을 변경하지 않기 때문입니다 (§3.9.1 / 1). C 스타일 캐스트로 작성할 수도 있습니다.

(unsigned char *)(data_in)

다시, 이것은에서 두 가지를 작품 unsigned*signed*다시. 또한이 절차를 한 방향으로 적용한 후 다시 적용하면 포인터 값 (즉, 가리키는 주소)이 변경되지 않는다는 보장도 있습니다 (§5.2.10 / 7).

이 모든 것은 signed char *사이의 변환 unsigned char *뿐만 아니라 각각 char */ unsigned char *char */ 에도 적용됩니다 signed char *. ( char, signed char그리고 unsigned char세 가지 유형, §3.9.1 / 1은 공식적 있습니다.)

명확하게 말하면 세 가지 캐스트 방법 중 어떤 것을 사용하는지는 중요하지 않지만 하나를 사용해야합니다. 변환은 합법적이지만 표준 변환이 아니기 때문에 포인터를 전달하는 것만으로는 작동하지 않으므로 암시 적으로 수행되지 않습니다 (시도하면 컴파일러에서 오류가 발생 함).

2) 값에 대한 접근
의 정의 함수 내에서 포인터를 역 참조하는 경우, 즉 *data_in기본 문자에 대한 glvalue를 검색하기 위해 수행하면 어떻게됩니까 ? 이것은 잘 정의되고 합법적입니까? 여기서 관련 규칙은 엄격한 별칭 규칙 (§3.10 / 10)입니다.

프로그램 이 다음 유형 중 하나가 아닌 다른 glvalue통해 객체의 저장된 값에 액세스하려고 하면 동작이 정의되지 않습니다.

  • [...]
  • 객체의 동적 유형에 해당하는 서명되거나 서명되지 않은 유형 인 유형
  • [...]
  • char또는 unsigned char유형입니다.

따라서, 액세스하는 signed char(또는 char)를 통해 unsigned char*(또는 char) 그 반대는 성립이 규칙에 의해 허용되지 않는다 - 당신은 문제없이이 작업을 수행 할 수 있어야합니다.

3) 결과 값
유형 변환 된 포인터를 역 참조한 후 얻은 으로 작업 할 수 있습니까? 위에서 설명한 포인터의 변환 및 역 참조는 문자 주소에 저장된 비트 패턴을 재 해석 (변경하지 않음)하는 것과 같다는 점을 기억하는 것이 중요합니다. 그렇다면 부호있는 문자의 비트 패턴이 부호없는 문자의 비트 패턴으로 해석 될 때 (또는 그 반대의 경우) 어떻게 될까요?

unsigned에서 signed로 이동할 일반적인 효과 는 0에서 128 사이의 값에 대해 아무 일도 일어나지 않고 128보다 큰 값은 음수가됩니다. 반대로 유사 : 부호있는에서 부호없는 것으로 이동할 때 음수 값이 128보다 큰 값으로 나타납니다.

그러나이 동작 은 실제로 표준에 의해 보장되지 않습니다 . 표준이 보장하는 유일한 것은 char, unsigned char세 가지 유형 signed char모두에 대해 모든 비트 (반드시 8, btw는 아님)가 값 표현에 사용된다는 것입니다. 따라서 하나를 다른 것으로 해석하고 몇 개의 복사본을 만든 다음 원래 위치에 다시 저장하면 정보 손실이 없음을 확신 할 수 있지만 (필요한 경우) 값이 무엇인지 반드시 알 수는 없습니다. 실제로 의미합니다 (적어도 완전히 이식 가능한 방식은 아닙니다).


unsigned char또는 signed char해석 일뿐입니다. 전환이 일어나지 않습니다.

바이트를 처리하고 있으므로 의도를 표시하려면 다음과 같이 선언하는 것이 좋습니다.

void process(unsigned char *data_in, int data_len);

[편집자가 언급 한대로 : 일반 char은 서명 된 유형이거나 서명되지 않은 유형일 수 있습니다. C 및 C ++ 표준은 둘 중 하나를 명시 적으로 허용합니다 (항상 unsigned char또는 중 하나 별도의 유형 signed char이지만 둘 중 하나 와 동일한 범위를 가짐)]


다른 종류의에 대한 포인터를 전달할 수 char있지만 명시 적으로 캐스팅해야 할 수도 있습니다. 포인터는 동일한 크기와 동일한 값을 보장합니다. 변환 중에는 정보 손실이 없습니다.

함수 내에서 로 변환 char하려면 변수에 값을 unsigned char할당 하거나 값을 .charunsigned charcharunsigned char

당신이 변환해야하는 경우 unsigned charchar데이터 손실없이, 그것은 여전히 가능 조금 더 열심히,하지만 :

#include <limits.h>

char uc2c(unsigned char c)
{
#if CHAR_MIN == 0
  // char is unsigned
  return c;
#else
  // char is signed
  if (c <= CHAR_MAX)
    return c;
  else
    // ASSUMPTION 1: int is larger than char
    // ASSUMPTION 2: integers are 2's complement
    return c - CHAR_MAX - 1 - CHAR_MAX - 1;
#endif
}

이 기능은 변환됩니다 unsigned charchar반환 된 값이 동일한에 변환 돌아올 수있는 그런 방식으로 unsigned char매개 변수로 값.


예, 문제없이 항상 char에서 unsigned char로 또는 그 반대로 변환 할 수 있습니다. 다음 코드를 실행하고 ASCII 테이블 (참조 : http://www.asciitable.com/ ) 과 비교하면 직접 증명을 볼 수 있으며 C / C ++가 변환을 처리하는 방법을 확인할 수 있습니다 . 정확히 같은 방식으로 :

#include "stdio.h"


int main(void) {
    //converting from char to unsigned char
    char c = 0;
    printf("%d byte(s)\n", sizeof(char));  // result: 1byte, i.e. 8bits, so there are 2^8=256 values that a char can store.
    for (int i=0; i<256; i++){
        printf("int value: %d - from: %c\tto: %c\n", c,  c, (unsigned char) c);
        c++;
    }

    //converting from unsigned char to char
    unsigned char uc = 0;
    printf("\n%d byte(s)\n", sizeof(unsigned char));
    for (int i=0; i<256; i++){
        printf("int value: %d - from: %c\tto: %c\n", uc, uc, (char) uc);
        uc++;
    }
}

라인이 너무 많기 때문에 출력을 게시하지 않습니다! 출력에서 각 섹션의 전반부, 즉 i = 0 : 127에서 문자에서 부호없는 문자로 또는 그 반대로 변환이 수정이나 손실없이 잘 작동 함을 알 수 있습니다.

However, from i=128:255 the chars and the unsigned chars cannot be casted, or you would have different outputs, because unsigned char saves the values from [0:256] and char saves the values in the interval [-128:127]). Nevertheless, the behaviour in this 2nd half is irrelevant, because in C/C++, in general, you only lead with chars/unsigned chars as ASCII characters, whose can take only 128 different values and the other 128 values (positive for chars or negative for unsigned chars) are never used.

If you never put a value in a char that doesn't represent a character, and you never put a value in an unsigned char that doesn't represent a character, everything will be OK!

extra: even if you use UTF-8 or other encodings (for special characters) in your strings with C/C++, everything with this kind of casts would be OK, for instance, using UTF-8 encoding (ref. http://lwp.interglacial.com/appf_01.htm):

char hearts[]   = {0xe2, 0x99, 0xa5, 0x00};
char diamonds[] = {0xe2, 0x99, 0xa6, 0x00};
char clubs[]    = {0xe2, 0x99, 0xa3, 0x00};
char spades[]   = {0xe2, 0x99, 0xa0, 0x00};
printf("hearts (%s)\ndiamonds (%s)\nclubs (%s)\nspades (%s)\n\n", hearts, diamonds, clubs, spades);

the output of that code will be:
hearts (♥)
diamonds (♦)
clubs (♣)
spades (♠)

even if you cast each of its chars to unsigned chars.

so:

  • "can I always safely pass a unsigned char * into this function?" yes!

  • "is it guaranteed that I can safely convert (cast) between char and unsigned char at will, without any loss of information?" yes!

  • "can I safely convert (cast) between pointers to char and unsigned char at will, without any loss of information?" yes!

  • "is the answer same in C and C++?" yes!


You really need to view the code to process() to know if you can safely pass in unsigned characters. If the function uses the characters as an index into an array, then no, you can't use unsigned data.


Semantically, passing between unsigned char * and char * are safe, and even though casting between them, so as in c++.

However, consider the following sample code:

#include "stdio.h"

void process_unsigned(unsigned char *data_in, int data_len) {
    int i=data_len;
    unsigned short product=1;

    for(; i--; product*=data_in[i]) 
        ;

    for(i=sizeof(product); i--; ) {
        data_in[i]=((unsigned char *)&product)[i];
        printf("%d\r\n", data_in[i]);
    }
}

void process(char *data_in, int data_len) {
    int i=data_len;
    unsigned short product=1;

    for(; i--; product*=data_in[i]) 
        ;

    for(i=sizeof(product); i--; ) {
        data_in[i]=((unsigned char *)&product)[i];
        printf("%d\r\n", data_in[i]);
    }
}

void main() {
    unsigned char 
        a[]={1, -1}, 
        b[]={1, -1};

    process_unsigned(a, sizeof(a));
    process(b, sizeof(b));
    getch();
}

output:

0
255
-1
-1

All the code inside process_unsigned and process are just IDENTICAL. The only difference is unsigned and signed. This sample shows that the code in the black box, do be affected by the SIGN, and nothing is guaranteed between the callee and caller.

Thus I would say that, it's applicable of passing only, but none of any other possibilities is guaranteed.

ReferenceURL : https://stackoverflow.com/questions/15078638/can-i-turn-unsigned-char-into-char-and-vice-versa

반응형