1

可変長文字配列の入力から文字列を逆にするプログラムがあります。関数は可変長の文字配列を返し、出力されます。出力を印刷すると、逆の文字列が表示されますが、コンソールの印刷にゴミ文字が追加されています。

これはバッファに戻るという点で「合法的な」操作ですか? 誰かが私のコードを批判し、それが正しいアプローチでない場合、より良い代替案を提案できますか?

ありがとう。

#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

4

2 に答える 2

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()これは、関数を複数回呼び出した後でも、返された割り当てられたメモリが問題ないことを示す代替手段です (単純な引数の代わりに areverse_string()を取るように変更されましたが、それ以外は変更されていません)。const char *char *

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が彼の回答で指摘したように)、文字列が null で終了していることを確認する必要があります。サンプル コードの問題をすぐに特定できない場合でも、ローカル文字列へのポインターを返すことは安全ではありません。これは、出力の最後のガベージを説明します。

于 2013-05-16T03:26:50.437 に答える
1

まず、そのようなローカルへのポインターを返すことは安全ではありません。イディオムは、関数へのパラメーターとして十分な大きさのバッファーへのポインターを受け取り、それに結果を入力することです。

ガベージはおそらく、結果文字列を null で終了していないためです。最後に必ず追加'\0'してください。

編集:これは、慣用的な C を使用して関数を記述する 1 つの方法です。

//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");
    }
}
于 2013-05-16T03:29:17.130 に答える