Инвертирование строки в C

То, что вы неправильно понимаете, - это подразделение, с которым вы работаете. при установке ширины контейнера вы не скажете ему, например, чтобы его ширина составляла ровно 500 пикселей. Это имеет смысл, когда вы начинаете думать о шрифтах. Fontsize 25 был бы хорош для вашего дисплея, но для дисплея 4K он мог бы быть минималистичным маленьким и нечитаемым. Чтобы обойти это стандартное устройство, для которого вы устанавливаете свой контейнер, составляет 1/96 дюйма. Вы можете изменить его, как показано ниже в цитате:

https://docs.microsoft.com/de-de/dotnet/api/system.windows.frameworkelement.width?view=netframework -4.7.2

qualDouble Двойное значение, как описано выше, за которым следует одна из следующих строк объявления единиц: px, in, cm, pt.

px (по умолчанию) - независимые от устройства единицы (1/96 дюйма на единицу)

в дюймах; 1 дюйм == 96px

см - это сантиметры; 1см == (96 / 2.54) px

pt - точки; 1pt == (96/72) px

Авто Включает режим автоматического изменения размера. См. Примечания.

BLOCKQUOTE>

34
задан codehitman 17 November 2016 в 18:07
поделиться

10 ответов

Если вы хотите попрактиковаться в расширенных функциях 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
  }
}

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

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

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

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

Поскольку я использовал макрос 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 , которые изначально хранят два значения v a и v b .

  // 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 , это никогда не происходит в приведенном выше коде, так что мы в порядке.

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

Есть куча Си для изучения. Веселитесь с этим!

Обновление: 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. Я сказал им, что эта функция заменяет память, и они обязаны дать мне аргумент, который позволяет это.

62
ответ дан 27 November 2019 в 15:51
поделиться

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

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;
}
1
ответ дан 27 November 2019 в 15:51
поделиться

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

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

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

4
ответ дан 27 November 2019 в 15:51
поделиться

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

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

РЕДАКТИРОВАТЬ: Извините, только что заметил приведенный выше пример XOR ...

6
ответ дан 27 November 2019 в 15:51
поделиться

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

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;
}
6
ответ дан 27 November 2019 в 15:51
поделиться

Попробуйте:

reverse_string(NULL);
reverse_string("");
7
ответ дан 27 November 2019 в 15:51
поделиться

Эта полная программа показывает, как я это сделаю. Имейте в виду, что я писал «С», когда большинство из вас, гадюки, были блеском в глазах ваших матерей, так что это старая школа, «делайте работу», «длинные имена для слабаков». Исправьте это, если хотите, меня больше интересует правильность кода.

Он обрабатывает 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'
14
ответ дан 27 November 2019 в 15:51
поделиться

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

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

}
17
ответ дан 27 November 2019 в 15:51
поделиться

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

#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;
}

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

24
ответ дан 27 November 2019 в 15:51
поделиться

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

1
ответ дан 27 November 2019 в 15:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: