提问人:Sangjun Lee 提问时间:1/23/2023 最后编辑:Vlad from MoscowSangjun Lee 更新时间:1/24/2023 访问量:155
C 函数内部结构下数组的动态分配
C dynamic allocation of an array under struct inside a function
问:
我有一个结构,它将包含一些动态分配的数组。
我已经编写了以下代码,它可以工作,但我不明白为什么它会起作用。
#include <stdio.h>
#include <stdlib.h>
struct Tray {
int *parr;
};
int allocateTray(int n, struct Tray *tray) {
tray->parr = calloc(n, sizeof(*tray->parr));
for (int i = 0; i<n; i++) {
tray->parr[i] = i+1;
}
}
int main() {
struct Tray tray = {NULL}, *ptray;
int n;
n = 5;
ptray = &tray;
allocateTray(n, ptray);
for (int i = 0; i<n; i++) {
printf("ptray->parr[%d] = %d \n", i, ptray->parr[i]);
}
return 0;
}
使用数组(不在结构体内),即使我在带有参数的函数中分配,它也不会给出分配的数组,并且会强制人们在那里使用双指针。arr
int *arr
main
但在这种情况下,我只是使用指向结构的指针,它起作用了。我在想我应该使用类似双指针的东西到结构。
为什么在这种情况下,它只适用于单个指针?
答:
“即使我在带有参数 int *arr 的函数中分配 arr,它也不会给 main 分配的数组”
通常,函数在通过其参数列表传递时需要修改的任何对象都需要传递对象的地址,而不是对象本身。举例来说,对于任何类型 T:
如果要改变,函数原型的参数应该是;
调用示例为T s
s
void func(T *s);
T s = 0;
func(&s);
如果要改变,函数原型的参数应该是;
调用示例为T *s
*s
void func(T **s);
T *s = 0;
func(&s);
如果要改变,函数原型的参数应该是;
调用示例为T **s
**s
void func(T ***s);
T **s = 0;
func(&s);
等等......(请注意,每个调用约定的明显相似性。
示例 - 以下代码将无法更改其参数的值:
int main(void)
{
int x = 0;//object to be changed
change_x(x);//passing object directly via argument
//x is returned unchanged
return 0;
}
void change_x(int x)
{
x = 10;//within this function only will x now contain 10
}
但此示例传递 address 并能够更改值:
int main(void)
{
int x = 0;//object to be changed
change_x(&x);//passing the address of the object to be changed
return 0;
}
void change_x(int *x)
{
*x = 10;//access to the object via its address allows change to occur
}
“我在想我应该使用类似双指针的东西来指向结构。”
是的,作为函数原型中的参数,当需要更改指针对象指向的内存内容时,该参数将起作用。
对于需要在函数中修改的指针(指向任何对象),也是如此,必须传递其地址,而不是指针本身。然后,这将要求该函数的参数容纳指向指针的指针。一个简单的例子,使用 with members 和 a member:struct
int
int *
typedef struct {
int a;
int b;
int *parr;
}val_s;
void change_val(val_s **v, size_t num_parr);
int main(void)
{
val_s *val = NULL;
int num = 10;
change_val(&val, num);//passing address to a pointer
val->a = 10;
val->b = 20;
for(int i = 0;i < num; i++) val->parr[i] = i;
//once finished using memory,
//free it in the reverse order in which it was allocated
free(val->parr);
free(val);
return 0;
}
void change_val(val_s **v, size_t num)//note only top level pointer address needs be send
{ //member pointers, whether allocated or not are
(*v) = malloc(sizeof(val_s)); //relative to memory of top level object
if(*v)
{
(*v)->parr = malloc(num*sizeof (*v)->parr);//allocate memory to member pointer
}
}
你不需要“双指针”;您需要一个指向要修改的对象的指针。如果希望函数修改指针对象,则需要将指针传递到该对象。如果希望函数修改结构,则需要传递指向该对象的指针。
而这正是你的代码所做的。您要修改 的 ,因此将指针传递给 的 。所以你的代码是有效的。main
tray
main
tray
下面演示了这一点。
首先,让我们看看不起作用的代码。
void fac( int *ac ) {
ac = malloc( sizeof( int ) );
}
int main( void ) {
int *a = NULL;
fac( a );
printf( "%p\n", (void *)a ); // XXX `a` isn't modified.
}
此代码修改 ,但不修改 。它没有实现我们希望它实现的目标。要修改 ,我们需要传递 的地址。ac
a
a
a
void fap( int **ap ) {
*ap = malloc( sizeof( int ) );
}
int main( void ) {
int *a = NULL;
fap( &a );
printf( "%p\n", (void *)a ); // XXX `s` isn't modified.
}
在这里,我们不是修改参数,而是修改 .这段代码确实实现了我们希望它实现的目标。*ap
a
现在,让我们介绍结构。
typedef struct { int *a } S;
void fsc( S sc ) {
sc.a = malloc( sizeof( int ) );
}
int main( void ) {
S s = { .a = NULL };
fsc( s );
printf( "%p\n", (void *)(s.a) ); // Ok. `s` is modified.
}
此代码修改 ,但不修改 。它没有实现我们希望它实现的目标。要修改 ,我们需要传递 的地址。sc
s
s
s
typedef struct { int *a } S;
void fsp( S *sp ) {
sp->a = malloc( sizeof( int ) ); // `sp->a` means `(*sp).a`
}
int main( void ) {
S s = { .a = NULL };
fsp( &s );
printf( "%p\n", (void *)(s.a) ); // Ok. `s` is modified.
}
在这里,我们不是修改参数,而是修改 .这段代码确实实现了我们希望它实现的目标。*sp
s
完整演示:
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef struct { int *a; } S;
void fac( int *ac ) {
ac = malloc( sizeof( int ) );
}
void fap( int **ap ) {
*ap = malloc( sizeof( int ) );
}
void fsc( S sc ) {
sc.a = malloc( sizeof( int ) );
}
void fsp( S *sp ) {
sp->a = malloc( sizeof( int ) ); // `sp->a` means `(*sp).a`
}
int main( void ) {
int *a = NULL;
fac( a );
printf( "%p\n", (void *)a ); // XXX `a` isn't modified.
fap( &a );
printf( "%p\n", (void *)a ); // Ok. `a` is modified.
S s = { .a = NULL };
fsc( s );
printf( "%p\n", (void *)(s.a) ); // XXX `a` isn't modified.
fsp( &s );
printf( "%p\n", (void *)(s.a) ); // Ok. `s` is modified.
}
警告:
<source>: In function 'fac':
<source>:10:16: warning: parameter 'ac' set but not used [-Wunused-but-set-parameter]
10 | void fac( int *ac ) {
| ~~~~~^~
<source>: In function 'fsc':
<source>:18:13: warning: parameter 'sc' set but not used [-Wunused-but-set-parameter]
18 | void fsc( S sc ) {
| ~~^~
输出:
(nil)
0x158a2d0
(nil)
0x158a310
要更改函数中的对象,您需要通过引用而不是按值传递它。
在 C 语言中,通过引用传递意味着通过指向对象的指针间接传递对象。因此,取消引用传递的指针,您将直接访问指针指向的对象并可以更改它。
使用数组(不在结构体内),即使我在 带有参数 int *arr 的函数,它不会给 main 分配的 数组,它迫使人们在那里使用双指针。
这意味着指针按值传递给函数。也就是说,该函数处理其局部变量(参数),该变量由传递的指针的值初始化。更改局部变量不会更改用作函数参数的原始指针。
考虑一个简单的程序
#include <stdio.h>
#include <stdlib.h>
void f( int *p )
{
p = malloc( sizeof( int ) );
}
int main( void )
{
int *a = NULL;
f( a );
printf( "a == NULL is %s\n", a == NULL ? "true" : "false" );
}
您可以按如下方式想象函数定义及其调用
f( a );
//...
void f( /* int *p */ )
{
int *p = a;
p = malloc( sizeof( int ) );
}
如您所见,该函数更改了其局部变量,该变量由传递给该函数的指针的值初始化。原始指针保持不变。p
a
a
如果要更改函数中 main 中声明的指针,则需要通过引用传递它。也就是说,程序将如下所示a
#include <stdio.h>
#include <stdlib.h>
void f( int **p )
{
*p = malloc( sizeof( int ) );
}
int main( void )
{
int *a = NULL;
f( &a );
printf( "a == NULL is %s\n", a == NULL ? "true" : "false" );
free( a );
}
现在取消引用函数中的指针p
*p = malloc( sizeof( int ) );
您可以直接访问原始指针,因此可以更改它。a
至于你的第一个程序,那么指针在结构中声明parr
Tray
struct Tray {
int *parr;
};
通过指向结构类型的对象的指针通过引用传递给函数。
struct Tray tray = {NULL}, *ptray;
//...
ptray = &tray;
allocateTray(n, ptray);
也就是说,取消引用函数中的指针,您可以直接访问结构类型的原始对象,并可以更改(任何)其数据成员。ptray
Gerhardh 的评论是正确的:您正在使用双指针。
考虑:
#include <stdio.h>
#include <stdlib.h>
struct Tray {
int *parr;
};
void allocateTray(int n, struct Tray *tray) {
tray->parr = calloc(n, sizeof(*tray->parr));
for (int i = 0; i<n; i++) {
tray->parr[i] = i+1;
}
}
int main() {
int *tray = NULL, **ptray;
int n;
n = 5;
ptray = &tray;
allocateTray(n, (struct Tray*)ptray);
for (int i = 0; i<n; i++) {
printf("ptray[%d] = %d \n", i, (*ptray)[i]);
}
return 0;
}
评论
allocateTray(n, &tray); for (int i = 0; i<n; i++) { printf("tray.parr[%d] = %d \n", i, tray.parr[i]); }
ptray
struct Tray *tray
NULL
double *dp; int **ipp
dp
ipp