簡體   English   中英

C:malloc 字符串顯示意外行為與雙指針和 function 調用

[英]C: malloc of string showing unexpected behaviour with double pointer and function calls

這是一個查詢,用於了解以下代碼即使出現錯誤也能正常工作。

據我所知,如果我想重新分配/重新分配傳遞給 function 的指針,則該指針需要作為雙指針傳遞。 錯誤地,我傳遞了一個指針,程序仍在運行。 我想它必須與指針作為字符串有關。

程序:

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

void func2( char **x){

    printf("befor func2 x = %u; *x = %u; **x = %s; x_size = %u\n", x, *x, *x, strlen(*x));
    free(*x);
    *x = (char *)malloc(20);
    strcpy(*x, "zyxwvutsrqponmlkjih");

    printf("\n\nafter func2 x = %u; *x = %u; **x = %s; x_size = %u\n", x, *x, *x, strlen(*x));
}

void func1( char *x){

    printf("befor func1 &x = %u; x = %u; *x = %s; x_size = %u \n", &x, x, x, strlen(x));
    func2(&x);
    printf("after func1 &x = %u; x = %u; *x = %s; x_size = %u \n", &x, x, x, strlen(x));
}

int main(){

    char *x; 
    x = (char *)malloc(10);
    strcpy(x, "abcdefghi");
    printf("befor  main &x = %u; x = %u; x = %s; x_size = %u\n", &x, x, x, strlen(x));
    func1(x);
    printf("after  main &x = %u; x = %u; x = %s; x_size = %u\n", &x, x, x, strlen(x));
    free(x);
    return 1;
}

OutPut:

befor  main &x = 489275896; x = 20414480; x = abcdefghi; x_size = 9
befor func1 &x = 489275864; x = 20414480; *x = abcdefghi; x_size = 9 
befor func2 x = 489275864; *x = 20414480; **x = abcdefghi; x_size = 9


after func2 x = 489275864; *x = 20414480; **x = zyxwvutsrqponmlkjih; x_size = 19
after func1 &x = 489275864; x = 20414480; *x = zyxwvutsrqponmlkjih; x_size = 19 
after  main &x = 489275896; x = 20414480; x = zyxwvutsrqponmlkjih; x_size = 19

我可以理解 output 直到func1 但是在func2中修改后,大小和值如何返回到main 我沒有將x作為雙指針從main傳遞到func1 但不知何故,它仍然有效。
是因為它是char *嗎?

編輯1:

在評論中建議編輯后:

程序:

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

void func2( char **x){

    printf("befor func2 x = %p; *x = %p; **x = %s; x_size = %u\n", x, *x, *x, strlen(*x));
    free(*x);
    *x = (char *)malloc(20);
    strcpy(*x, "zyxwvutsrqponmlkjih");

    printf("\n\nafter func2 x = %p; *x = %p; **x = %s; x_size = %u\n", x, *x, *x, strlen(*x));
}

void func1( char *x){

    printf("befor func1 &x = %p; x = %p; *x = %s; x_size = %u \n", &x, x, x, strlen(x));
    func2(&x);
    printf("after func1 &x = %p; x = %p; *x = %s; x_size = %u \n", &x, x, x, strlen(x));
}

int main(){

    char *x, *y, *z; 
    x = (char *)malloc(10);
    z = (char *)malloc(100);
    y = (char *)malloc(100);
    strcpy(x, "abcdefghi");
    printf("befor  main &x = %p; x = %p; x = %s; x_size = %u\n", &x, x, x, strlen(x));
    func1(x);
    printf("after  main &x = %p; x = %p; x = %s; x_size = %u\n", &x, x, x, strlen(x));
    free(x);
    free(y);
    free(z);
    return 1;
}

Output:

befor  main &x = 0x7fff78cb09c8; x = 0x1c7a010; x = abcdefghi; x_size = 9
befor func1 &x = 0x7fff78cb09a8; x = 0x1c7a010; *x = abcdefghi; x_size = 9 
befor func2 x = 0x7fff78cb09a8; *x = 0x1c7a010; **x = abcdefghi; x_size = 9


after func2 x = 0x7fff78cb09a8; *x = 0x1c7a010; **x = zyxwvutsrqponmlkjih; x_size = 19
after func1 &x = 0x7fff78cb09a8; x = 0x1c7a010; *x = zyxwvutsrqponmlkjih; x_size = 19 
after  main &x = 0x7fff78cb09c8; x = 0x1c7a010; x = zyxwvutsrqponmlkjih; x_size = 19

該程序在引入多個 malloc 后仍然有效。

你所擁有的是未定義的行為

以下是程序的重要部分(使用重命名的變量以便能夠在函數中區分它們):

void func2(char **x)
{
    free(*x);
    *x = malloc(SOME_OTHER_SIZE);
}

void func1(char *y)
{
    func2(&y);
}

int main(void)
{
    char *z = malloc(SOME_SIZE);
    func1(z);
}

main function 中分配一些 memory,並使z指向它。

然后調用func1按值傳遞指針z ,這意味着指針被復制到func1參數變量y中。 現在你有兩個指針指向同一個 memory: zmain function 和yfunc1 function。

然后func1調用func2 ,但它通過不傳遞y中值的副本而是傳遞指向變量y本身的指針來模擬按引用傳遞。 func2 free 是*x指向的 memory 時,它會使指針*xyz無效。 然后它重新分配*x以指向一些新的 memory。 這將改變y指向的位置,但不會改變z ,這仍然是無效的。

func1返回指針z不再有效時,任何取消引用它的嘗試都將導致所述未定義行為


從圖形上看,它可以是這樣的:

  1. main function分配memory並使z指向它:

     +---+ +-----------+ | z | --> | Memory... | +---+ +-----------+
  2. 調用 function func1 ,傳遞z的副本:

     +---+ | z | -\ +---+ \ +-----------+ >-> | Memory... | +---+ / +-----------+ | y | -/ +---+
  3. 調用 function func2 ,將指針傳遞給y

     +---+ | z | -\ +---+ \ +-----------+ >-> | Memory... | +---+ +---+ / +-----------+ | x | --> | y | -/ +---+ +---+
  4. function func2 free 是*x指向的 memory :

     +---+ | z | -\ +---+ \ >->??? +---+ +---+ / | x | --> | y | -/ +---+ +---+
  5. function func2分配新的 memory 並使*x (因此y )指向它:

     +---+ | z | -->??? +---+ +---+ +---+ +---------------+ | x | --> | y | --> | New memory... | +---+ +---+ +---------------+

從上面應該很容易看出為什么func2中的free(*x)也會使main function 中的z無效。

現在有趣的部分,這就是為什么 function 中的z指向的main似乎已更改:這似乎是 ZCD69B4957F06CD818D7BF3D61980E291 新分配器的一個怪癖分配。 重要的一點是z仍然無效。

您似乎認為使用char *x (單指針)作為func1的參數是錯誤的。 不過,我覺得完全沒問題。 func1期望一個指向 char 作為參數的指針,或者基本上是一個 memory 地址,當取消引用時會給出一個字符或一堆字符。 當你寫func1(x); 在 main 中,您傳遞的是 x,一堆字符的地址,這正是func1所期望的參數類型。

為什么 x 是一堆字符的地址? 在這種情況下,指針x存儲一個數組(字符)的地址。 現在,你可能知道,如果你只寫一個數組的名字,你就會得到數組的基地址。 請參閱以下代碼:

#include <stdio.h>
#include <stdlib.h>

int main () {
    int arr[5] = {1, 2, 3, 4, 5};
    printf ("%d\n", arr);       // address of the first element (1) of the array
                                // or, the base address of the array
    printf ("%d\n", &arr[0]);   // same as above
    printf ("%d\n\n", *arr);      // gives the first element of the array

    int *x = malloc (5*sizeof (int));
    *x = 1;
    *(x+1) = 2;
    *(x+2) = 3;
    *(x+3) = 4;
    *(x+4) = 5;
    printf ("%d\n", x);        // address of the first element (1) of the array
                               // or, the base address of the array
    printf ("%d\n", &*(x+0));  // same as above
    printf ("%d\n\n", *x);     // gives the first element of the array
    return 0;
}

output如下:

6356712
6356712
1

9768168
9768168
1

現在,為什么我們可以修改 x 指向的字符數組中的值? 因為我們已經傳遞了該數組的基地址。 我們可以取消引用該地址以訪問數組並使用它做任何我們想做的事情。

暫無
暫無

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

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