Kevin Frostad Kevin Frostad - 1 month ago 7
C Question

While loop assignment in C don't work as expected

Trying to print all names read from a file ordered alphabetically.

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

int compare(const void *a, const void *b){
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
}

int main(int argc, char* argv[]){

//Takes input in terminal/cmd (filename.txt).
char *input = malloc(sizeof(char) * 50);
if (argc < 2){
printf("Enter filename: ");
scanf("%s", input);
} else
input = argv[1];
//.......................


int count = 0; //Names count
char *names[100]; //Pointers to each name in file
char str[50];
FILE *file;
file = fopen(input, "r");

//Allocate space for the pointers
for (int i = 0; i < 100;i++){
names[i] = malloc(sizeof(char) * (50 + 1));
}

//Adding names to array
if (file) {
while ((fgets(str, 50, file)) != NULL){
count++;
names[count] = str;
printf("%s", names[count]); //This will print names as read from file
}
fclose(file);
printf("%s", names[2]); //This will print the last name read with the while loop no matter the index of names[]

} else printf("Can't read from file");

//Sorting "names" in alphabetical order
int length = sizeof(names)/sizeof(char*); //length of names
qsort(names, length, sizeof(char*), compare);

//printing each name (will be the last string read by while loop times the length)
for(int i = 0; i < length; i++){
printf("%s", names[i]);

}
printf("%d", count); //printing names count
}


Compiling and running the program gives me this output:

Tredigar
Ghorvas
Wolvar
Lurtrum
Sabakzar
Hagan
Korlag
Malagar
Ferrek
Baelnar
Grimmalk
Roken
Vabul
Radek
Agaro
Krag
Balfam
Vistrum
Halzar
Maulnar
Auxlan
Krim
Borkul
Thorin
Morak
Arnan
Garmul
Avamir
Darkul
Bariken
Mardam
Hlant
Rogath
Melgar
Thorbalt
Dyrnar
Ezegan
Smethykk
Sharak
Swargar
Halagmar
Rozag
Orobok
Arval
Kurman
Erag
Dolmen
Glint
Haeltar
Haeltar
8┼Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
Haeltar
49


Obviously I'm trying to sort the strings in alphabetical order. My current problem is that wherever I print names[] outside the while loop the pointers all point to Haeltar which is the last name assigned in the while loop. I've searched far and wide but haven't been able to figure out what's causing this behavior. I am relatively new to C programming.

FIX:

Using strcpy() instead of names[count] = str;

if (file) {
while ((fgets(str, 50, file)) != NULL){
strcpy(names[count], str);
count++;
}
fclose(file);

}

Answer

This names[count] = str; assigns every member of your array the address of str. They all point to it. So naturally they all point to "Haeltar", which is what was last written into str. Note that in doing the assignment, you also leak all the allocated memory too.

You'll need to either define names as char names[100][50] and read directly into it fgets(names[count], 50, file). Or allocate memory for each member (using malloc, like you already did) and copy into it (using strcpy)1.


1 If you are lucky enough to be on a POSIX system, strdup can do both in a single call.