【问题标题】:"memset"ting a struct causes printf to print struct member longer than its limit"memset"ting 结构导致 printf 打印结构成员的时间超过其限制
【发布时间】:2015-12-25 13:23:39
【问题描述】:

我在一个非常大的 C 程序中观察到了这个问题。

结构声明为:

struct kc910InputParms {
    char inStream[71];
}

在程序中,当对该结构变量进行memset 操作时,printf 语句打印的时间超过其限制(即 71 个字符是限制)。请参阅下面在 gdb 中调试时获得的输出。

(gdb) s
cpyInParmsToCbl (in=0xffffb9f6, out=0x80ef320) at kc190Common.c:456
456       memset((void*)out, ' ', sizeof(kc910InputParms));
(gdb) printf "<%s>\n", out.inStream
<>
(gdb) n
458       strncpy(out->inStream+16, in->system_id,strlen(in->system_id));
(gdb) printf "<%s>\n", out.inStream
<                                                                       Pending>

请注意上面:附加了 7 个额外字符作为“待处理”,这将打印 out.inStream 最多 78 个字符。这在程序的后续阶段引入了缺陷。

有人可以帮我理解这种行为以及使用memset 的正确方法吗(如果我做错了)?

如果发生这种情况是因为变量out.inStream 没有被空字符'\0' 终止,那么我的问题是:为什么它在memset 之前没有以相同的方式打印?

【问题讨论】:

  • 您确定您使用的是 C 编译器而不是 C++?
  • 要重置 out 指向的内存,只需执行:memset((void*)out, '\0', sizeof(kc910InputParms))。注意空格字符“”和“\0”是两个不同的东西。
  • @alk:是的,我很确定,我使用的是 C 编译器 :)
  • @SelçukCihan:我明白,但我没有在memset 中使用\0,因为在程序中,inStream 必须完全使用(70 个字符)。该程序是按照这种方式构建的以处理逻辑。
  • str*() 系列函数需要 C-"strings",即:0-terminated char-arrays。

标签: c struct gdb memset


【解决方案1】:

这是由于缺少终止nul。它之前工作的原因是由于编译器将内存区域设置为nuls,或者只是随机运气。

您还告诉strncpy,只有与字符串中的字符一样多的字节才有空间,因此它不会添加终止符。这是错误的做法。你应该告诉strncpydestination 中有多少字节可用,而不是source 中有多少字符。在这种情况下 54 (70-16)。对于源长度超过 54 个字符的情况,您应该将最后一个字节显式设置为 nul。

printf 和其他 C 字符串函数对数组大小或内存分配一无所知,它们通过终止 nul。因此,如果您没有它,它们将继续运行,直到找到一个,可能会耗尽分配的内存空间并导致未定义的行为。这就是为什么您会在输出中获得超过 71 个字符的原因。

【讨论】:

  • 我理解您在strncpy 上的观点,但在帖子中,strncpy 根本没有执行。 458 行只是为了告诉用户 this 行将在用户点击n 时立即执行。但是我没有点击n,而是使用printf 来知道memset 之后的值。那么,回到你的第一段,你的意思是说printf 可以在inStream 的内存空间之外打印超过71 个字符?
  • @Rohit printf 将打印,直到遇到 nul。它对数组大小或任何东西一无所知。
  • 感谢您提醒我这一点。这是有道理的。请更新您的答案以包含这部分,因为我的问题主要集中在printf :)
猜你喜欢
  • 1970-01-01
  • 2021-11-04
  • 2011-12-19
  • 2013-06-19
  • 1970-01-01
  • 1970-01-01
  • 2012-08-30
  • 1970-01-01
  • 2016-09-24
相关资源
最近更新 更多