【问题标题】:Continuous memory allocation with different data type in C?C中不同数据类型的连续内存分配?
【发布时间】:2013-04-14 03:35:32
【问题描述】:

我正在尝试编写一个字符串(完全是 char 数组),其中包含固定的 14 个起始字符并以不同的内容结尾。可变位包含 2 个浮点数和 1 个 32 位整数,它们在数组中被单独视为 4 个 1 字节字符,以逗号分隔。它可以通过以下代码来说明,由于某些明显的原因(*char 不能分配给 *float),它无法编译。那么,我该怎么做才能绕过它呢?

char *const comStr = "AT+UCAST:0000=0760,0020,0001\r"; // command string

float *pressure;
float *temperature;
uint32_t *timeStamp;

pressure = comStr + 14; // pressure in the address following the '=' in command string

temperature = comStr + 18; // temperature in the address following the 1st ',' in command string

timeStamp = comStr + 22; // time stamp in the address following the 2nd ',' in command string

我对 C 语言中的 struct 和 union 之类的东西有一个不清楚的记忆,它严格保留了在“结构”中定义变量的内存分配顺序。也许是这样的:

typedef struct
{
  char[14] command;
  float *pressure;
  char comma1;
  float *temperature;
  char comma2;
  uint32_t *time_stamp;
  char CR;
}comStr;

这个结构能保证 comStr-> command[15] 给我*pressure的第一个/最后一个字节(取决于字节序)吗?还是有什么其他特殊的结构能瞒得过我?

(注意:comStr-> command[15] 不会在以后的代码中进行评估,因此超出索引边界在这里不是问题。这里唯一重要的是内存是否连续分配,以便从内存地址开始持续 29 个字节的硬件获取(comStr-> 命令)给了我我想要的字符串)。

附言在写这篇文章的时候,我想到了一个主意。我可以只使用 memcpy() 吗?) memcpy 有 void* 类型的参数,希望它能工作!我现在要试试!无论如何,所有冰雹stackOverflow!

编辑:我应该让自己更清楚,对于任何误导和误解,我深表歉意!我要构造的字符数组是通过UART逐字节发送的。为此,如果将字符数组的起始存储器地址和长度提供给 DMA 系统,则将使用 DMA 系统将数组自动逐字节传输到发送缓冲区。所以字符数组必须连续存储在内存中。我希望这能让问题更清楚。

【问题讨论】:

  • 这可能会导致字符串包含不可打印的字符和/或中间被空字符截断。你确定要这个吗?
  • 至 n.m.是的。这是一个用于传输的字符串,在传输完比特后,不可打印的“浮点”字符将再次被解释为浮点数并给出有意义的数据;)非常感谢您的通知;)
  • 如果字符串是用于传输而不是显示给人类,那么逗号和CR是什么意思?
  • comma1, comma2 只是为稍后创建这样的结构时插入实际的','字符保留的内存地址。 CR 与为 '\r' 字符(回车)保留的想法相同。
  • 需要这些实际的逗号字符...为什么?

标签: c memory-management


【解决方案1】:

这个提议的结构:

typedef struct
{
  char[14] command;
  float *pressure;
  char comma;
  float *temperature;
  char comma;
  uint32_t *time_stamp;
  char CR;
}comStr;

不会帮助您满足您的要求:

这里唯一重要的是内存是否连续分配,以便从内存地址 (comStr->command) 开始持续 29 个字节的硬件获取正好给我我想要的字符串。

请注意,您不能有两个同名的成员;例如,您需要使用 comma1comma2。另外,数组维度放错地方了。

一个问题是结构中会有填充字节。

另一个问题是指针将保存结构外部的地址(因为结构内部没有任何有效的东西可供它们指向)。

不清楚你在追求什么。一个字符串中的 4 个字节只能表示非常有限的浮点值范围。如果您追求二进制数据 I/O,那么您可以删除指针和逗号:

typedef struct
{
  char     command[14];
  float    pressure;
  float    temperature;
  uint32_t time_stamp;
}comStr;

如果您希望逗号出现,那么您将不得不更加努力:

typedef struct
{
  char     command[14];
  char     pressure[4];
  char     comma1;
  char     temperature[4];
  char     comma2;
  char     time_stamp[4];
  char     CR;
} comStr;

您必须小心加载数据:

struct comStr com;
float         pressure = ...;
float         temperature = ...;
uint32_t      time_stamp = ...;

assert(sizeof(float) == 4);
...
memmove(&com.pressure, &pressure, sizeof(pressure));
memmove(&com.temperature, &temperature, sizeof(temperature));
memmove(&com.time_stamp, &time_stamp, sizeof(time_stamp));

您必须使用一组类似的内存副本解压。请注意,您将无法对结构使用简单的字符串操作;在结构的任何或所有 pressuretemperaturetime_stamp 部分中可能有零字节。


结构填充

#include <stddef.h>
#include <stdio.h>
#include <stdint.h>

typedef struct
{
  char      command[14];
  float    *pressure;
  char      comma1;
  float    *temperature;
  char      comma2;
  uint32_t *time_stamp;
  char      CR;
} comStr;

int main(void)
{
    static const struct
    {
        char    *name;
        size_t   offset;
    } offsets[] =
    {
        { "command",     offsetof(comStr, command)     },
        { "pressure",    offsetof(comStr, pressure)    },
        { "comma1",      offsetof(comStr, comma1)      },
        { "temperature", offsetof(comStr, temperature) },
        { "comma2",      offsetof(comStr, comma2)      },
        { "time_stamp",  offsetof(comStr, time_stamp)  },
        { "CR",          offsetof(comStr, CR)          },
    };
    enum { NUM_OFFSETS = sizeof(offsets)/sizeof(offsets[0]) };

    printf("Size of comStr = %zu\n", sizeof(comStr));
    for (int i = 0; i < NUM_OFFSETS; i++)
        printf("%-12s  %2zu\n", offsets[i].name, offsets[i].offset);

    return 0;
}

Mac OS X 上的输出:

Size of comStr = 64
command        0
pressure      16
comma1        24
temperature   32
comma2        40
time_stamp    48
CR            56

请注意该结构在 64 位机器上的大小。每个指针都是 8 字节,并且是 8 字节对齐的。

【讨论】:

  • 4 字节字符串为我提供了 32 位浮点数的空间,它有 23(+1) 位的有效位。浮点数表示一个 16 位整数除以另一个所得的商,这两个整数的范围很近。因此它具有足够的精度。
【解决方案2】:

您的问题要涵盖的各种问题。我将对其中一些问题进行一下尝试。

  • 保证结构中成员的顺序与您声明它们的顺序相同。但是这里有一个不同的问题 - 填充。 检查这个 -http://c-faq.com/struct/padding.html 并关注那里的其他链接/问题
  • 接下来你错误地认为像“125”这样的东西是一个整数,或者像“1.25”这样的东西是一个浮点数——它不是——它是一个字符串。即

    char * p = "125";
    

    p[0] 将不包含 0。它将包含 '0' - 如果编码是 ASCII,那么这将是 48。即 p[0] 将包含 48 而不是 0。p[1] 将包含 49 & p[2] 将包含 52。它与 float 类似。

相反的情况也会发生。 即,如果您有一个地址并将其视为 char 数组 - char 数组将不包含您认为的浮点数。 试试这个程序看看这个

#include <stdio.h>

struct A
{
    char c[4];
    float * p;
    int i;
};

int main()
{
    float x = 1.25;
    struct A a;
    a.p = &x;
    a.i = 0; // to make sure the 'presumed' string starting at p gets null terminate after the float
    printf("%s\n", &a.c[4]); 
}

对我来说,它打印“╪·↓”。这与字节顺序无关。

  • 在为结构对象赋值时需要记住的另一件事 - 您需要记住 comStr.pressure 和 comStr.temperature 是指针。您不能直接为它们赋值。您需要为它们提供现有浮点数的地址,或者动态分配它们可以指向的内存。

您还尝试创建 char 数组或解析已经存在的 char 数组。如果您尝试创建它,更好的方法是使用snprintf 来做您想做的事。 snprintf 使用类似于 printf 的格式说明符,但打印到 char 数组。您可以通过这种方式创建您的 char 数组。还有一个更大的问题——你打算用你创建的这个 char 数组做什么——这将决定字节顺序是否与你相关。

如果您尝试从给定的 char 数组中读取并尝试拆分为浮点数和逗号等,那么执行此操作的一种方法是sscanf,但对于您的特定字符串格式可能会很困难。

【讨论】:

  • 第二点我很清楚。我应该说得更清楚。我确实希望将浮点数与确切的位一样形成 char 数组,因此在将这些位传输到另一个设备之后,我可以提取这些位并将它们解释为浮点数。填充物我刚看了看,是的,它是颈部疼痛。但我认为乔纳森通过删除逗号并仔细对齐结构给出了一个“解决方案”。对于第三点,我没有意识到这种副作用是我的错。我想我可以放下指针并在 struct 中使用实际的浮点数来避免它。
  • 另外,'snprintf' 是个好主意。但是此函数使用 ASCII 字符表示位,如果需要超过 4 个精度数字,则本质上需要更多的内存地址。结果,这增加了字符数组长度并降低了传输效率。
【解决方案3】:

最后,我找到了一个简单的方法,但我不知道这种方法是否有任何缺点。我做到了:

char commandStr[27];
char *commandHeader = "AT+UCAST:0000=";
float pressure = 760.0;
float temperature = 20.0;
uint32_t timeStamp = 0;

memcpy(commandStr, commandHeader, 14);
commandStr[26] = '\r';

memcpy((void*)(comStr+14), (void*)(&pressure), 4);
memcpy((void*)(comStr+18), (void*)(&temperature), 4);
memcpy((void*)(comStr+22), (void*)(&timeStamp), 4);

此代码是否存在任何安全问题或性能问题或其他问题?

【讨论】:

  • 您不需要 void * 演员表。您可能更喜欢使用sizeof() 代替文字4,但是您还需要考虑偏移量(如果大小不是4)。 C 标准中没有任何内容说“sizeof(float) == 4, but I don't know of a machine where that is not true (at least, not one where CHAR_BIT == 8”;如果更大,那么所有规则都会改变)。否则,没关系。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2016-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多