将结构转换为指向其第一个元素的指针类型

时间:2012-11-03 15:46:26

标签: c pointers casting struct

看着:

Can a C compiler add padding before the first element in a structure?

我想出了以下代码:
(忽略在此示例中未释放内存的事实。)

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

但是没有去。与printf相比,strcpy不是可变参数,因此存在类型检查。编译器正确地抱怨:

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如何正确处理?

1 个答案:

答案 0 :(得分:2)

  

为什么不允许将结构转换为指针类型?

因为没有意义。您如何将一大堆可能不相关的不同类型的信息重新解释为简洁的内存地址?但是,在您之前提出的问题中,所有回答的人cited the C standard以及标准中的一个特定声明均表示

  

结构的地址是其第一个元素的地址

所以(正如@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格式说明符时,它会弹出另一个字符指针 - 实际上,它是指向结构的指针,以及指向第一个元素的指针,即字符串打印。
  • 所以它很乐意打印字符串并返回。

此外,这是仍未定义的行为,尽管它有效 - 如果你没有指定printf()的格式字符串,它实际上对应于你传入的类型它的可变参数,这是不符合的,你可以期待任何事情发生。