67

次のようなデータを期待する関数を使用したい:

void process(char *data_in, int data_len);

したがって、実際には数バイトを処理しているだけです。

しかし、生のバイトに関しては、「unsigned char」を使用する方が快適です (正の 0 から 255 の値のみを処理する方が正しいと「感じる」)ので、私の質問は次のとおりです。

unsigned char *この関数に常に安全に a を渡すことはできますか?

言い換えると:

  • 情報を失うことなく、char と unsigned char の間で自由に安全に変換 (キャスト) できることが保証されていますか?
  • 情報を失うことなく、char へのポインタと unsigned char の間で自由に安全に変換 (キャスト) できますか?

おまけ: 答えは C と C++ で同じですか?

4

6 に答える 6

104

明示的なキャストを使用する場合、簡単な答えは「はい」ですが、それを詳細に説明するために、次の3つの側面を検討する必要があります。

1)変換の合法性
ソースタイプを最初に変換でき(これは標準の変換signed T*、§4.10)、明示的(§5.2.9/ 13):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)

は標準レイアウトタイプ( §3.9.1 char/7,8および§3.9/9)であり、符号は配置を変更しないため(§3.9.1/ 1)。Cスタイルのキャストとして書くこともできます。

(unsigned char *)(data_in)

繰り返しますが、これは前後の両方の方法で機能しunsigned*ますsigned*。この手順を一方向に適用してから元に戻した場合でも、ポインター値(つまり、それが指しているアドレス)が変更されないという保証もあります(§5.2.10/ 7)。

signed char *これはすべて、との間の変換だけでなく、それぞれ/と/にunsigned char *も当てはまります。(、および正式には3つの異なるタイプ、§3.9.1/ 1です。)char *unsigned char *char *signed char *charsigned charunsigned char

明確にするために、3つのキャスト方法のどれを使用するかは問題ではありませんが、1つを使用する必要があります。変換は合法ですが、標準の変換ではないため、ポインタを渡すだけでは機能しません。したがって、暗黙的に実行されることはありません(試行すると、コンパイラはエラーを発行します)。

2)値へのアクセス
の明確 性関数内でポインターを逆参照する場合、つまり*data_in、基になる文字のglvalueを取得するために実行するとどうなりますか。これは明確で合法ですか?ここで関連するルールは、厳密なエイリアシングルール(§3.10/10)です。

プログラムが次のタイプのいずれか以外のglvalueを介してオブジェクトの保存された値にアクセスしようとした場合、動作は未定義です。

  • [...]
  • オブジェクトの動的型に対応する符号付きまたは符号なしの型である型、
  • [...]
  • charまたはunsigned charタイプ。

したがって、signed char(またはchar)を介してunsigned char*(または)にアクセスすること、charおよびその逆にアクセスすることは、このルールによって禁止されていません。問題なくこれを実行できるはずです。

3)結果の値
型変換されたポインターの参照を解除した後、取得した値で作業できるようになりますか?上記のポインタの変換と逆参照は、文字のアドレスに格納されているビットパターンを再解釈する(変更しない)ことになることに注意することが重要です。では、符号付き文字のビットパターンが符号なし文字のビットパターンとして解釈されると(またはその逆に)どうなりますか?

符号なしから符号付きに移行する場合、一般的な影響として、0〜128の値では何も起こらず、128を超える値は負になります。逆も同様です。符号付きから符号なしに移行する場合、負の値は128より大きい値として表示されます。

ただし、この動作は実際には標準によって保証されていません。標準が保証する唯一のことは、3つのタイプchar、、、unsigned charおよびsigned charのすべてについて、すべてのビット(必ずしも8、ところではない)が値の表現に使用されることです。したがって、一方を他方として解釈し、いくつかのコピーを作成してから元の場所に保存すると、(必要に応じて)情報が失われることはありませんが、必ずしも値がわからない場合があります。実際には(少なくとも完全に移植可能な方法ではありません)を意味します。

于 2013-03-02T08:11:05.593 に答える
17

unsigned charまたはsigned char単なる解釈です。変換は発生していません。

バイトを処理しているので、意図を示すために、次のように宣言することをお勧めします

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

[編集者による指摘: プレーンcharは、符号付きまたは符号なしタイプのいずれかです。C および C++ 標準では、明示的にいずれかを許可しています (常に または とは別の型ですが、それらのいずれunsigned charsigned charと同じ範囲を持ちます)]

于 2013-02-25T23:39:16.507 に答える
6

はい、いつでも 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 から、char から unsigned char への変換、およびその逆の変換が、変更や損失なしでうまく機能することが出力でわかります。

ただし、i=128:255 からは、char と unsigned char をキャストできません。または、unsigned char は [0:256] の値を保存し、char は間隔 [-128:127] の値を保存するため、出力が異なります。 ]))。それにもかかわらず、この後半の動作は無関係です。C/C++ では一般に、128 の異なる値と他の 128 の値 (文字の正または負符号なし文字の場合) は使用されません。

文字を表さない char に値を入れたり、文字を表さない unsigned char に値を入れたりしなければ、すべて問題ありません。

追加: C/C++ の文字列で UTF-8 またはその他のエンコーディング (特殊文字用) を使用する場合でも、この種のキャストを使用するものはすべて問題ありません。たとえば、UTF-8 エンコーディング (参照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);

そのコードの出力は次のようになります:
ハート (♥)
ダイヤモンド (♦)
クラブ (♣)
スペード (♠)

各文字を符号なし文字にキャストした場合でも。

それで:

  • 「unsigned char * をこの関数にいつでも安全に渡すことができますか?」はい!

  • 「情報を失うことなく、自由に char と unsigned char の間で安全に変換 (キャスト) できることが保証されていますか?」はい!

  • 「情報を失うことなく、char へのポインターと unsigned char の間で自由に安全に変換 (キャスト) できますか?」はい!

  • 「答えは C と C++ で同じですか?」はい!

于 2013-03-06T18:24:28.087 に答える
3

意味的には、との間の受け渡しは安全であり、C++ のようにそれらの間でキャストしても安全です。 unsigned char *char *

ただし、次のサンプル コードを検討してください。

#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();
}

出力:

0
255
-1
-1

process_unsigned内部のすべてのコードprocessはまったく同じです。唯一の違いは、署名なしと署名済みです。このサンプルは、ブラック ボックス内のコードがSIGNの影響を受け、呼び出し先と呼び出し元の間で何も保証されないことを示しています。

したがって、合格のみに適用できますが、他の可能性は保証されていません。

于 2013-03-05T06:13:22.790 に答える
2

別の種類の へのポインターを渡すことができcharますが、明示的にキャストする必要がある場合があります。ポインターは、同じサイズと同じ値であることが保証されています。変換中に情報が失われることはありません。

関数内に変換する場合charは、変数に値を代入するか、値を にキャストするだけです。unsigned charcharunsigned charcharunsigned char

unsigned charデータを失わずに変換する必要がある場合はchar、少し難しくなりますが、それでも可能です。

#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 charします。charunsigned char

于 2013-02-25T23:53:36.417 に答える
1

process()符号なし文字を安全に渡すことができるかどうかを知るには、実際にコードを表示する必要があります。関数が文字を配列へのインデックスとして使用する場合、いいえ、符号なしデータを使用することはできません。

于 2013-03-02T09:06:16.043 に答える