3

見ながら:

C コンパイラは、構造体の最初の要素の前にパディングを追加できますか?

次のコードを思いつきました:
(この例ではメモリが解放されていないという事実は無視してください)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char *cstr;
    size_t len;
} str_t;

void setStr(str_t* dest, const char* src)
{
    size_t len = strlen(src);
    dest->cstr = malloc(len + 1);
    dest->len = len;
    memcpy(dest->cstr, src, len + 1);
}

int main(void)
{
    str_t str;
    setStr(&str, "woot!");
    printf("%s\n", str);
    return 0;
}

驚くべきことに、これは実際に機能します。この呼び出し:

printf("%s\n", str);

これと同等のようです:

printf("%s\n", str.cstr);

したがって、次のことも可能であると考えられます。

char* plainstr = malloc(str.len + 1);
strcpy(plainstr, str);

しかし、行きません。とは対照的にprintfstrcpyは可変引数ではないため、型チェックがあります。コンパイラは正当に不平を言います:

passing 'str_t' to parameter of incompatible type 'const char *'

しかし、それをキャストすることによって、コンパイラーに「私は本当にそれを意味します」と伝えようとしています:

strcpy(plainstr, (const char*)str);

どちらも機能しません:

operand of type 'str_t' where arithmetic or pointer type is required

以下は機能しないことに注意してください。

strcpy(plainstr, (const char*)&str);

以来str.cstr != &str。たとえば、この出力:

printf("%p %p\n", str.cstr, &str);

次のとおりです。

0xbdb010 0x7fff788f6ab8

実際、ガベージ データが にコピーされていplainstrます。

質問は次のとおりです。

  1. 構造体をポインター型にキャストできないのはなぜですか?
  2. キャストが許可されていprintfない場合、これを正しく処理するのはなぜですか?
4

1 に答える 1

2

構造体をポインタ型にキャストできないのはなぜですか?

意味がないからです。簡潔なメモリアドレスとして、異なるタイプのおそらく無関係な情報の全体をどのように再解釈しますか? ただし、あなたが尋ねた前の質問では、回答したすべての人がC標準を引用し、標準の特定のステートメントの1つは次のように述べています

構造体のアドレスは、その最初の要素のアドレスです

したがって(@Matがすでに指摘しているように)、実際に書くことができます

strcpy(destination, *(const char **)&str);

そして、それは私が列挙した理由から「うまくいく」でしょう。

キャストが許可されていない場合、printf がこれを正しく処理するのはなぜですか?

C では、型キャストは多くの場合、コンパイラをだますためだけに行われるためです (そうでない場合を除く)。構造体を渡すと、構造体がコピーされ、スタックは次のようになります (簡単にするために、構造体からパディングを意図的に省略しています)。

> top of the stack: pointer to the format string
> address of the copied struct *and*  address of the copy of the char pointer
> address of the length of the string (size_t)
> every other stuff

だから、今何printf()をするかは次のとおりです。

  • スタックから最初の値を取り出します。フォーマット文字列になります。
  • フォーマット文字列でフォーマット指定子に遭遇すると、%s別の char ポインターをポップします。実際には、それは構造体へのポインターであり、出力される文字列である最初の要素へのポインターです。
  • したがって、文字列を喜んで出力して返します。

また、これは機能するという事実にもかかわらず、まだ未定義の動作printf()です-可変引数として渡す型に実際に対応するフォーマット文字列を指定しない場合、それは準拠しておらず、何かが起こると予想できます。

于 2012-11-03T15:58:03.810 に答える