【问题标题】:correct use of malloc in function with passed uint32_t array pointer通过传递的 uint32_t 数组指针在函数中正确使用 malloc
【发布时间】:2015-05-08 03:12:21
【问题描述】:

我在读取包含 4 字节无符号整数的二进制文件、释放传递的数组引用、将其重新分配到新大小然后尝试访问数组成员的函数中使用 malloc 时遇到困难。我认为问题出在 uint32_t 类型,因为该数组似乎被视为 8 字节整数数组,而不是 4 字节整数数组。不完全确定我哪里出错了,可能是使用 malloc,IE 也许我需要指示它以与我正在做的不同的方式创建 uint32_t 类型,或者它可能是其他的东西。代码:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <malloc.h>

int filecheck (uint32_t **sched, int * count) {
    int v, size = 0;
    FILE *f = fopen("/home/pi/schedule/default", "rb");
    if (f == NULL) { 
        return 0; 
    } 
    fseek(f, 0, SEEK_END);
    size = ftell(f);
    fseek(f, 0, SEEK_SET);
    int schedsize = sizeof(uint32_t);
    int elementcount = size / schedsize;
    free(*sched);
    *sched = malloc(size);
    if (elementcount != fread(sched, schedsize, elementcount, f)) { 
        free(*sched);
        return 0;
    } 
    fclose(f);
// This works correctly and prints data as expected
    for (v=0;v<elementcount;v++) { 
        printf("Method 1 %02d %u \n", v, ((uint32_t*)sched)[v]);
    }

// This skips every other byte byt does not print any numbers > 32 bit unsigned
    for (v=0;v<elementcount;v++) { 
        printf("Method 2 %02d %u \n", v, sched[v]);
    }
// This treats the binary file as if it was 64 bit uints, printing 64 bit numbers
    for (v=0;v<elementcount;v++) { 
        printf("Method 3 %02d %lu \n", v, sched[v]);
    }
    *count = elementcount;
    return 1;
}

int main (){
    uint32_t *sched = NULL;
    int i, count = 0;
    if (filecheck(&sched, &count)) {
        for (i=0;i<count;i++) { // At the next line I get a segmentation fault
            printf("Method 4 %02d %u\n", i, sched[i]);
        }
    } else {
        printf("Error\n");
    }
    return 0;
}

我按要求添加了文件读取代码。尝试以我正在做的方式访问 main() 中的数组 sched 将打印我正在读取的数据,就好像它是 8 字节整数一样。所以我猜 sched 被视为一个 64 位整数数组,而不是我定义的 32 位数组。我想这是因为我释放了它并再次在它上面使用了 malloc。但我相信我指示 malloc 它应该创建的类型是 uint32_t 所以我很困惑为什么数据没有被这样对待。

编辑:找到了一种让它工作的方法,但不确定它是否正确(方法 1)。这是将此数组视为 uint32_t 类型的唯一且最干净的方法吗?当然,编译器应该知道我在处理什么类型,而不必每次使用它时都强制转换。

编辑:实际上,当我尝试在 main 中访问它时,我遇到了段错误。我在代码中添加了一条注释来反映这一点。我之前没有注意到这一点,因为我在几个地方使用了 for print 循环来跟踪编译器如何查看数据。

编辑:不确定是否可以添加二进制数据文件。我是在十六进制编辑器中手工制作的,它的确切内容并不那么重要。在 bin 文件中,FF FF FF FF FF FF FF FF 应该被读取为 2 个 uint32_t 类型,而不是 1 个 64 位整数。 Fread AFAIK 不关心这一点,它只是填充缓冲区,但如果那是错误的,则需要更正。

TIA,皮特

【问题讨论】:

  • 您询问您阅读的数据,但没有说明您是如何阅读的,以及为什么它会以这种方式显示,而没有说明输出是什么。除了强制转换 malloc (which you shouldn't do in C),您的分配调用对于存储 10x uint32_t 来说似乎是正确的。唯一的问题是 printf 说明符,当使用 @987654326 时,它应该通过 inttypes.h 中的适当常量连接形成@ 但是 如何 你正在加载你的数组,而 what 你正在加载它是全部在你身上。
  • 要正确打印uint32_t,请执行以下操作:printf("%"PRIu32"\n", x);(当然,这需要inttypes.h)。
  • 抱歉 - 我将开始削减文件读取位,这将填补我认为的空白。但切入正题,只是数据打印不正确,索引也不正确。我玩过很多 printf 说明符,虽然有些创建的结果与其他说明不同,但它们要么每隔一个字节跳过,要么将数据视为 64 位字节。数组的索引是错误的。该指针被视为指向 64 位类型的指针,而不是 32 位类型。
  • @Pete printf("%zu\n", sizeof(uint32_t)); 说什么?
  • @WhozCraig 感谢演员 malloc,已阅读该链接并将进行相应调整。

标签: c malloc uint32-t


【解决方案1】:
// This works correctly and prints data as expected
    for (v=0;v<elementcount;v++) { 
        printf("Method 1 %02d %u \n", v, ((uint32_t*)sched)[v]);
    }

这不太对,因为sched 是uint32_t**,而你分配给*sched。而且还因为%u 不是您应该打印uint32_t 的方式。 首先取消引用sched,然后应用数组索引访问。

你想要printf("%02d %"PRIu32"\n", v, (*sched)[v]));

// This skips every other byte byt does not print any numbers > 32 bit unsigned
    for (v=0;v<elementcount;v++) { 
        printf("Method 2 %02d %u \n", v, sched[v]);
    }

是的,那是因为 sched[v] 是 uint32_t*,并且由于它是指针类型并且您可能在 64 位机器上运行,它可能是 64 位...所以您使用 8- 进行迭代字节增量而不是 4 字节增量,并尝试将指针打印为 %u。 8*10 比 4*10 大,也会越界,很可能会导致分段错误。

// This treats the binary file as if it was 64 bit uints, printing 64 bit numbers
    for (v=0;v<elementcount;v++) { 
        printf("Method 2 %02d %lu \n", v, sched[v]);
    }

这类似于第二个示例,只是您使用稍微更合适但仍然不正确的%lu 打印。

您还需要将fread() 改为*sched 而不是sched,否则会导致UB,这很可能会在您尝试读取数组时导致分段错误 .

另外,你在 main 中的 for 循环永远不应该运行,因为你没有将 count 设置为 filecheck 中的任何内容,所以它应该仍然为零(至少在没有 UB 的情况下)。

其他cmets:

  • 正如您刚刚了解到的,不要将 malloc() 的结果转换为 C。
  • main() 应该返回一个值。 0 如果一切顺利的话。
  • 当然,您可以使用fwrite() 将一些uint32_t 以字节形式写入文件。
  • 查看malloc()的结果,看是否成功。
  • 将size_t 用于size_t 值而不是int,并相应地打印(%zu)。

【讨论】:

  • 啊!正在考虑您的一些建议,但仍然无法正常工作,但随后按照您的说法进入 *sched,然后它就组合在一起了。在阅读了其他一些帖子和教程之后,我想我也理解得很好。您能否扩展您所做的最后一条评论,重新使用 size_t?另外,包含 inttypes.h 是 printf 这些 uint32_t 的唯一方法吗?谢谢,皮特
  • @Pete 是的,据我所知,包括inttypes.h 是打印uint32_t 和co 的唯一便携式方式。像fread() 这样的东西取并返回size_t,这是一种特殊的无符号整数类型,保证能够保持任何合法大小。虽然在这种情况下,这没什么大不了的,但如果您正在处理非常大的数组,这可能会让您感到困惑。隐式(自动)将size_t 转换为int 或从int 转换,如果您没有意识到它正在发生,也可能会引起您的注意。同样,ftell 返回 long - 如果您将结果存储为 int,则可能会遇到另一个问题。
  • @Pete 基本上,只要知道你正在使用的 stdlib/system 调用预期或返回的类型,总有一天你可能会避免一个可怕的错误。这对于编写将在不同类型大小的平台上运行的可移植代码尤其重要。如果您认为我的回答有帮助,那么 SO 的工作方式是考虑 accepting it
  • 绝对是 Iskar,现在接受了,也非常感谢。还有关于尺寸的额外信息,关于 ftell 等 - 我真的不知道这一点,它指向我需要做的阅读,以确保这个简单的小程序没有错误,它需要作为它的在低功耗硬件上长时间运行。很好的答案,让我再次开车,加上额外的信息。如果可以的话,我会加一。
猜你喜欢
  • 2010-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-16
  • 2021-02-24
相关资源
最近更新 更多