Замена двух символов одной строки указателями

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

Это должна быть довольно простая задача, но здесь просто пробел в моих знаниях. Мне удалось успешно создать циклы for, которые правильно перебирают строку, используя i и j в качестве управляющих переменных. Я также смог распечатать символы, которые нужно поменять местами.

Единственная проблема заключается в том, что закомментированная строка кода в цикле for. Предполагается, что он меняет местами два значения, но выдает ошибку, и я не понимаю, почему.

#include <stdio.h>

int length(char *p);
void reverse(char *);

int main() {
    char *p = "Computer";
    reverse(p);
}

int length(char *p) {
    int i;
    for (i = 0; *(p + i) != '\0'; i++);
    return i;
}

void reverse(char *p) {
    int l, i;
    char t;

    for (l = 0; *(p + l) != '\0'; l++);
    int len = l;
    int temp;
    for (i = 0; i < l; i++, l--) {
        t = *(p + i);
        printf("i-th element - %c\n", t);
        printf("j-th element - %c\n", *(p + len - 1 - i));
        *(p + i) = *(p + len - 1 - i);  // crash
    }
    puts(p);
}

Я даже не уверен, что их можно поменять местами таким образом. Я бы выбрал подход t = p[i]; p[i] = p[i + 1].... Если кто-нибудь может исправить и объяснить это и драму, связанную с указателем, это было бы здорово.

Какую ошибку выдает?

L. Scott Johnson 11.12.2020 14:54

Зачем использовать простую и многословную *(p + i) нотацию вместо более простой p[i] нотации? Использование индексов имеет дело с указателями так же, как и явная, подробная альтернатива.

Jonathan Leffler 11.12.2020 21:21
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
161
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Во-первых, вы не должны пытаться модифицировать строковые литералы.

Вы должны использовать (модифицируемый) массив:

    char p[] = "Computer";

вместо

    char *p = "Computer";

Во-вторых, вам понадобится

*(p + len - 1 - i) = t;

после

*(p + i) = *(p + len - 1 - i);

для завершения обмена.

Ух ты. Я не могу в это поверить. Я просто должен был изменить это. char p[] = "Компьютер";. Я уже знал, как выполнить обмен, это было просто для проверки.

Storm Claw 11.12.2020 15:00

Не могли бы вы объяснить причину и разницу между двумя ([] и *)? У меня проблемы с пониманием указателей.

Storm Claw 11.12.2020 15:01

Для начала вы не можете изменить строковый литерал

char *p = "Computer";

любая попытка изменить строковый литерал приводит к неопределенному поведению.

Поэтому вместо указателя на строковый литерал объявите массив символов, например

char s[] = "Computer";

Более того, ваша функция reverse не использует функцию length и фактически не переворачивает строку.

Эта петля

for (i = 0; i < l; i++, l--) {

использует две вспомогательные переменные в качестве индексов.

Функция reverse не должна ничего выводить. Что он должен сделать, так это отменить переданную строку. Именно вызывающая функция решает, выводить ли перевернутую строку или использовать ее для других целей.

Функции length и reverse могут быть объявлены и определены с использованием только указателей следующим образом.

size_t length( const char *s ) 
{
    const char *p = s;

    while ( *p ) ++p;

    return p - s;
}

и

char * reverse( char *s ) 
{
    if ( *s )
    {
        for ( char *p = s, *q = s + length( s ); p < --q; ++p )
        {
            char c = *p;
            *p = *q;
            *q = c;
        }
    }

    return s;
}

Функция reverse может быть вызвана как

int main( void )
{
    char s[] = "Computer";

    puts( s );
    puts( reverse( s ) );
}

Вот демонстрационная программа.

#include <stdio.h>

size_t length( const char *s ) 
{
    const char *p = s;

    while ( *p ) ++p;

    return p - s;
}

char * reverse( char *s ) 
{
    if ( *s )
    {
        for ( char *p = s, *q = s + length( s ); p < --q; ++p )
        {
            char c = *p;
            *p = *q;
            *q = c;
        }
    }

    return s;
}

int main( void )
{
    char s[] = "Computer";

    puts( s );
    puts( reverse( s ) );
}

Его вывод

Computer
retupmoC

Как видите, в обеих функциях нет ни одной переменной, используемой в качестве индекса. Функции используют только указатели.

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

/* reverses a string
 * !!! this function modifies input string !!! 
 */
char *reverse(char *s)
{
    char *h, *t, c; /* h)ead, t)ail and a temporary c */
    int len = 0;
    /* positioning the pointers at beginning and end of string s */
    len = length(s);

    h = (char*)&s[0];
    t = (char*)&s[len - 1];

    for (int i = 0; i < (len / 2); i++)
    {
        /* swap chars */
        c = *h;
        *h = *t;
        *t = c;

        /* increase h pointer, decrease t pointer */
        h++;
        t--;
    }
    return s;
}

Чистые указатели... в любой форме при замене *h и *t вы не можете уйти от временного хранилища ( c );

Имейте в виду, что входная строка должна быть объявлена ​​как char name[], иначе sigsegv следует за вами!

Если вы хотите сохранить строку для последующей печати непосредственно в stdout, вы можете сделать это:

char * reverse(char * I){
  int i,l;
  char * O;
  l=strlen(I);
  O=malloc(l+1);
  for(i=1;i<=l;i++)O[i-1]=I[l-i];
  O[l]='\0';
  return O;
}
int main(){
  char * p=reverse("Computer");
  printf("%s",p);
  free(p);
}

Другие вопросы по теме