这是一个查询,用于了解以下代码如何在出现错误的情况下正常工作。
据我所知,如果我想重新分配/重新分配传递给函数的指针,该指针需要作为双指针传递。由于错误,我传递了一个指针,程序仍在运行。我想它一定与指针是字符串有关。
程序:
#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
函数中的z
和func1
函数中的y
。
然后func1
调用func2
,但它通过不传递y
中值的副本,而是传递指向变量y
本身的指针来模拟逐引用传递。当func2
释放了*x
指向的内存时,它会使指针*x
、y
和z
无效。然后,它重新分配*x
以指向某个新的内存。这将更改y
所指向的位置,但不会更改z
,后者仍然无效。
当func1
返回指针z
不再有效时,任何取消引用它的尝试都将导致所述未定义行为。
从某种程度上可以看出:
main
函数分配内存并使z
指向它:+---+ +-----------+ | z | --> | Memory... | +---+ +-----------+
调用函数
func1
,传递z
:的副本+---+ | z | - +---+ +-----------+ >-> | Memory... | +---+ / +-----------+ | y | -/ +---+
调用函数
func2
,将指针传递给y
:+---+ | z | - +---+ +-----------+ >-> | Memory... | +---+ +---+ / +-----------+ | x | --> | y | -/ +---+ +---+
函数
func2
free是*x
:指向的内存+---+ | z | - +---+ >-> ??? +---+ +---+ / | x | --> | y | -/ +---+ +---+
函数
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指向的字符数组中的值?因为我们已经传递了该数组的基地址。我们可以取消引用该地址以访问数组,并对其执行任何操作。