C动态分配函数内结构下的数组

dnph8jn4  于 2023-01-25  发布在  其他
关注(0)|答案(4)|浏览(193)

我有一个包含动态分配数组的结构。
我已经写了下面的代码,它工作,但我不明白为什么它确实工作。

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

对于数组(不在结构体中),即使我在带参数int *arr的函数中分配arr,它也不会给予main分配的数组,而是强制main在那里使用双指针。
但是在这个例子中,我只是使用了一个指向结构体的指针,它工作了,我在想我应该使用一些类似双指针的东西来指向结构体。
为什么在这种情况下,它只与一个指针?

oug3syen

oug3syen1#

  • "即使我在参数为int * arr的函数内分配arr,它也不会将分配的数组赋给main"*

一般来说,无论函数通过参数列表传递时要修改的对象是什么,都需要传递对象的 * address *,而不是对象本身。
对于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
}

但是这个例子传递地址并且能够改变值:

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 
}
  • "我在想,我应该使用类似双指针的东西来指向一个结构。"*

是的,作为函数原型中的一个参数,当需要更改指针对象所指向的内存内容时,它将起作用。
对于需要在函数中修改的指针(指向任何对象),情况也是如此,必须传递它的 * address *,而不是指针本身。这就需要该函数的参数容纳指向指针的指针。下面是一个简单的例子,使用structint成员以及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
    }
}
t98cgbkg

t98cgbkg2#

你不需要"双指针";你需要一个指向你要修改的对象的指针。2如果你想让函数修改一个指针对象,你需要传递一个指向那个对象的指针。3如果你想让函数修改一个结构,你需要传递一个指向那个对象的指针。
这正是你的代码所做的,你想修改maintray,所以你传递一个指针到maintray,所以你的代码可以工作。
下面的例子说明了这一点。
首先,让我们看一下不起作用的代码。

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。这段代码确实实现了我们希望它实现的目标。
Full demo

#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
mznpcxlj

mznpcxlj3#

要更改函数中的对象,需要通过引用传递,而不是通过值传递。
在C语言中,通过引用传递意味着通过一个指向对象的指针间接传递对象。因此,通过解引用传递的指针,你将直接访问指针所指向的对象,并可以更改它。
对于一个数组(不在结构体中),即使我在一个参数为int *arr的函数中分配arr,它也不会给予main分配的数组,而是强制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,从而可以改变它。
对于你的第一个程序,在结构Tray中声明的指针parr

struct Tray {
  int *parr;
};

通过指向结构类型的对象的指针以引用方式传递给函数。

struct Tray tray = {NULL}, *ptray;
  //...
  ptray = &tray;

  allocateTray(n, ptray);

也就是说,在函数中取消引用指针ptray,您可以直接访问结构类型的原始对象,并可以更改(任何)其数据成员。

uklbhaso

uklbhaso4#

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

相关问题