цикл для переворота строки в C

Итак, я посмотрел на SO и не нашел кода, который отвечает на мой вопрос. Я написал функцию, которая должна перевернуть строку как входную в cmd-line. Вот функция:

void reverse (char string[]) {
    int x;
    int i = 0;
    char line[strlen(string)];

    for (x = strlen(string) - 1; x > 0; x--) {
        char tmp = string[x];
        line[i] = tmp;
        i++;
    }
    string = line;
}

Когда я вызываю свою функцию reverse (), строка остается прежней. т.е. 'abc' остается 'abc'

Если потребуется дополнительная информация или вопрос неуместен, дайте мне знать.

Спасибо!!


person kandaspohn    schedule 21.11.2015    source источник
comment
Я никогда не использую синтаксис char string[] для аргументов, поскольку он делает неочевидным, что string на самом деле является указателем. (Изменить: я имею в виду человека, читающего или пишущего код; компьютеру все равно)   -  person user253751    schedule 21.11.2015
comment
для того, чтобы было понятно, должно ли это быть: char * string []?   -  person kandaspohn    schedule 21.11.2015
comment
Было бы char* string. Следующий вопрос: знаете ли вы, почему void f(int x) {x = 7;} int main() {int y = 5; f(y); printf("%d\n", y); return 0;} печатает 5, а не 7?   -  person user253751    schedule 21.11.2015
comment
в более общем плане: что произойдет, если вы вернете указатель на локальную переменную?   -  person jev    schedule 21.11.2015
comment
Я действительно новичок в этом, так что полегче со мной, но .... это потому, что y указывает на значение, а не на адрес?   -  person kandaspohn    schedule 21.11.2015
comment
не возвращает ли указатель адрес переменной, а НЕ значение?   -  person kandaspohn    schedule 21.11.2015
comment
проверьте это stackoverflow.com/questions/784417/reversing- а-строка-в-с   -  person artm    schedule 21.11.2015
comment
Спасибо за ссылку!   -  person kandaspohn    schedule 21.11.2015
comment
Я знаю, что вы спрашивали о C, но если вы открыты для C ++: std::reverse(string.begin(),string.end()).   -  person Eric Angle    schedule 21.11.2015


Ответы (4)


Вы объявляете свой line массив на char короче, помните null в конце.

Другой момент, это должно быть for (x = strlen(string) - 1; x >= 0; x--), так как вам нужно скопировать символ в 0.

void reverse (char string[]) {
    int x;
    int i = 0;
    char line[strlen(string) + 1];

    for (x = strlen(string) - 1; x >= 0; x--) {
        char tmp = string[x];
        line[i] = tmp;
        i++;
    }

    for(x = 0; x < strlen(string); x++)
    {
        string[x] = line[x];
    }
}

Обратите внимание, что эта функция вызовет апокалипсис при передаче пустой строки или строкового литерала (как сказал Бобби Сакамано).

Предложение, которое вы, вероятно, можете сделать: void reverse(char source[], char[] dest) и проверить, пуста ли исходная строка.

person John Mark Gabriel Caguicla    schedule 21.11.2015
comment
В исходном коде массив line (char line[strlen(string)]) был объявлен в стеке, а не в куче, поэтому он исчезает после вызова функции, оставляя string нигде. - person John Mark Gabriel Caguicla; 21.11.2015
comment
@BobbySacamano Ах да, это было моим плохим. Вы правы насчет string = line, поскольку string действительно был передан в стек. Запутался char string[], никогда не привык передавать массивы. Ваше здоровье. - person John Mark Gabriel Caguicla; 21.11.2015
comment
Последний вопрос, Джон ... что именно делает добавление +1 к строке char [strlen (string) + 1]? Это то, что получает символ в 0? - person kandaspohn; 21.11.2015
comment
Например, предположим, что мы передаем сообщение reverse, это означает, что strlen() вернет 7, но строки C должны оканчиваться нулем, поэтому в самом последнем символе должен быть 0, поэтому вам понадобится один последний слот для него (следовательно, char line[8]). - person John Mark Gabriel Caguicla; 21.11.2015
comment
@JohnMarkCaguicla стандартно требует, чтобы символ '\ 0' всегда был 0? - person Bobby Sacamano; 21.11.2015
comment
@BobbySacamano Нет, я просто использовал это для объяснения. - person John Mark Gabriel Caguicla; 21.11.2015

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

  • Измените оператор присваивания внизу процедуры на memcpy.
  • Измените условие цикла на ‹-

Итак, ваш правильный код:

void reverse (char string[]) {
  int x;
  int i = 0;
  char line[strlen(string)];

  for (x = strlen(string) - 1; x >= 0; x--) {
    char tmp = string[x];
    line[i] = tmp;
    i++;
  }
  memcpy(string, line, sizeof(char) * strlen(line));
}
person bruceg    schedule 21.11.2015
comment
вы можете объяснить, что делает memcpy ()? - person kandaspohn; 21.11.2015
comment
он копирует память из второго операнда в первый операнд на количество байтов, указанное третьим операндом. sizeof(char) == 1 всегда, если вы видите примеры строк, в которых отсутствует sizeof. Для других типов данных вам понадобится sizeof. - person Bobby Sacamano; 21.11.2015
comment
memcpy - это стандартная функция c, которая копирует данные из 2-го параметра (источника) в первый параметр (место назначения). Количество данных в байтах указывается в 3-м параметре. В приведенном выше коде он скопирует символы из переменной строки в ячейку памяти переменной строки. - person bruceg; 21.11.2015
comment
Если кто-то вызовет реверс со строковым литералом, это, кстати, сломает ситуацию. - person Bobby Sacamano; 21.11.2015
comment
Правильно. Это вызовет сбой. - person bruceg; 21.11.2015

Поскольку вы хотите перевернуть строку, вы сначала должны решить, хотите ли вы перевернуть копию строки или перевернуть строку на месте (на месте). Поскольку вы спросили об этом в контексте «C», предположим, что вы хотите изменить существующую строку (перевернуть существующую строку) и сделать копию строки в вызывающей функции, если вы хотите сохранить оригинал.

Вам понадобится строковая библиотека

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

Индексирование массивов работает, и эта версия использует этот подход,

/* this first version uses array indexing */
char*
streverse_a(char string[])
{
    int len; /*how big is your string*/
    int ndx; /*because 'i' is hard to search for*/
    char tmp; /*hold character to swap*/
    if(!string) return(string); /*avoid NULL*/
    if( (len=strlen(string)) < 2 ) return(string); /*one and done*/
    for( ndx=0; ndx<len/2; ndx++ ) {
        tmp=string[ndx];
        string[ndx]=string[len-1-ndx];
        string[len-1-ndx]=tmp;
    }
    return(string);
}

Но вы можете сделать то же самое с указателями,

/* this is how K&R would write the function with pointers */
char*
streverse(char* sp)
{
    int len, ndx; /*how big is your string */
    char tmp, *bp, *ep; /*pointers to begin/end, swap temporary*/
    if(!sp) return(sp); /*avoid NULL*/
    if( (len=strlen(bp=sp)) < 2 ) return(sp); /*one and done*/
    for( ep=bp+len-1; bp<ep; bp++, ep-- ) {
        tmp=*bp; *bp=*ep; *ep=tmp; /*swap*/
    }
    return(sp);
}

(Нет, действительно, компилятор не взимает меньше за возврат void.)

И поскольку вы всегда тестируете свой код,

char s[][100] = {
    "", "A", "AB", "ABC", "ABCD", "ABCDE",
    "hello, world", "goodbye, cruel world", "pwnz0r3d", "enough"
};

int
main()
{
    /* suppose your string is declared as 'a' */
    char a[100];
    strcpy(a,"reverse string");

    /*make a copy of 'a', declared the same as a[]*/
    char b[100];
    strcpy(b,a);
    streverse_a(b);
    printf("a:%s, r:%s\n",a,b);

    /*duplicate 'a'*/
    char *rp = strdup(a);
    streverse(rp);
    printf("a:%s, r:%s\n",a,rp);
    free(rp);

    int ndx;
    for( ndx=0; ndx<10; ++ndx ) {
        /*make a copy of 's', declared the same as s[]*/
        char b[100];
        strcpy(b,s[ndx]);
        streverse_a(b);
        printf("s:%s, r:%s\n",s[ndx],b);

        /*duplicate 's'*/
        char *rp = strdup(s[ndx]);
        streverse(rp);
        printf("s:%s, r:%s\n",s[ndx],rp);
        free(rp);
    }
}
person ChuckCottrill    schedule 21.11.2015

Последняя строка в вашем коде ничего не делает string = line;

Параметры передаются по значению, поэтому, если вы измените их значение, оно будет локальным только для функции. Указатели - это значение адреса памяти, на которую они указывают. Если вы хотите изменить указатель, которому была передана функция, вам нужно взять указатель на этот указатель.

Вот краткий пример того, как вы могли бы это сделать.

void reverse (char **string) {
    char line = malloc(strlen(*string) + 1);
    //automatic arrays are deallocated once the function ends
    //so line needs to be dynamically or statically allocated

   // do something to line

    *string = line;
}

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

char *test = "hello";
reverse(test);
free(test); //this is pretty scary

Кроме того, если тест был выделен как динамическая память, указатель на него был бы потерян, и тогда это стало бы утечкой памяти.

person Bobby Sacamano    schedule 21.11.2015