【问题标题】:Conversion of array of characters to struct type将字符数组转换为结构类型
【发布时间】:2020-12-29 18:02:26
【问题描述】:

我想了解如何将 char 数组转换为 struct 类型。我做了以下事情:

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

typedef struct {

   int a;
   int b;
   int c;

} test;

int main()
{
    char data[20];

    strcpy(data, "text");

    test *ptr = (test*)data;

    return 0;
}

为了尝试了解正在发生的事情,我添加了以下几行:

如果我添加行“printf(“%s”, ptr)”,尽管有警告,程序输出是“文本”。

接下来,如果在该行之前我初始化了一个字段,比如 ptr->a = 1,那么前一个 printf 的输出将是一些奇怪的字符。

我猜在转换之后,-data-指针指向的内存被扩展为保存结构字段。我的问题是在转换后尝试访问数据。

那么,我的第一个问题是,当上述转换发生时,内存中发生了什么?

另外,我怎样才能从 -ptr- 指针中取回原始数据?

【问题讨论】:

  • Nothing whatsoever is happening in this program。没有可观察到的行为,一个好的编译器会将其全部转换为无操作。修改后,坏事可能会发生,也可能不会发生。如果您想讨论修改后的程序,请发布修改后的程序,而不是要应用的修改列表。
  • C 标准规定:A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.

标签: c


【解决方案1】:

首先,不要这样做。

其次,当您访问一个对象时,您使用的类型会告诉编译器(或其他 C 实现)如何解释内存中的字节。例如,如果xint x; 声明,那么当您在3*x + 4 等表达式中使用x 时,它会告诉编译器从内存中读取x 的字节并将它们解释为int.

test *ptr = (test *) data; 中,您告诉编译器将指向data[0] 的指针(因为data 自动转换为其第一个元素的地址&amp;data[0])更改为指向test 结构的指针。如果这有效(见下文),那么ptr 指向内存中的相同字节,但是,当您在诸如*ptrptr-&gt;a 之类的表达式中使用ptr 时,您是在告诉编译器解释这些字节就好像它们是 test 结构一样(ptr-&gt;a 告诉编译器进入该结构,获取成员 a 的字节,并将它们解释为好像它们是 int 的字节)。内存中的字节不会改变。所有这些变化都是编译器如何解释它们。我们将在下面看看它是如何工作的。首先,让我们看看为什么不应该这样做的三个原因。

第一,当您将char * 类型的指针转​​换为test * 类型的指针时,C 标准仅保证对齐 正确时才会起作用。对齐是对对象可以在内存中开始的地址的限制。 char 的数组可以从任何位置开始,因此您的 data 数组可以有任何地址。但是,在许多 C 实现中,int 必须以四个字节的倍数开始,这将强制test 结构至少具有该对齐要求。这意味着,如果data 不是从四个字节的倍数开始,C 标准不保证(test *) data 会产生有意义的结果或不会陷入陷阱。

第二,尽管 C 保证如果对齐正常,转换将产生具有某种意义的结果,但它唯一保证该结果是可以转换回原始类型并用于访问具有该结果的数据原始类型。它不保证生成的test * 类型的指针的行为类似于指向内存中相同位置的指针。 (这是一般指针转换的规则。有一些具体的转换有进一步的保证。例如,任何指向对象的指针都可以转换为指向char的指针,并且保证结果指向第一个对象的字节。)

三,C 只保证通过某些类型完成的访问对象将起作用。如果一个对象是用一种类型定义的,例如char 的数组,并通过另一种类型访问,例如int,C 标准不保证程序完全可以工作。在很大程度上,对象只能作为其原始类型或相关的兼容类型访问,但也有一些例外。一个例外是可以通过字符类型访问任何对象的字节。 (所以你可以从intchar,但不能从charint。)

那么,如果您想探索当您将data 的字节重新解释为test 时会发生什么,您应该怎么做呢?正确的方法是将字节复制到test 对象中,可以这样完成:

test x;
memcpy(&x, data, sizeof x);

然后你可以打印x.ax.bx.c,看看它们的值是什么。

如果您的 C 实现使用四字节 int,就像许多人一样,那么 x.a 将包含复制到的字符串中的字节。那些将是带有“t”、“ e”、“x”和“t”。您为 x.a 获得的值将取决于这些代码是什么(许多 C 实现使用 ASCII 代码)以及 C 实现对 int 中的字节使用的顺序。

假设您的 C 实现没有在成员 ab 之间插入任何填充,这很可能,那么 x.b 的第一个字节将为零。但是,b 中的剩余字节和c 中的字节将是indeterminate,因为它们是从data 中的字节复制而来的,而这些字节从未被赋予任何值。 “Indeterminate”是 C 标准中的一个特殊词,表示字节可能根本不包含固定值;每次访问它们时,它们可能会有所不同。在实践中,C 实现通常会使用内存中为数组data 选择的位置发生的任何值。但是,编译器的积极优化可能会产生其他结果。

另外,我怎样才能从 -ptr- 指针中取回原始数据?

您可以将指针转换回来:

char *p = (char *) ptr;

然后p 可用于访问字节作为它们的原始char 类型,p[0]p[1] 等等。

【讨论】:

  • 感谢您的详细回答。我可能应该解释我为什么要这样做。原因是我正在学习网络编程,我想使用结构创建一个 tcp_header,然后将数据与 header 一起发送..
  • @Toni:一般来说,要构建一个网络数据包,创建一个unsigned char 的数组并将数据复制到其中(与memcpy 一样)。可以使用适当制作、可能打包的数据结构来完成此操作,但这取决于 C 实现。
猜你喜欢
  • 2021-12-14
  • 2020-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
  • 1970-01-01
  • 2019-02-09
  • 1970-01-01
相关资源
最近更新 更多