C中不同数据类型的连续内存分配?

时间:2013-04-14 03:35:33

标签: c memory-management

我正在尝试编写一个包含固定的14个起始字符并以不同内容结尾的字符串(char数组)。变化位包含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的第一个/最后一个字节(取决于endian)?或者有什么其他特殊结构可以躲避我吗?

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

P.S。在我写这篇文章时,我提出了一个想法。我是否可以只使用memcpy();)memcpy具有void *类型的参数,希望它可以工作!我现在要试试吧!无论如何,所有冰雹stackOverflow!

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

3 个答案:

答案 0 :(得分:1)

您的问题中涉及的各种问题。我会解决其中的一些问题。

  • 结构中成员的顺序保证与您声明它们的顺序相同。但这里有一个不同的问题 - 填充。 检查一下 - 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&amp; comStr.temperature是指针。您无法直接为它们指定值。您需要为它们提供现有浮点数的地址,或者动态分配它们可以指向的内存。

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

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

答案 1 :(得分: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字节对齐的。

答案 2 :(得分:0)

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

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

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