1

I am trying to create a game, and at some point when the user come inside the game, there will be created a structure for that player. If the player type its name the game prompts the player with the following:

Type player name:> George
Choose one of the following Otions:
    [+]1) to remove George from player List.
    [+]0) to keep George.

The player need to type 3 names and decide if one or all of them should be removed or keep.

The problem I have is that I cannot keep the created (remained) list if the player decide to delete one or more from list.

Here is a part of the program which can be compiled:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define PERCENT 5
#define LEFT "left"
#define RIGHT "Right"

struct node {
    int player_ID;
    struct node* prev;
    struct node* next;
    char move_player[256];
    char player_name[ 31 ];
} node;

struct node *root;
struct node *current;
struct node *player_node( int player_ID, const char *player_name );
void delete_player ( void *player_list );

int main( void )
{
    char name[256] = { 0 };
    int i = 0, clean;

    root = player_node( PERCENT, RIGHT );
    current =  root;

    do{
        struct node *player = player_node( PERCENT, LEFT );
        player->prev = current;
        current->next = player;
        current = player;

        printf( "Type player name:> ");
        if ( fgets( name, 256, stdin ) == NULL )
        {
            printf("Error, fgets()\n");
            exit( EXIT_FAILURE );
        }
        name[ strcspn( name, "\n" ) ] = '\0';
        strncpy( player->player_name, name, strlen( name ) );
        printf( "Choose one of the following Otions:\n" );
        printf( "\t[+]1) to remove %s from player List.\n", current->player_name );
        printf( "\t[+]0) to keep %s.\n", current->player_name );


        int opt = 0;
        if ( scanf( "%d", &opt) != 1 )
        {
            printf("Error, scanf()\n");
        }else if ( opt == 1 )
        {
            delete_player ( player );
        }
        while ( ( clean = getchar() ) != '\n' && clean != EOF );
        i++;
    }while( i < 3 );

    struct node *tmp = root->next;
    while ( tmp != NULL )
    {
        printf( "Name = %s", tmp->player_name );
        tmp = tmp->next;
    }
    free( root );
}

struct node *player_node( int player_ID, const char *const movement )
{
    struct node *player = malloc( sizeof( struct node ) );
    player->prev = NULL;
    player->next = NULL;
    player->player_ID = player_ID;
    strncpy( player->move_player, movement, strlen( movement) );
    strncpy( player->player_name, "NULL", 5 );
    return player;
}

void delete_player ( void *player_list )
{
    struct node *local_client = (struct node* )player_list;
    if ( local_client == current )
    {
        current = local_client->prev;
        current->next = NULL;
    } else
    {
        local_client->prev->next = local_client->next;
        local_client->next->prev = local_client->prev;
    }
    free( local_client );
}

If the code reaches this:

struct node *tmp = root->next;
while ( tmp != NULL )
{
    printf( "Name = %s", tmp->player_name );
}

There are no remain players in the list, because the List is not printed.

Why is the list empty?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 1
    `strncpy(player->move_player, movement, strlen(movement));`: you don't copy the NUL terminator. Just write `strcpy(player->move_player, movement)`. Also be aware that `strncpy` is not at all a safer alternative of `strcpy` read _carefully_ the documentation of `strncpy`, it does not what you think it does. There are most likely other errors. – Jabberwocky Nov 20 '18 at 13:22

1 Answers1

1

Use strcpy() instead of strncpy(). The latter is not safer than the the former.

Change:

strncpy(player->player_name, name, strlen( name ));

to:

strcpy(player->player_name, name);

Do the same for this call:

strncpy( player->move_player, movement, strlen( movement) );

since in both cases you are not copying the NULL-terminator.

Then, when printf(), or any standard string function handles your string, it doesn't know when to stop...

The core of your problem was that [strlen()]3 computes the length of the string, without taking into account the NULL terminator:

The length of a C string is determined by the terminating null-character: A C string is as long as the number of characters between the beginning of the string and the terminating null character (without including the terminating null character itself).

I would also change strncpy( player->player_name, "NULL", 5 ); to be homogeneous.


If you must use strncpy(), then just add 1 in the third parameter, like so:

strncpy(player->player_name, name, strlen( name ) + 1);

However, keep in mind that using strncpy() in this code is pointless, and only decreases readability, by adding complexity and making the code less clean.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • err. the usage of `strncpy` here is pointless. `strcpy` is sufficient. `strncpy( player->player_name, name, strlen( name ) + 1);` is equivalent to `strcpy( player->player_name, name);` – Jabberwocky Nov 20 '18 at 14:02
  • @Jabberwocky I would use `strcpy()` myself. In fact the first version of this answer used that function. However, after reading [Why should you use strncpy instead of strcpy?](https://stackoverflow.com/questions/1258550/why-should-you-use-strncpy-instead-of-strcpy) and thinking of the minimum changes OP had to do to them code, edited with using `strncpy()`, since it might be a good habit to get to know that method too... I will edit my question again, but you think it can improve, please let me know! – gsamaras Nov 20 '18 at 14:08
  • 1
    `strncpy( player->move_player, movement, strlen( movement) + 1);` might look safer than `strcpy`, but it isn't.... – Jabberwocky Nov 20 '18 at 14:09
  • @Jabberwocky it isn't, you are right. I rolled back my answer, thank you for really helping me improve this answer! – gsamaras Nov 20 '18 at 14:18
  • @gsamaras Sorry for the late response, How do I free it after, because if I do this: `free( root->next );` and then this `free( root );` still get one malloc not FREE()ed from valgrind ==>> `total heap usage: 6 allocs, 5 frees, 3,296 bytes allocated` –  Nov 20 '18 at 15:18
  • You should call free as many times as you malloced. Did you do that @MichaelB.? Thanks for the upvote! – gsamaras Nov 20 '18 at 15:25
  • @gsamaras Well, I need to figure out where the problem is :D. `free( root->next->next );` , `free( root->next );` and `free( root );` is the job here :|. Thank you –  Nov 20 '18 at 15:41
  • 1
    @MichaelB. good luck. I sometimes check the [list management](https://gsamaras.wordpress.com/code/list-c/) our professor 8 years ago taught as, to remind me what I should do. It might useful for you to check the free function there. If you have problems though, please post a *new* question, and if you want, share with me the link, so I can help, hope that helps! – gsamaras Nov 20 '18 at 15:49