簡體   English   中英

使用函數中的指針反轉字符串,main中的輸出亂碼

[英]Reverse a string using pointers in a function, output in main is garbled

我正在嘗試使用Stephen Prata的C Primer Plus自學C,並且本章最后的練習之一是“編寫一個將字符串內容替換為反向字符串的函數”。 這是一章有關字符串的章節,其中包含大量的指針。 我正在嘗試盡可能多地使用指針,以便我能更好地理解,但我遇到了麻煩。

我的問題是,當我在main中打印返回指針的值時,出現亂碼。

當我使用gdb時(也只是學習如何使用它),我可以看到我的函數返回的內存地址與該函數中使用的地址相同,並且在我可以的最大范圍內將其分配給了我的指針告訴。

我嘗試了很多事情,我想念什么? FWIW我還沒有在書中學到malloc,盡管我經常在試圖更好地理解C的各種www頁面上看到它。


$ 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)

您將返回指向在堆棧上分配的局部變量(以ISO標准術語表示的自動變量)的指針,一旦從函數返回,則釋放了內存,留下了一個指向內存的懸空指針它可能包含也可能不包含您放置在其中的字符串,這完全取決於環境。 您應該將輸出緩沖區作為函數參數提供,或者使用malloc分配它,或者在C ++中使用new來分配它。

編輯; 添加了一些示例代碼

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;
}
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的章節,就可以根據s1的長度為s3分配必要的內存。 在此之前,請繼續閱讀並獲得樂趣。

問題描述聽起來像您可以將字符串反過來就位。 把事情簡單化。

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

s2指向您的本地ar3陣列。 因此,當str_rev返回時,通過s2查看ar3所在的內存不再有效。 現在s2被稱為懸掛指針,這是學習正確使用C指針的巨大痛苦之一。

對於不使用malloc且滿足“替換字符串的內容”的練習要求的簡單解決方案,請嘗試將結果復制到函數參數指針(原始string ;但是請小心,因為當前的代碼已更改了指針string )。 您知道此指針指向具有足夠字符的內存,並且不在您的函數中。

問題的一部分是,在處理字符串文字時,例如,實際上不能“用相反的字符串替換字符串的內容”。 char * s1 = "abcd";形式分配的字符串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
#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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM