Невозможно создать программу, которая будет инвертировать строку - PullRequest
2 голосов
/ 02 сентября 2010

Я использую Linux. Я пытаюсь написать программу на c, которая выведет строку назад. Вот мой код:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (){
    char string[100];
    printf ("Enter string:\n");
    gets (string);
    int length = strlen (string)-1;
    for (length = length; length>=0; length--){
        puts (string[length]);
    }
}

А вот и ошибка:

a.c:10: warning: passing argument 1 of ‘puts’ makes pointer from integer without a cast
/usr/include/stdio.h:668: note: expected ‘const char *’ but argument is of type ‘char’
/tmp/cc5rpeG7.o: In function `main':
a.c:(.text+0x29): warning: the `gets' function is dangerous and should not be used.

Что мне делать?

Ответы [ 5 ]

11 голосов
/ 02 сентября 2010

Забудьте, что функция gets() существует - она ​​смертельна.Вместо этого используйте fgets() (но обратите внимание, что он не удаляет символ новой строки в конце строки).

Вы хотите поместить один символ за раз: используйте putchar(), чтобы записать его в стандартный вывод.Не забудьте добавить новую строку в выход после цикла.

Кроме того, for (length = length; length >= 0; length--) не является идиоматическим C. Используйте одно из:

  • for ( ; length >= 0; length--)
  • for (length = strlen(string) - 1; length >= 0; length--)
  • for (int length = strlen(string) - 1; length >= 0; length--)

В последней альтернативе используется функция, добавленная в C99 (которая была доступна в C ++ задолго до этого).

Такжемы могли бы обсудить, является ли length подходящим именем для переменной.Лучше было бы переименовать его в i или pos или что-то подобное, потому что, хотя оно инициализируется длиной входного файла, оно фактически используется как индекс массива, а не как длина чего-либо.

Субъективный : не ставьте пробел между именем функции и списком ее параметров.Отцы-основатели C этого не делают - и вы не должны.


Почему «получает () смертельно?»

Первый интернет-червь - Моррис червь 1988 года - использовал программу fingerd, которая использовала gets() вместо fgets().С тех пор многие программы были повреждены, потому что они использовали gets(), а не fgets() или другую альтернативу.

Фундаментальная проблема заключается в том, что gets() не знает, сколько места доступно для хранения данных.читает.Это приводит к «переполнению буфера» - термину, который можно искать в вашей любимой поисковой системе, который будет возвращать огромное количество записей.

Если кто-то введет 150 символов ввода в пример программы, то gets() будет хранить 150 символов в массиве длиной 100. Это никогда не приводит к счастью - обычно это приводит к дампу ядра, но с тщательно выбранными входными данными - часто генерируемыми скриптами Perl или Python - вы, вероятно, можете заставить программу выполнятьсяпроизвольный другой код.Это действительно имеет значение, если программа когда-либо будет запускаться пользователем с «повышенными привилегиями».

Кстати, gets(), вероятно, будет удален из библиотеки Standard C в следующем выпуске (C1x - см. N1494 из WG14 ).Он еще долго не исчезнет из реальных библиотек C (20 лет?), Но его следует заменить на эту реализацию (или что-то подобное):

#undef NDEBUG
#include <assert.h>
char *gets(char *buffer)
{
    assert("Probability of using gets() safely" == 0);
}

Oneдругие мелкие детали, частично обсуждаемые под комментариями к основному вопросу.

Показанный код явно для C99;объявление length на полпути через функцию недопустимо в C89.Учитывая это, для функции main() все в порядке, чтобы она не возвращала значение явно, потому что стандарт C99 следует примеру стандарта C ++ и позволяет пропустить возврат из main(), а эффект такой же, какreturn(0); или return 0; в конце.

Таким образом, программа в этом вопросе не может быть строго ошибочной из-за отсутствия return в конце.Тем не менее, я рассматриваю это как одно из более специфических решений по стандартизации, и я бы предпочел, чтобы стандарты исключили это положение - или сделали что-то более радикальное, например, разрешив вездесущему, но ошибочному void main() наблюдать это, когда контроль возвращается из этогоВ результате статус успеха возвращается в среду.К сожалению, не стоит бороться за изменение этого аспекта стандарта, но в качестве личного стиля я не использую лицензию, предоставленную для исключения финального return из main().Если код должен работать с компиляторами C89, в конце он должен иметь явное return 0; (но тогда объявление length также должно быть исправлено).

1 голос
/ 02 сентября 2010

Вы также можете использовать рекурсию, чтобы сделать это. Я думаю, что это выглядит лучше, чем при использовании цикла.

Просто вызовите метод со своей строкой, и перед печатью char в методе вызовите метод снова с той же строкой, за вычетом первого символа.

Это выведет вашу строку в обратном порядке.

0 голосов
/ 02 сентября 2010

Первый:

НИКОГДА НИКОГДА НИКОГДА НИКОГДА НИКОГДА НИКОГДА использовать gets();это будет вводить точку отказа в вашем коде.gets() невозможно определить, насколько велик целевой буфер, поэтому, если вы передадите буфер размером 10 символов, а во входном потоке будет 100 символов, gets() с радостью сохранит эти дополнительные 90 символов в памяти за пределамиконец вашего буфера, потенциально заглушая что-то важное.Переполнение буфера - это простая вредоносная программа;Червь Моррис специально использовал gets() вызов в sendmail.

Используйте взамен fgets();это позволяет вам указать максимальное количество символов для чтения из входного потока.Однако, в отличие от gets(), fgets() сохранит завершающий символ новой строки в буфере, если для этого есть место, поэтому вы должны учитывать это:

char string[100]; 
char *newline;
printf("Enter a string: ");
fflush(stdout);
fgets(string, sizeof string, stdin);
newline = strchr(buffer, '\n');      // search for the newline character
if (newline)                         // if it's present
  *newline = 0;                      // set it to zero

Теперь это не так ....

Ваша ошибка связана с тем, что puts() ожидает аргумент типа char *, но вы передаете аргумент типа char, следовательно, сообщение "указатель из целого числа без приведения"(char является целым типом).Чтобы записать один символ в стандартный вывод, используйте putchar() или fputc().

0 голосов
/ 02 сентября 2010

Используйте putc или putchar, так как puts указано для получения char*, а вы кормите его char.

0 голосов
/ 02 сентября 2010

Вы должны использовать putchar вместо puts

Итак, этот цикл:

for (length = length; length>=0; length--){
    puts (string[length]);
}

Будет:

for (length = length; length>=0; length--){
    putchar (string[length]);
}

putchar примет один символ в качестве параметра и выведет его на stdout, что вам и нужно. puts, с другой стороны, напечатает всю строку в stdout. Поэтому, когда вы передаете один символ в функцию, которая ожидает целую строку (массив символов, NULL завершенная строка), компилятор запутывается.

...