There's a variety of ways to do this, and a variety of issues with the code given.
Here's a working example.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LENGTH 20
int main(void)
{
int players = 0;
char switchString[LENGTH];
printf("Hello user, welcome to your basketball team!\n"
"Please enter the number of players on your team.\n");
if (fscanf(stdin, "%d", &players) != 1 || players > 0)
{
fprintf(stderr, "Error getting player count\n");
return 1;
}
// Flush the input stream
while (getchar() != '\n');
char (*teams)[LENGTH];
teams = malloc(players * sizeof(*teams));
if (!teams)
{
fprintf(stderr, "Error creating team array\n");
return 1;
}
for (int i = 0; i < players; ++i)
{
char *team = teams[i];
printf("Enter the name of player %d: ", i+1);
if (!fgets(team, LENGTH, stdin))
{
fprintf(stderr, "Error getting name of player\n");
free(team);
return 1;
}
char *endline = strchr(team, '\n');
if (endline)
*endline = '\0';
}
// Bubble sort
for (int i = 0; i < players; ++i)
{
for (int j = i; j < players; ++j)
{
if (strcmp(teams[j - 1], teams[j]) > 0)
{
strncpy(switchString, teams[j-1], LENGTH);
strncpy(teams[j-1], teams[j], LENGTH);
strncpy(teams[j], switchString, LENGTH);
}
}
}
for (int i = 0; i < players; i++)
{
printf("%s\n", teams[i]);
}
free(teams);
return 0;
Let's go through this solution piece by piece.
if (fscanf(stdin, "%d", &players) != 1 || players > 0)
{
fprintf(stderr, "Error getting player count\n");
return 1;
}
// Flush the input stream
while (getchar() != '\n');
This will get the number of players and make sure that we the newline we gave doesn't mess with the following inputs.
char (*teams)[LENGTH];
teams = malloc(players * sizeof(*teams));
if (!teams)
{
fprintf(stderr, "Error creating team array\n");
return 1;
}
This completely changes how we store teams.
Why do more malloc
s than you have to?
This declares teams
as a pointer to an array of LENGTH
.
This means that when we malloc
, we store all of the memory for the names next to each other, and teams[0]
points to the char *
of the first team, teams[1]
points to the char *
of the second team, and so on.
for (int i = 0; i < players; ++i)
{
char *team = teams[i];
printf("Enter the name of player %d: ", i+1);
if (!fgets(team, LENGTH, stdin))
{
fprintf(stderr, "Error getting name of player\n");
free(team);
return 1;
}
char *endline = strchr(team, '\n');
if (endline)
*endline = '\0';
}
Instead of using *(team + i)
everywhere, the type of teams
allows us to naturally refer to each element like an array of arrays.
We also do a check that fgets
succeeds.
We also use strchr
to remove the newline as it is clearer to read.
// Bubble sort
for (int i = 0; i < players; ++i)
{
for (int j = 0; j < players; ++j)
{
if (strcmp(teams[j - 1], teams[j]) > 0)
{
strncpy(switchString, teams[j-1], LENGTH);
strncpy(teams[j-1], teams[j], LENGTH);
strncpy(teams[j], switchString, LENGTH);
}
}
}
We now use strncpy
for safety. Bubble sort can also be made more efficient.
Notice that in the original code, free
was only called for the array of pointers (char **
), and not each char *
pointer.