Подтвердить что ты не робот

Обращение строки в C

Я разработал программу с обратной строкой. Мне интересно, есть ли лучший способ сделать это, и если у моего кода есть какие-то потенциальные проблемы. Я ищу практиковать некоторые дополнительные функции C.

char* reverse_string(char *str)
{
    char temp;
    size_t len = strlen(str) - 1;
    size_t i;
    size_t k = len;

    for(i = 0; i < len; i++)
    {
        temp = str[k];
        str[k] = str[i];
        str[i] = temp;
        k--;

        /* As 2 characters are changing place for each cycle of the loop
           only traverse half the array of characters */
        if(k == (len / 2))
        {
            break;
        }
    }
}
4b9b3361

Ответ 1

Если вы хотите использовать расширенные функции C, как насчет указателей? Мы можем подбрасывать макросы и xor-swap для удовольствия тоже!

#include <string.h> // for strlen()

// reverse the given null-terminated string in place
void inplace_reverse(char * str)
{
  if (str)
  {
    char * end = str + strlen(str) - 1;

    // swap the values in the two given variables
    // XXX: fails when a and b refer to same memory location
#   define XOR_SWAP(a,b) do\
    {\
      a ^= b;\
      b ^= a;\
      a ^= b;\
    } while (0)

    // walk inwards from both ends of the string, 
    // swapping until we get to the middle
    while (str < end)
    {
      XOR_SWAP(*str, *end);
      str++;
      end--;
    }
#   undef XOR_SWAP
  }
}

A указатель (например, char *, чтение справа налево как указатель на char) - это тип данных в C, который используется чтобы ссылаться на местоположение в памяти другого значения. В этом случае, место хранения a char. Мы можем разыгрывать указатели, префикс их с помощью *, что дает нам значение хранится в этом месте. Таким образом, значение, сохраненное в str, равно *str.

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

Здесь мы используем один указатель для ссылки на первый необработанный char строки (str), а другой - для последней (end). Мы меняем свои значения (*str и *end) и перемещаем указатели внутрь до середины строки. После str >= end, либо они оба указывают на тот же char, что означает, что наша исходная строка имела нечетная длина (а средняя char не должна быть отменена), или мы все обработали.

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

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

    while (str < end)
    {
      do { *str ^= *end; *end ^= *str; *str ^= *end; } while (0);
      str++;
      end--;
    }

Обратите внимание, что аргументы макроса отображаются один раз за каждый раз, когда они используются в макроопределение. Это может быть очень полезно - но может также нарушить ваш код если используется неправильно. Например, если бы я сжал приращение/декремент инструкции и вызов макроса в одну строку, например

      XOR_SWAP(*str++, *end--);

Тогда это будет расширяться до

      do { *str++ ^= *end--; *end-- ^= *str++; *str++ ^= *end--; } while (0);

который выполняет тройную операцию приращения/уменьшения, и на самом деле сделать своп, который он должен делать.

Пока мы обсуждаем этот вопрос, вы должны знать, что означает xor (^). Это базовый арифметическая операция - как сложение, вычитание, умножение, деление, за исключением это обычно не учили в начальной школе. Он объединяет два целых числа по битам - как дополнение, но мы не заботимся о переносе. 1^1 = 0, 1^0 = 1, 0^1 = 1, 0^0 = 0.

Известным трюком является использование xor для замены двух значений. Это работает из-за трех основных свойства xor: x ^ 0 = x, x ^ x = 0 и x ^ y = y ^ x для всех значений x и y. Итак, скажем, у нас есть два переменные a и b, которые первоначально сохраняют два значения va и vb.

  // initially:
  // a == va
  // b == vb
  a ^= b;
  // now: a == va ^ vb
  b ^= a;
  // now: b == vb ^ (va ^ vb)
  //        == va ^ (vb ^ vb)
  //        == va ^ 0
  //        == va
  a ^= b;
  // now: a == (va ^ vb) ^ va
  //        == (va ^ va) ^ vb
  //        == 0 ^ vb
  //        == vb

Итак, значения меняются местами. У этого есть одна ошибка - когда a и b являются одной и той же переменной:

  // initially:
  // a == va
  a ^= a;
  // now: a == va ^ va
  //        == 0
  a ^= a;
  // now: a == 0 ^ 0
  //        == 0
  a ^= a;
  // now: a == 0 ^ 0
  //        == 0

Так как мы str < end, это никогда не происходит в приведенном выше коде, поэтому мы в порядке.

В то время как нас беспокоит правильность, мы должны проверить наши крайние случаи. Строка if (str) должна удостовериться, что нам не был указатель NULL для строки. Как насчет пустой строки ""? Ну strlen("") == 0, поэтому мы инициализируем end как str - 1, что означает, что условие while (str < end) никогда не является истинным, поэтому мы ничего не делаем. Что правильно.

Там есть куча C для изучения. Получайте удовольствие от этого!

Обновление: mmw подводит итог, так что вам нужно быть осторожным, как вы это вызываете, поскольку он работает на месте.

 char stack_string[] = "This string is copied onto the stack.";
 inplace_reverse(stack_string);

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

 char * string_literal = "This string is part of the executable.";
 inplace_reverse(string_literal);

Приведёт к тому, что ваш код будет пламен и погибнет во время выполнения. Это потому, что string_literal просто указывает на строку, которая хранится как часть исполняемого файла - обычно это память, которую вы не можете редактировать ОС. В более счастливом мире ваш компилятор узнает об этом и кашляет ошибку при попытке скомпилировать, сообщая вам, что string_literal должен иметь тип char const *, так как вы не можете изменять содержимое. Однако это не тот мир, в котором живет мой компилятор.

Есть некоторые хаки, которые вы можете попытаться удостовериться в том, что какая-то память находится в стеке или в куче (и поэтому доступна для редактирования), но они не обязательно переносимы, и это может быть довольно уродливо. Тем не менее, я более чем счастлив возложить ответственность за это на функцию invoker. Я сказал им, что эта функция делает манипуляции с памятью, это их обязанность дать мне аргумент, который позволяет это.

Ответ 2

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

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

void reverse_string(char *str)
{
    /* skip null */
    if (str == 0)
    {
        return;
    }

    /* skip empty string */
    if (*str == 0)
    {
        return;
    }

    /* get range */
    char *start = str;
    char *end = start + strlen(str) - 1; /* -1 for \0 */
    char temp;

    /* reverse */
    while (end > start)
    {
        /* swap */
        temp = *start;
        *start = *end;
        *end = temp;

        /* move */
        ++start;
        --end;
    }
}


int main(void)
{
    char s1[] = "Reverse me!";
    char s2[] = "abc";
    char s3[] = "ab";
    char s4[] = "a";
    char s5[] = "";

    reverse_string(0);

    reverse_string(s1);
    reverse_string(s2);
    reverse_string(s3);
    reverse_string(s4);
    reverse_string(s5);

    printf("%s\n", s1);
    printf("%s\n", s2);
    printf("%s\n", s3);
    printf("%s\n", s4);
    printf("%s\n", s5);

    return 0;
}

Отредактировано так, что конец не будет указывать на возможное повреждение памяти, когда strlen равно 0.

Ответ 3

Вы можете поместить свой тест (len/2) в цикл for:

for(i = 0,k=len-1 ; i < (len/2); i++,k--)
{
        temp = str[k];
        str[k] = str[i];
        str[i] = temp;

}

Ответ 4

Эта полная программа показывает, как я это сделаю. Имейте в виду, что я писал C, когда большинство из вас, пьяницы, были блеском в глазах ваших мам, поэтому это старая школа, работа на дому, длинные-var-names-are-for-wimps. Исправьте, что, если хотите, меня больше интересует правильность кода.

Он обрабатывает NULL, пустые строки и все строковые размеры. Я не тестировал его со строками максимального размера (max (size_t)), но он должен работать, и если вы обрабатываете строки, большие, вы все равно сумасшедшие: -)

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

char *revStr (char *str) {
    char tmp, *src, *dst;
    size_t len;
    if (str != NULL)
    {
        len = strlen (str);
        if (len > 1) {
            src = str;
            dst = src + len - 1;
            while (src < dst) {
                tmp = *src;
                *src++ = *dst;
                *dst-- = tmp;
            }
        }
    }
    return str;
}

char *str[] = {"", "a", "ab", "abc", "abcd", "abcde"};

int main(int argc, char *argv[]) {
    int i;
    char s[10000];
    for (i=0; i < sizeof(str)/sizeof(str[0]); i++) {
        strcpy (s, str[i]);
        printf ("'%s' -> '%s'\n", str[i], revStr(s));
    }
    return 0;
}

Результат:

'' -> ''
'a' -> 'a'
'ab' -> 'ba'
'abc' -> 'cba'
'abcd' -> 'dcba'
'abcde' -> 'edcba'

Ответ 5

Попробуйте следующее:

reverse_string(NULL);
reverse_string("");

Ответ 6

Вы можете изменить свое объявление цикла цикла, чтобы сделать код короче:

char* reverse_string(char *str)
{
    char temp;
    size_t len = strlen(str) - 1;
    size_t stop = len/2;
    size_t i,k;

    for(i = 0, k = len; i < stop; i++, k--)
    {
        temp = str[k];
        str[k] = str[i];
        str[i] = temp;
    }
    return str;
}

Ответ 7

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

Кроме того, это может быть разборчиво, но len/2 следует рассчитывать только один раз, IMO.

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

Ответ 8

Больше никто не использует указатели?

void inplace_rev( char * s ) {
  char t, *e = s + strlen(s);
  while ( --e > s ) { t = *s;*s++=*e;*e=t; }
}

EDIT: Извините, просто заметил вышеприведенный пример XOR...

Ответ 9

void reverse(char *s)
{
  char *end,temp;
  end = s;
  while(*end != '\0'){
    end++;
  }
  end--;  //end points to last letter now
  for(;s<end;s++,end--){
    temp = *end;
    *end = *s;
    *s = temp; 
  }
}

Ответ 10

rev {
int len = strlen(str)-1;
for ( int i =0; i< len/2 ; i++ ) {
        char t = str[i];
        str[i] = str[len-i];
        str[len-i] = t;
        }

}

Ответ 11

bool reverse_string(char* str)
{
    // Make sure str is reversible
    if (!str ||  strlen(str) < 2)
        return false;

    char* first = str;
    char* last = str + strlen(str) - 1; // Minus 1 accounts for Index offset
    char temp;

    do{
        temp = *first;
        *first = *last;
        *last = temp;
    }
    while (++first < --last); // Update Pointer Addresses and check for equality

    return true;
}

Это решение основано на сообщении GManNickG с несколькими изменениями. Исходный логический оператор может быть опасным, если! Str не оценивается до операции strlen (для NULL ptr). Это не относится к моему компилятору. Я думал, что добавлю этот код, потому что это хороший пример цикла do-while.

Ответ 12

Поскольку вы говорите, что хотите получить фантазию, возможно, вы захотите обменять своих персонажей с помощью XOR swap.

Ответ 13

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

size_t length = strlen(str);
size_t i;

for (i = 0; i < (length / 2); i++)
{
    char temp = str[length - i - 1];
    str[length - i - 1] = str[i];
    str[i] = temp;
}

Ответ 14

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

int main() 
{
    char *data = "hello world";
    int length=strlen(data);
    char bytes[length];
    int n=0;
    while(n<=length)
    {
       bytes[n] = data[length-n-1];
       n++;
    }
    printf("%s\n", bytes);
    return 0;   
}

Ответ 15

#include <stdio.h>

int main()    
{

    char string[100];
    int i;

    printf("Enter a string:\n");
    gets(string);
    printf("\n");
    for(i=strlen(string)-1;i>-1;i--)

    printf("%c",string[i]);
}

Ответ 16

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

char *string_reverse(char *dst, const char *src)
{
    if (src == NULL) return NULL;

    const char *src_start = src;
    char *dst_end = dst + strlen(src);
    *dst_end = '\0';

    while ((*--dst_end = *src_start++)) { ; }

    return dst;
}

и здесь работает пример.

Ответ 17

Here is my shot which will handle all the cases 
char *p ="KDLAKDADKADAD"
char p[] = "lammdlamldaldladadada"
also empty string 

#include<stdio.h>
#include<string.h>enter code here
#include<stdlib.h>
char *string_reverse(char *p);
int main()
{

        char *p = " [email protected]";
        char *temp = string_reverse(p);
        printf("%s", temp);
}
char *  string_reverse( char *p )
{

        if(*p == '\0')
        {
                printf("No charecters are present \n");
                return 0;
        }
        int count = strlen(p)+1;
        int mid = strlen(p)/2;
        char *q  = (char *)malloc(count * sizeof(char));
        if( q )
        {
                strcpy(q,p);
                char *begin,*end,temp;
                begin = q ;
                end = q+strlen(p)-1  ;
                int i = 0;
                while( i < mid/2 )
                {
                        temp = *end;
                        *end = *begin;
                        *begin = temp;
                        begin++;
                        end--;
                        i++;
                }
                return q;
        }
        else
        {

                printf("Memory Not allocated ");
        }
        free(q);
}

Ответ 18

Мои два цента:

/* Reverses n characters of a string and adds a '\0' at the end */
void strnrev (char *txt, size_t len) {
    size_t idx;
    for (idx = len >> 1; idx > 0; idx--) {
        txt[len] = txt[idx - 1];
        txt[idx - 1] = txt[len - idx];
        txt[len - idx] = txt[len];
    }
    txt[len] = '\0';
}

/* Reverses a null-terminated string */
void strrev (char *txt) {
    size_t len = 0;
    while (txt[len++]);
    strnrev(txt, --len);
}

Тест # 1 - strrev():

char string[] = "Hello world!";
strrev(string);
printf("%s\n", string); // Displays "!dlrow olleH"

Тест № 2 - strnrev():

char string[] = "Hello world!";
strnrev(string, 5);
printf("%s\n", string); // Displays "olleH"

Ответ 19

/* Author: Siken Dongol */
#include <stdio.h>

int strLength(char *input) {
    int i = 0;
    while(input[i++]!='\0');
    return --i;
}

int main()
{
    char input[] = "Siken Man Singh Dongol";

    int len = strLength(input);
    char output[len];

    int index = 0;
    while(len >= 0) {
        output[index++] = input[--len];
    }

    printf("%s\n",input);
    printf("%s\n",output);
    return 0;
}

Ответ 20

Код выглядит излишне сложным. Вот моя версия:

void strrev(char* str) { 
    size_t len = strlen(str);
    char buf[len]; 

    for (size_t i = 0; i < len; i++) { 
        buf[i] = str[len - 1 - i]; 
    }; 

    for (size_t i = 0; i < len; i++) { 
        str[i] = buf[i]; 
    }
}

Ответ 21

простой и простой код xD

void strrev (char s[]) {

int i;
int dim = strlen (s);
char l;

for (i = 0; i < dim / 2; i++) {
    l = s[i];
    s[i] = s[dim-i-1];
    s[dim-i-1] = l;
    }

}

Ответ 22

Это хороший вопрос ant2009. Вы можете использовать автономную функцию для изменения строки. Код...

#include <stdio.h>
#define MAX_CHARACTERS 99

int main( void );
int strlen( char __str );

int main() {
    char *str[ MAX_CHARACTERS ];
    char *new_string[ MAX_CHARACTERS ];
    int i, j;

    printf( "enter string: " );
    gets( *str );

    for( i = 0; j = ( strlen( *str ) - 1 ); i < strlen( *str ), j > -1; i++, j-- ) {
        *str[ i ] = *new_string[ j ];
    }
    printf( "Reverse string is: %s", *new_string" );
    return ( 0 );
}

int strlen( char __str[] ) {
    int count;
    for( int i = 0; __str[ i ] != '\0'; i++ ) {
         ++count;
    }
    return ( count );
}

Ответ 23

Вы можете попробовать эту арифметику указателя:

void revString(char *s)
{
  char *e = s; while(*e){ e++; } e--;
  while(e > s){ *s ^= *e; *e ^= *s; *s++ ^= *e--; }
}