Перевернуть строку, используя указатели в функции, вывод в main искажен - PullRequest
1 голос
/ 27 января 2011

Я пытаюсь самостоятельно изучить C, используя C Primer Plus от Стивена Прата, и одно из упражнений в конце главы - «Написать функцию, которая заменяет содержимое строки настрока перевернута.Это глава о символьных строках с хорошей дозой указателей.Я пытаюсь использовать указатели как можно больше, чтобы лучше понять, но я застрял.

Моя проблема в том, что когда я печатаю значение указателя возврата в main, он искажается.

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

Я столько всего перепробовал, чего мне не хватает?FWIW Я еще не узнал о malloc в книге, хотя я вижу, что он упоминается на различных страницах www, которые я часто посещал, пытаясь лучше понять C.

<code>
$ cc -o exercise8 exercise8.c && ./exercise8
This is s1 before: abcd
This is s2 in function: dcba
This is s3 after: d`!

/*   A function that replaces the contents of a string with the string reversed. */
#include <stdio.h>
#include <string.h>
char *str_rev(char * string);
int main(void)
{
   char * s1  = "abcd";
   char * s3;
   printf("This is s1 before: %s\n", s1);

   s3 = str_rev(s1);
   printf("This is s3 after: %s\n", s3);

}

char *str_rev(char * string)
{
   char ar3[5];
   char * s2;
   int len = 0;

   s2 = ar3;

   len = (strlen(string) - 1);
   string = string + len;

   while ( len >= 0 )
   {
      *s2 = *string;
      len--;
      string--;
      s2++;
   }
   s2++;
   *s2 = 0;
   s2 = s2 - 5;

   printf("This is s2 in function: %s\n", s2);
   return s2;
}
$ gdb exercise8
GNU gdb (GDB) 7.1-ubuntu

Reading symbols from exercise8...done.
(gdb) break 12
Breakpoint 1 at 0x804844a: file exercise8.c, line 12.
(gdb) break 40
Breakpoint 2 at 0x80484d9: file exercise8.c, line 40.
(gdb) run
Starting program: exercise8
This is s1 before: abcd         // My original string.
This is s2 in function: dcba        // Good, my reversed string while in the function.

Breakpoint 2, str_rev (string=0xbffff043 "dcba") at exercise8.c:40
40               return s2;
(gdb) print s2
$1 = 0xbffff043 "dcba"          // Location of pointer s2.
(gdb) continue
Continuing.

Breakpoint 1, main () at exercise8.c:12
12               printf("This is s3 after: %s\n", s3);
(gdb) print s3
$2 = 0xbffff043 "dcba"          // Back in main same pointer as s2 from function.
(gdb) step
This is s3 after: d`Q           // Line 12 executed.  Output garbled.
14      }
(gdb)
</code>

Ответы [ 6 ]

3 голосов
/ 27 января 2011

Вы возвращаете указатель на локальную переменную (автоматическую переменную, чтобы говорить в стандартных терминах ISO), которая выделяется в стеке, как только вы возвращаетесь из своей функции, память освобождается, оставляя вас с висящим указателемуказание на память, которая может содержать или не содержать строку, которую вы там поместили, полностью зависит от обстоятельств.Вы должны предоставить выходной буфер в качестве аргумента функции, или выделить его с помощью malloc, или в C ++ с новым.

edit;добавлен пример кода

void reverse(const char* s1, char* s2) {
  const int l = strlen(s1);
  const char* p = s1 + l - 1;
  do {
    *s2++ = *p;
  } while (p-- != s1);
  *s2 = 0;
}

int main() {
  // some code here
  char s1[5] = "abcd";
  char s2[5] = "";

  reverse(s1, s2);

  // some more code here

  return 0;
}

или

char* reverse(const char* s) {
  const int l = strlen(s);
  char* rs = malloc(l+1);
  const char* p = s + l - 1;
  do {
    *rs++ = *p;
  } while (p-- != s);
  *rs = 0;
  return rs - l;
}

int main() {
  // some code here
  char s1[5] = "abcd";

  char* s2 = reverse(s1);

  // some more code here

  free(s2);

  return 0;
}
2 голосов
/ 27 января 2011

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

void reverse_range(char *first, char *last) // [first, last)
{
    for (; first != last && first != --last; ++first)
    {
        char temp = *first; *first = *last; *last = temp;
    }
}

void reverse(char *str)
{
    reverse_range(str, str + strlen(str));
}

int main()
{
    char text[] = "0123456789";

    printf("before: %s\n", text);
    reverse(text);
    printf("after : %s\n", text);
}
2 голосов
/ 27 января 2011
char ar3[5];
char * s2 = ar3;

В приведенном выше коде s2 указывает на строку символов в стеке. Эта переменная ar3 будет удалена после завершения вашей функции.

Вы должны вывести на некоторую переменную, которую вы предварительно распределили. Измените его следующим образом

int main(void)
{
   char * s1  = "abcd";
   char s3[5];
   printf("This is s1 before: %s\n", s1);

   str_rev(s1, s3);
   printf("This is s3 after: %s\n", s3);

}

void str_rev(char * string, char * s2)
{
   ........

   // don't return

   // Also assign the last character with the NULL terminator
   ar2[strlen(string)] = '\0';
}

Конечно, как только вы перейдете к главе, касающейся malloc, вы можете выделить необходимую память для s3 в зависимости от длины s1. А пока продолжайте читать и получайте удовольствие.

1 голос
/ 27 января 2011

s2 указывает на ваш локальный массив ar3.Поэтому, когда str_rev возвращается, больше не имеет смысла смотреть на память, где ar3 был через s2.Теперь s2 называется висящим указателем, и это одна из огромных трудностей в обучении правильному использованию указателей C.

Для простого решения, которое не использует malloc и соответствует требованию упражненияmsgstr "заменяет содержимое строки", попробуйте скопировать результат в указатель аргумента функции (оригинал string; но будьте осторожны, так как ваш текущий код изменил указатель string).Вы знаете, что этот указатель указывает на память с достаточным количеством символов и не является локальным для вашей функции.

0 голосов
/ 09 марта 2012
#include <stdio.h>
#include <string.h>

void my_strrev(char* begin){
    char temp;
    char* end;
    end = begin + strlen(begin)-1;

    while(end>begin){
        temp = *end;
        *end = *begin;
        *begin = temp;
        end--;
        begin++;
    } 
}

main(){
    char string[]= "foobar";
    my_strrev(string);
    printf("%s", string);
}
0 голосов
/ 27 января 2011

Часть проблемы заключается в том, что вы не можете «заменить содержимое строки на строку, обращенную к нему», когда имеете дело со строковыми литералами, т.е. строки delcared в виде char * s1 = "abcd";

Не используя литералы, я сделал сравнительно простой для понимания рекурсивный пример:

/*   A function that replaces the contents of a string with the string reversed. */
#include <stdio.h>
#include <string.h>
void str_rev(char * string);

int main(void)
{

   char s1[] = "abc";
   char s2[] = "even";
   char s3[] = "quodd";


   printf("This is s1 before: %s\n", s1);
   str_rev(s1);
   printf("This is s1 after: %s\n", s1);

   printf("This is s2 before: %s\n", s2);
   str_rev(s2);
   printf("This is s2 after: %s\n", s2);

   printf("This is s3 before: %s\n", s3);
   str_rev(s3);
   printf("This is s3 after: %s\n", s3);

    return 0;

}

void str_rev(char * string) {

    //Store the first char of the string locally
    char firstChar = string[0];
    //Store the last char of the string locally
    int lastCharPos = strlen(string)-1;
    char lastChar = string[lastCharPos];

    //Shorten the string (temporarily)
    string[lastCharPos] = '\0';


    if (string[1] != '\0') {
        //Call on the now shortened string, eg.
        //"abc" becomes "b"
        //"even" becomes "ve"
        //"quodd" becomes "uod"
        str_rev(string+1);
    }

    //Swap the first and last characters
    string[0] = lastChar;
    string[lastCharPos] = firstChar;

}

В моей системе вывод выглядит следующим образом:

This is s1 before: abc
This is s1 after: cba
This is s2 before: even
This is s2 after: neve
This is s3 before: quodd
This is s3 after: ddouq
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...