我有一个程序可以从可变长度字符数组的输入中反转一个字符串。该函数返回一个可变长度的字符数组并打印出来。当我打印输出时,我确实得到了反向字符串,但在我的控制台打印中附加了垃圾字符。
就返回缓冲区而言,这是一项“合法”操作吗?有人可以批评我的代码并建议一个更好的选择,如果它不是正确的方法吗?
感谢。
#include <stdio.h>
#include <stdlib.h>
char *reverse_string(char *input_string);
char *reverse_string(char *input_string)
{
int i=0;
int j=0;
char *return_string;
char filled_buffer[16];
while (input_string[i]!='\0')
i++;
while (i!=0)
{
filled_buffer[j]=input_string[i-1];
i--;
j++;
}
return_string=filled_buffer;
printf("%s", return_string);
return return_string;
}
int main (void)
{
char *returned_string;
returned_string=reverse_string("tasdflkj");
printf("%s", returned_string);
return 1;
}
这是我从Xcode输出的 - jklfdsat \ 347 \ 322 \ 227 \ 377 \ 231 \ 235
答案 0 :(得分:4)
不,在函数中返回指向本地字符串的指针是不安全的。 C不会阻止你这样做(虽然有时编译器会在你提出要求时发出警告;在这种情况下,局部变量return_string
会阻止它发出警告,除非你将代码更改为return filled_buffer;
)。但这不安全。基本上,空间被其他函数重用,因此他们欢快地践踏曾经是一个整齐格式化的字符串。
你能否更详细地解释这个评论 - “不,这不安全......”
当函数返回时,局部变量(与字符串常量相对)超出范围。返回指向超出范围的变量的指针是未定义的行为,这是不惜一切代价避免的。当您调用未定义的行为时,即使程序重新格式化您的硬盘驱动器,也可能发生任何事情 - 包括看似工作的程序 - 并且没有理由抱怨。此外,不能保证在不同的机器上,甚至在当前机器上使用相同编译器的不同版本也会发生同样的事情。
将输出缓冲区传递给函数,或者让函数使用malloc()
来分配可以返回并由调用函数释放的内存。
#include <stdio.h>
#include <string.h>
int reverse_string(char *input_string, char *buffer, size_t bufsiz);
int reverse_string(char *input_string, char *buffer, size_t bufsiz)
{
size_t j = 0;
size_t i = strlen(input_string);
if (i >= bufsiz)
return -1;
buffer[i] = '\0';
while (i != 0)
{
buffer[j] = input_string[i-1];
i--;
j++;
}
printf("%s\n", buffer);
return 0;
}
int main (void)
{
char buffer[16];
if (reverse_string("tasdflkj", buffer, sizeof(buffer)) == 0)
printf("%s\n", buffer);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *reverse_string(char *input_string);
char *reverse_string(char *input_string)
{
size_t j = 0;
size_t i = strlen(input_string) + 1;
char *string = malloc(i);
if (string != 0)
{
string[--i] = '\0';
while (i != 0)
{
string[j] = input_string[i-1];
i--;
j++;
}
printf("%s\n", string);
}
return string;
}
int main (void)
{
char *buffer = reverse_string("tasdflkj");
if (buffer != 0)
{
printf("%s\n", buffer);
free(buffer);
}
return 0;
}
请注意,示例代码在每个格式字符串的末尾包含换行符;它可以更容易地判断字符串的结尾位置。
这是一个替代main()
,它表明即使在多次调用reverse_string()
函数(已修改为const char *
而不是char *
后,返回的已分配内存仍然正常{1}}参数,但在其他方面没有变化。)
int main (void)
{
const char *strings[4] =
{
"tasdflkj",
"amanaplanacanalpanama",
"tajikistan",
"ablewasiereisawelba",
};
char *reverse[4];
for (int i = 0; i < 4; i++)
{
reverse[i] = reverse_string(strings[i]);
if (reverse[i] != 0)
printf("[%s] reversed [%s]\n", strings[i], reverse[i]);
}
for (int i = 0; i < 4; i++)
{
printf("Still valid: %s\n", reverse[i]);
free(reverse[i]);
}
return 0;
}
另外(在pwny中指出answer之前我将此注释添加到我的内容中),您需要确保您的字符串为空终止。即使您可能没有立即发现示例代码的问题,返回指向本地字符串的指针仍然是不安全的。这说明了输出结束时的垃圾。
答案 1 :(得分:1)
首先,返回一个指向本地的指针是不安全的。成语是接收一个指向足够大的缓冲区的指针作为函数的参数,并用结果填充它。
垃圾可能是因为你没有空终止你的结果字符串。请务必在最后附加'\0'
。
编辑:这是使用惯用语C编写函数的一种方法。
//buffer must be >= string_length + 1
void reverse_string(char *input_string, char* buffer, size_t string_length)
{
int i = string_length;
int j = 0;
while (i != 0)
{
buffer[j] = input_string[i-1];
i--;
j++;
}
buffer[j] = '\0'; //null-terminate the string
printf("%s", buffer);
}
然后,你称之为:
#define MAX_LENGTH 16
int main()
{
char* foo = "foo";
size_t length = strlen(foo);
char buffer[MAX_LENGTH];
if(length < MAX_LENGTH)
{
reverse_string(foo, buffer, length);
printf("%s", buffer);
}
else
{
printf("Error, string to reverse is too long");
}
}