字符串的C:malloc,用双指针和函数调用显示意外行为



这是一个查询,用于了解以下代码如何在出现错误的情况下正常工作。

据我所知,如果我想重新分配/重新分配传递给函数的指针,该指针需要作为双指针传递。由于错误,我传递了一个指针,程序仍在运行。我想它一定与指针是字符串有关。

程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void func2( char **x){
printf("befor func2 x = %u; *x = %u; **x = %s; x_size = %un", x, *x, *x, strlen(*x));
free(*x);
*x = (char *)malloc(20);
strcpy(*x, "zyxwvutsrqponmlkjih");
printf("nnafter func2 x = %u; *x = %u; **x = %s; x_size = %un", 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 = %un", &x, x, x, strlen(x));
func1(x);
printf("after  main &x = %u; x = %u; x = %s; x_size = %un", &x, x, x, strlen(x));
free(x);
return 1;
}

输出:

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

直到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 = %un", x, *x, *x, strlen(*x));
free(*x);
*x = (char *)malloc(20);
strcpy(*x, "zyxwvutsrqponmlkjih");
printf("nnafter func2 x = %p; *x = %p; **x = %s; x_size = %un", 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 = %un", &x, x, x, strlen(x));
func1(x);
printf("after  main &x = %p; x = %p; x = %s; x_size = %un", &x, x, x, strlen(x));
free(x);
free(y);
free(z);
return 1;
}

输出:

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函数中,您可以分配一些内存,并使z指向它

然后调用func1,通过值传递指针z,这意味着指针被复制到func1自变量变量y中。现在有两个指针指向同一内存:main函数中的zfunc1函数中的y

然后func1调用func2,但它通过不传递y中值的副本,而是传递指向变量y本身的指针来模拟逐引用传递。当func2释放了*x指向的内存时,它会使指针*xyz无效。然后,它重新分配*x以指向某个新的内存。这将更改y所指向的位置,但不会更改z,后者仍然无效。

func1返回指针z不再有效时,任何取消引用它的尝试都将导致所述未定义行为


从某种程度上可以看出:

  1. main函数分配内存并使z指向它:

    +---+     +-----------+
    | z | --> | Memory... |
    +---+     +-----------+
    
  2. 调用函数func1,传递z:的副本

    +---+
    | z | -
    +---+       +-----------+
    >-> | Memory... |
    +---+   /    +-----------+
    | y | -/
    +---+
    
  3. 调用函数func2,将指针传递给y:

    +---+
    | z | -
    +---+       +-----------+
    >-> | Memory... |
    +---+     +---+   /    +-----------+
    | x | --> | y | -/
    +---+     +---+
    
  4. 函数func2free是*x:指向的内存

    +---+
    | z | -
    +---+       
    >-> ???
    +---+     +---+   /    
    | x | --> | y | -/
    +---+     +---+
    
  5. 函数func2分配新的内存,并使*x(因此y(指向它:

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

从上面可以很容易地看出为什么func2中的free(*x)也会使main函数中的z无效。

现在有趣的是,这就是为什么main函数中z指向的内存似乎发生了变化:这似乎是系统中内存分配器的一个怪癖,它将新分配映射到与旧分配相同的位置。重要的一点是z仍然无效。

您似乎认为使用char *x(单个指针(作为func1的参数是一个错误。不过,我认为这完全没问题。func1期望有一个指向char的指针作为参数,或者基本上是一个内存地址,当被取消引用时,它会给出一个char或一堆chars。当您在main中编写func1(x);时,您正在传递x,一组字符的地址,这正是func1所期望的参数类型。

为什么x是一堆字符的地址?在这种情况下,指针x存储(字符(数组的地址。现在,您可能知道,如果只写一个数组的名称,就可以得到该数组的基地址。请参阅以下代码:

#include <stdio.h>
#include <stdlib.h>
int main () {
int arr[5] = {1, 2, 3, 4, 5};
printf ("%dn", arr);       // address of the first element (1) of the array
// or, the base address of the array
printf ("%dn", &arr[0]);   // same as above
printf ("%dnn", *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 ("%dn", x);        // address of the first element (1) of the array
// or, the base address of the array
printf ("%dn", &*(x+0));  // same as above
printf ("%dnn", *x);     // gives the first element of the array
return 0;
}

输出如下:

6356712
6356712
1
9768168
9768168
1

现在,为什么我们能够修改x指向的字符数组中的值?因为我们已经传递了该数组的基地址。我们可以取消引用该地址以访问数组,并对其执行任何操作。

最新更新