将字符串返回到可变长度字符数组

时间:2013-05-16 03:22:22

标签: c

我有一个程序可以从可变长度字符数组的输入中反转一个字符串。该函数返回一个可变长度的字符数组并打印出来。当我打印输出时,我确实得到了反向字符串,但在我的控制台打印中附加了垃圾字符。

就返回缓冲区而言,这是一项“合法”操作吗?有人可以批评我的代码并建议一个更好的选择,如果它不是正确的方法吗?

感谢。

#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

2 个答案:

答案 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");
    }
}