如果在C中提供缓冲区大小,我怎么知道剩下多少以及何时需要停止使用内存?
例如,如果我写的函数是这样的:
void ascii_morse (lookuptable *table, char* morse, char* ascii, int morse_size) {
}
在这个应用程序中,我将传递一个字符串(ascii),我将使用其他函数将其转换为morse,将每个ascii char转换为morse。我面临的问题是如何确保我没有超过缓冲区大小。我甚至不知道何时使用缓冲区大小或我每次使用它时如何减少它。
当然输出将是莫尔斯(所以我将添加字符串给莫尔斯,但我想我知道如何做到这一点,它只是缓冲区大小是我很难理解的)
如果您需要更多信息来了解问题,请告诉我,我尽力解释它。
答案 0 :(得分:3)
听起来有一些关于“缓冲”的混淆。没有缓冲区。 morse-size
告诉你已经为morse
分配了多少内存(技术上,morse
指向的内存块)。如果莫尔斯大小是20,那么你有20个字节。这是19个字节的可用空间,因为字符串由空字节终止。您可以将morse-size
视为“字符串的最大长度加一”。
您需要检查morse-size
以确保您没有在morse
中写入比它能容纳的更多字节。 morse
只不过是一个指向内存中单个点的数字。不是范围,而是一个点。之后分配给morse
的内容是什么。如果你将更多内容放入morse
,你就有可能覆盖别人的记忆。 C不会为您检查这个,这是最高性能的价格。
如果你去剧院并且迎来者告诉你,“你可以拥有座位A3和下一个5”,然后离开。你必须要有礼貌,不要占用6个席位,其他人则获得A8。
valgrind等工具对于发现C中的记忆错误并保持理智非常宝贵。
C中不是字符串吗?欢迎来到整个计算世界中导致错误的最大根本原因。
答案 1 :(得分:2)
您需要将缓冲区大小与指针一起传递。
int
ascii_to_morse(lookuptable *table,
char* morse, int morse_size,
char* ascii);
缓冲区大小不一定与字符串的当前长度相同(使用strlen可以找到)。
上面给出的函数将读取ascii字符串(不需要知道缓冲区大小,因此不会传递)并写入morse指向的缓冲区,大小为morse_size。它返回写入的字节数(不计算空值)。
编辑:以下是此函数的一个实现,虽然它无法使用正确的morse代码值,但它显示了如何管理缓冲区:
typedef void lookuptable; // we ignore this parameter below anyway
// but using void lets us compile the code
int
ascii_to_morse(lookuptable *table,
char* morse, int morse_size,
char* ascii)
{
if (!ascii || !morse || morse_size < 1) { // check preconditions
return 0; // and handle it as appropriate
// you may wish to do something else if morse is null
// such as calculate the needed size
}
int remaining_size = morse_size;
while (*ascii) { // false when *ascii == '\0'
char* mc_for_letter = ".-"; //BUG: wrong morse code value
++ascii;
int len = strlen(mc_for_letter);
if (remaining_size <= len) { // not enough room
// 'or equal' because we must write a '\0' still
break;
}
strcpy(morse, mc_for_letter);
morse += len; // keep morse always pointing at the next location to write
remaining_size -= len;
}
*morse = '\0';
return morse_size - remaining_size;
}
// test the above function:
int main() {
char buf[10];
printf("%d \"%s\"\n", ascii_to_morse(0, buf, sizeof buf, "aaa"), buf);
printf("%d \"%s\"\n", ascii_to_morse(0, buf, sizeof buf, "a"), buf);
printf("%d \"%s\"\n", ascii_to_morse(0, buf, sizeof buf, "aaaaa"), buf);
return 0;
}
答案 2 :(得分:2)
void ascii-morse (lookuptable *table, char* morse, char* ascii, int morse-size)
通过上面原型的外观,您已经传入了输出缓冲区的大小。
ascii
无疑将是一个以空字符结尾的字符串,morse
将成为输出缓冲区:morse_size
(不 morse-size
因为你有它,因为那不是一个有效的标识符)将允许你写多少个字符。
伪代码将类似于:
set apointer to start of ascii, mpointer to start of morse.
while apointer not at end of ascii:
get translation from lookuptable, using the character at apointer.
if length of translation is greater than morse_size:
return an error.
store translation to mpointer.
add 1 to apointer.
add length of translation to mpointer.
subtract length of translation from morse_size.
if morse_size is zero:
return an error.
store string terminator to mpointer.
你必须将它转换为C并实现查找功能,但这应该是一个好的开始。
指针用于从相关字符串中提取和插入。对于每个字符,您基本上检查输出缓冲区中是否有足够的空间来添加莫尔斯代码段。并且,最后,您还需要检查字符串终止符'\0'
是否有足够的空间;
用于检查是否有足够空间的方式是将morse_size
变量减少每次通过morse
添加到morse_size
的字符串的长度循环。这样,{{1}}将始终是缓冲区中剩余的大小供您使用。
答案 3 :(得分:1)
无法从指针推断缓冲区大小。它需要作为参数传递,或以某种方式知道(从DEFINE值或其他常量)或隐式知道...(后者,隐式方法是“危险的”,如果大小以某种方式改变但这种变化是没有反映在使用缓冲区的地方......)
或者,更常见的是在输入缓冲区(函数将从中读取的缓冲区)的情况下,缓冲区的末尾可以用特殊字符或一系列这样的字符来标记。
答案 4 :(得分:0)
其中一个可能的(慢)解决方案是允许函数处理NULL缓冲区指针并返回所需的缓冲区大小。然后用适当大小的缓冲区第二次调用它
答案 5 :(得分:0)
另一个解决方案是,不是传入要写入的预分配目标字符串,而是执行分配并返回指向该字符串的指针。由于调用者无需猜测您的函数需要多少内存,因此这样更安全。
char *ascii2morse(const char *ascii, lookuptable *table)
您仍需为摩尔斯电码分配足够的内存。由于摩尔斯电码不是固定长度,因此有两种策略。第一种是简单地计算出给定长度字符串所需的最大可能内存(最长的莫尔斯序列* ascii中的字符数)并分配它。这似乎是一种浪费,但无论如何,调用者必须为原始计划做些什么。
另一种方法是使用realloc
根据需要不断增长字符串。你弄清楚你需要多少字节来编码下一个字符,重新分配那么多并将其附加到字符串。这可能会慢一点,内存分配器现在非常复杂,但它会使用你需要的内存。
同时避免用户必须预先分配未知内存量的陷阱,并且消除了不必要的“用户没有分配足够的内存”错误情况。
如果你真的想节省内存,我会将每个点/短划线存储在摩尔斯电码中,为2位而不是8位。你有三个“单词”,短信和长信。这至少是2位空间。