简体   繁体   中英

C - How to pass struct pointer array to a function by reference

I have this struct:

typedef struct {
    char name[31];
    int played;
    int won;
    int lost;
    int tie;
    int points;
} Player;

And this function which fill the struct array with data from file:

int load(Player *players[], int max_players, int *player_count)
{
    static const char filename[] = "players.txt";
    FILE *file = fopen(filename, "r");

    if (file != NULL)
    {
        char line[128]; 

        players = malloc(max_players * sizeof *players);

        while (1) /* read until end of file */
        {

            players[*player_count] = malloc(sizeof(Player));

            if (*player_count < max_players && fgets(players[*player_count]->name, sizeof players[*player_count]->name, file) != NULL)
            {
                fscanf(file, "%d", &players[*player_count]->played);    // read played
                fscanf(file, "%d", &players[*player_count]->won);       // read won 
                fscanf(file, "%d", &players[*player_count]->lost);      // read lost 
                fscanf(file, "%d", &players[*player_count]->tie);       // read tie 
                fscanf(file, "%d", &players[*player_count]->points);    // read points 
                fgets(line, sizeof line, file);                         // read new line

                // increase player count
                *player_count += 1;
            }
            else
            {
                break;
            }
        }

        fclose(file);
    }
    else
    {
        return 0;
    }
    return 1;
}

Now I am having a problem with calling it by passing players as a reference so that the updated data of players gets reflected at the calling end.

Below is my calling code, which I think has the problem:

Player *players[MAX_PLAYERS] = { NULL };
int playerCount = 0;
load(players, MAX_PLAYERS, &playerCount);

When I debug the code, the players' array gets filled in the function but when it returns back, the players' value is still null.

Any help would be appreciated.

You are overwriting the local variable players .

Remove the below line from the function you don't need it.

   players = malloc(max_players * sizeof *players);|

As you already have the array of pointers in main .


You don't need array of pointers of type Player you just need array of type Player

Player *players;
load(&players, MAX_PLAYERS, &playerCount);

And in load function.

int load(Player **players, int max_players, int *player_count)
{
    static const char filename[] = "players.txt";
    FILE *file = fopen(filename, "r");

    if (file != NULL)
    {
        char line[128]; 

        (*players) = malloc(max_players * sizeof **players);

        while (1) /* read until end of file */
        {


            if (*player_count < max_players && fgets((*players)[*player_count].name, sizeof (*players)[*player_count].name, file) != NULL)
            {
                fscanf(file, "%d", &(*players)[*player_count].played);    // read played
                fscanf(file, "%d", &(*players)[*player_count].won);       // read won 
                fscanf(file, "%d", &(*players)[*player_count].lost);      // read lost 
                fscanf(file, "%d", &(*players)[*player_count].tie);       // read tie 
                fscanf(file, "%d", &(*players)[*player_count].points);    // read points 
                fgets(line, sizeof line, file);                         // read new line

                // increase player count
                *player_count += 1;
            }
            else
            {
                break;
            }
        }

        fclose(file);
    }
    else
    {
        return 0;
    }
    return 1;
}

C does not support passing a variable by reference.

I just kept it simple. Your function should look like this:

int load(Player *players, int max_players, int *player_count)
{
    static const char filename[] = "players.txt";
    FILE *file = fopen(filename, "r");

    if (file != NULL)
    {
        char line[128]; 

        while (!feof(file ) && !ferror(file )) /* read until end of file */
        {
            fscanf(file, "%d", &players[*player_count].played);    // read played
            fscanf(file, "%d", &players[*player_count].won);       // read won 
            fscanf(file, "%d", &players[*player_count].lost);      // read lost 
            fscanf(file, "%d", &players[*player_count].tie);       // read tie 
            fscanf(file, "%d", &players[*player_count].points);    // read points 
            fgets(line, sizeof line, file);                         // read new line

            // increase player count
            *player_count += 1;
        }

        fclose(file);

        return 1;
    }

    return 0;
}

and main:

int main ()
{
    Player players[MAX_PLAYERS] = { NULL };
    int playerCount = 0;
    load(players, MAX_PLAYERS, &playerCount);
    printf("");
}

the following proposed code:

  1. has not been compiled as the OP did not post a minimal, complete, verifiable example
  2. properly passes the parameters
  3. properly checks for errors
  4. performs the desired functionality
  5. shows how to initialize the pointer in main()
  6. shows how to update the pointer in main()
  7. follows the plan that unrecoverable errors cause the code to output to stderr all the error information, then exits
  8. follows the 'typical' returned value of '0' for success
  9. be sure, in main() to pass all the allocated memory pointers to free() to avoid a memory leak

and now, the proposed code modifications:

regarding:

    typedef struct 
    {
        char name[31];
        int played;
        int won;
        int lost;
        int tie;
        int points;
    } Player;

this anonymous struct will be very difficult to display 
the individual fields via a debugger,
because debuggers use the 'tag' name of the struct 
to reference the individual fields


in main function: Notice only declaring a pointer initialized to NULL,
then passing the address of the pointer to the function: `load()`

    Player *players =  NULL;
    int playerCount = 0;
    load(&players, &playerCount);  

notice the double '**' on the 'players' parameter
This enables the sub function to modify the pointer field in the caller

    int load(Player **players, int *player_count)
    {
        static const char filename[] = "players.txt";

        // it is poor programming practice to name a variable the
        // same as a struct, so changed `file` to `fp`
        FILE *fp = fopen(filename, "r");
        if( !fp )
        {
            perror( "fopen to read players.txt failed" );
            exit( EXIT_FAILURE );
        }

        // implied else, fopen successful

        // increased the size of the input buffer `line[]` for safety
        char line[1024]; 

        // note: used `calloc()` so when cleaning up from error
        //       no need to check if a specific entry in the 'player'
        //       array is used.  `free()` handles a NULL parameter just fine
        *players = calloc( MAX_PLAYERS, sizeof(Player*) );
        if( !*players )
        {
            perror( "calloc for array of pointers to players failed" );
            exit( EXIT_FAILURE );
        }

        // implied else, malloc successful

        /* read until end of file  or array full*/
        while (*player_count < MAX_PLAYERS && fgets(line, sizeof line, fp)) 
        {
            (*players)[*player_count] = malloc(sizeof(Player));
            if( !(*players)[ *player_count ] )
            {
                perror( "malloc for individual player failed" );
                for( int i=0; i<MAX_PLAYERS; i++ )
                {
                    free( (*players)[i] );
                }
                free( *players ); 
                exit( EXIT_FAILURE );
            }

            // implied else, malloc successful

            if( sscanf( line, "%d %d %d %d %d", 
                players[*player_count]->played,    // read played
                players[*player_count]->won,       // read won 
                players[*player_count]->lost,      // read lost 
                players[*player_count]->tie,       // read tie 
                players[*player_count]->points) != 5 )   // read points 
            {
                fprintf( stderr, "extraction of player fields failed\n" );
                exit( EXIT_FAILURE );
            }


            // increase player count
            (*player_count) += 1;
        }

        fclose(file);

        return 0;
    }

I THINK YOUR FUNCTION SHOULD BE WRITED LIKE THIS

int func(int ***players){
    *players=new int* [MAXSIZE];
    //use this if use c
    // *players=(int**)malloc(sizeof(int*)*MAXSIZE);
    for(int i=0;i<MAXSIZE;i++){
        *(*players+i)=new int[2];
        // *(*players+i)=(int*)malloc(sizeof(int)*2); 
        //2 is test can choose every number if your computer allow
    }
}

and you main should like this main:

int main(){

    int **players=nullptr;
    func(&players);
    //use follow if you must delete 
    for(int i=0;i<MAXSIZE;i++){
        delete players[i];
    }
    delete players;
    return 0;
}
typedef struct {    int played;} Player;


void load(Player* &players, int max_players, int &player_count)
{
    players = (Player*)malloc(max_players * sizeof(Player));

    for (int i = 0; i < max_players; i++)
    {
        players[i].played = i;
        player_count++;
    }
}

int main()
{
    const int MAX_PLAYERS=3;
    Player* players=  NULL ;
    int playerCount = NULL;
    load(players, MAX_PLAYERS, playerCount);

    //...
    free(players);
}
  • struct:
typedef struct Foo {
  int x;
} Foo;

function:

void f(Foo **foo) {
  foo[0]->x = 2021;                // already allocate in main
  foo[1]    = malloc(sizeof(Foo)); // not locate yet
  foo[1]->x = 2022;
}
  • main:
int main() {
  Foo **foo = malloc(100 * sizeof(Foo *)); // foo[10]

  foo[0]    = malloc(sizeof(Foo));         // must allocate before using
  foo[0]->x = 2020;                        // assing 2021 to foo[0].x must use arrow for pointer

  f(foo);
  foo[1]->x = 2024;                        // already allocate in function f();

  printf("main: value of x: %d\n", foo[0]->x);
  printf("main: value of x: %d\n", foo[1]->x);
}
  • Output:
$ gcc test.c && ./a.out
value of x: 2021
value of x: 2024

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM