I'm new in c and sorry for my poor English.
I'm trying to write a program that ask to user if he want enter data (region, date of detection, mm of rain) using keyboard and save it in file or if he want give it file's name.
No problem at this time and file is written or read correctly.
File have this structure:
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
....
Try with scanf() (not report main because there is no problem in it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum mese_e {Gen=1, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic} tipo_mese;
typedef struct data_s
{
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s
{
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s
{
tipo_dati_file* array;
int count;
} tipo_ritorna;
int conta_righe(char* Nome_f)
{
int i=0;
char c;
FILE* file;
file=fopen(Nome_f,"r");
while ((c=fgetc(file))!=EOF)
{if(c=='\n')
i++;}
fclose(file);
return i;
}
void crea_array (char* Nome_f)
{
int i,n;
char* regione= (char*)malloc(sizeof(char));
tipo_data data;
int mm_pioggia;
tipo_ritorna risultati;
FILE* file;
n = conta_righe(Nome_f);
printf("%d\n",n);
tipo_dati_file* array = (tipo_dati_file*) malloc (n*sizeof (tipo_dati_file));
file = fopen(Nome_f,"r");
if( file==NULL )
{
printf("Errore in apertura del file!");
exit(1);
}
for(i=0; i<=n; i++)
{
fscanf(file,"%s %d/%d/%d %d\n",regione, &data.giorno, &data.mese, &data.anno, &mm_pioggia);
strcpy(array[i].regione, regione);
array[i].data.giorno=data.giorno;
array[i].data.mese= data.mese;
array[i].data.anno= data.anno;
array[i].mm_pioggia= mm_pioggia;
printf("%s %d/%d/%d %d\n",array[i].regione,array[i].data.giorno, array[i].data.mese,array[i].data.anno,array[i].mm_pioggia);
}
fclose(file);
}
try with fgets()
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef enum mese_e {Gen=1, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic} tipo_mese; typedef struct data_s {
int giorno;
tipo_mese mese;
int anno; } tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia; } tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count; } tipo_ritorna;
int conta_righe(char* Nome_f) {
int i=0;
char c;
FILE* file;
file=fopen(Nome_f,"r");
while ((c=fgetc(file))!=EOF)
{if(c=='\n')
i++;}
fclose(file);
return i;
} void crea_array (char* Nome_f, int v) {
int i=0,s;
char* r;
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file* array = (tipo_dati_file*) malloc (v*sizeof (tipo_dati_file));
file = fopen(Nome_f,"r");
if( file==NULL )
{
printf("Errore in apertura del file!");
exit(1);
}
if (feof(file)==0)
{
char* buf= (char*) malloc(v*sizeof(char));
/*while ( fgets( buf,10000, file) != NULL )
{
r = sscanf( buf, "%s% d/%d/%d %d\n", array[i].regione, &array[i].data.giorno, &array[i].data.mese, &array[i].data.anno, &array[i].mm_pioggia);
printf("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno, array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}*/
while(1)
{
r=fgets( buf,1000, file);
if (r!=NULL)
{
printf("%s",buf);
sscanf( buf, "%s% d/%d/%d %d\n", array[i].regione, &array[i].data.giorno, &array[i].data.mese, &array[i].data.anno, &array[i].mm_pioggia);
printf("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno, array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
else exit(1);
}
}
else exit(1);
fclose(file); }
You have two primary problems I see in crea_array,
You declare char* r; but then attempt to assign the return of sscanf (buf, "%s %d/%d/%d %d\n", ... (e.g. r = sscanf (.... This is incorrect. sscanf returns type int representing the number of successful conversions that took place as specified in your format string (e.g. "%s %d/%d/%d %d\n" would return 5 on success, and remove the '\n', it will cause problems). Your compiler should be screaming warnings at you. If not, you need to enable compiler warnings by adding -Wall -Wextra -pedantic as compiler options and do not accept code until it compiles without a single warning.
crea_array must be declared as type tipo_dati_file * and it must return array; at the end. You must assign the return to a pointer back in the caller. You must also free (buf); before the return or you have just created a memory leak as there is no way to free() the memory you allocated for buf after the function returns. (further, if you are simply allocating 1000-char each time, just use a fixed buffer, e.g. char buf[1000]; and eliminate the need to allocate buf completely.
Putting it altogether, you could do something similar to:
#define MAXC 1000 /* if you need a constant, #define one (or more) */
tipo_dati_file *crea_array (char* Nome_f, int v)
{
int i = 0,
s,
r;
char buf[MAXC] = "";
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file *array = malloc (v * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
exit (EXIT_FAILURE);
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
exit (EXIT_FAILURE);
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", array[i].regione,
&array[i].data.giorno, &array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fput ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
printf ("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno,
array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
fclose (file);
return array;
}
Note: I have not compiled the code above.
Then in main, you could do something similar to:
tipo_dati_file *array = crea_array (name, v);
(note: you should also pass a 3rd parameter of type int *numelem so you can assign *numelem = i; before return making the number of elements filled available back in the caller if less than v are actually read)
If you will post a A Minimal, Complete, and Verifiable Example (MCVE), along with a sample data file (10 lines or so), I'm happy to help further and I can validate the code works -- as I will have something I can compile and run.
Edit Following Warnings Posted (in comments)
The two warnings are addressed in detail in the comments below the answer. Once those are resolved, you will run into a horrible SegFault as you are not allocating storage for array[i].regione.
In you nested set of structs:
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
regione is an uninitialized pointer that points to some indeterminate memory location (which you do not own). When you attempt to write characters there with sscanf (BOOM - SegFault -- most likely).
You have two choices (1) declare regione as a fixed array, e.g. char regione[CONST] (inefficient), or (2) read the string for regione into a temporary buffer and then allocate storage for strlen + 1 chars and copy the string from the temporary buffer to the new block of memory and assign the starting address for that block to regione (you can use strlen/malloc/memcpy or strdup -- if you have it, it does all three)
With that fix and a few tweaks to your crea_array function (like passing a pointer to int to hold the number of struct filled in array instead of v), you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXDATA 64
typedef enum mese_e { Genv= 1, Feb, Mar, Apr, Mag, Giu,
Lug, Ago, Set, Ott, Nov, Dic
} tipo_mese;
typedef struct data_s {
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count;
} tipo_ritorna;
tipo_dati_file *crea_array (char *Nome_f, int *nelem)
{
int i = 0,
r;
char region[MAXC] = ""; /* temp buffer to hold array[i].regione */
char buf[MAXC] = "";
FILE* file;
tipo_dati_file *array = malloc (MAXDATA * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
return NULL;
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
return NULL;
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", region,
&array[i].data.giorno, (int*)&array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fputs ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
array[i].regione = strdup (region);
if (!array[i].regione) { /* strdup allocates - you must validate */
perror ("strdup-array[i].regione");
for (int j = 0; j < i; j++) /* on failure free prior mem */
free (array[j].regione); /* and return NULL */
free (array);
return NULL;
}
i++;
}
fclose (file);
*nelem = i; /* update nelem with number of struct filled */
return array;
}
int main (int argc, char **argv) {
int index = 0,
nelem = 0;
char *datafile = argc > 1 ? argv[1] : "dat/staterain.txt";
tipo_ritorna statistics[MAXDATA] = {{ .array = NULL }};
statistics[index].array = crea_array (datafile, &nelem);
if (statistics[index].array && nelem > 0) {
statistics[index].count = nelem;
for (int i = 0; i < statistics[index].count; i++) {
printf ("%-12s %02d/%02d/%4d %3d\n",
statistics[index].array[i].regione,
statistics[index].array[i].data.giorno,
statistics[index].array[i].data.mese,
statistics[index].array[i].data.anno,
statistics[index].array[i].mm_pioggia);
free (statistics[index].array[i].regione); /* free strings */
}
free (statistics[index].array); /* free array */
}
return 0;
}
Example Use/Output
$ ./bin/staterain
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/staterain
==3349== Memcheck, a memory error detector
==3349== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3349== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3349== Command: ./bin/staterain
==3349==
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
==3349==
==3349== HEAP SUMMARY:
==3349== in use at exit: 0 bytes in 0 blocks
==3349== total heap usage: 5 allocs, 5 frees, 2,110 bytes allocated
==3349==
==3349== All heap blocks were freed -- no leaks are possible
==3349==
==3349== For counts of detected and suppressed errors, rerun with: -v
==3349== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
Related
I'm trying to use strsep to remove extra characters in an CSV file. The problem is that when I run it, it gives me Segmentation Fault and I can't figure out why. Here's the code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
typedef struct {
int id, followers, following, public_gists;
int public_repos;
char *login;
char *type;
char *created_at;
int *follower_list;
int *following_list;
} *User;
void checkUsersFile();
FILE *createCSV();
void corrigirFicheiro();
User criarUser();
int count = 0;
void checkUsersFile() {
//Ficheiro "users.csv"
FILE *file = fopen("ficheirosG1/users-set2.csv", "r");
//Verifica se o ficheiro "users.csv" existe
if(!file) {
printf("Ficheiro não encontrado");
return;
}
//Cria ficheiro "users-ok.csv"
FILE *newFile = createCSV("users-ok.csv");
corrigirFicheiro(file, newFile);
printf("%d\n", count);
}
//Cria e retorna ficheiro "users-ok.csv"
FILE *createCSV(char *nome) {
FILE *file = fopen(nome, "w");
return file;
}
//Função responsável por intrepretar o ficheiro "users.csv" e colocar os dados corretos no ficheiro "users-ok.csv"
void corrigirFicheiro(FILE *file, FILE *newFile) {
//imprimirPrimeiraLinha(file, newFile);
char string[200000];
//Uma linha do ficheiro com, no máximo, 200.000 caracteres
while ((fgets(string, 200000, file))) {
if (string[0] != '\0') {
//1. Criar user
//2. Print user
User user = criarUser(&string);
if (user != NULL) {
printf("ok\n");
}
free(user);
}
}
//free(string);
}
//Cria um User a partir de uma linha do ficheiro
User criarUser(char *str) {
User novoUser;
novoUser = (User) malloc(sizeof(User));
for(int i = 0; i<10; i++) {
//char *a = strdup(strsep(&str, ";"));
//char *b = strdup(strsep(&a, "\n"));
char *p = strsep(&str, ";\n\r");
if (strlen(p) == 0) {
count++;
free(novoUser);
return NULL;
}
}
return novoUser;
}
int main(){
checkUsersFile();
return 0;
}
Using gdb to debug the code, it says that it occurs in the line if(strlen(p) == 0 {
So it doesn't even enter the switch case.
I don't know why this is happening.
Thank you
I see no reason to think that the strsep() call is responsible for the error you encounter.
This is wrong, however:
User novoUser = (User) malloc(sizeof(User));
and it very likely is responsible for your error.
User is a pointer type, so sizeof(User) is the size of a pointer, which is not large enough for a structure of the kind that a User points to. When you later try to assign to the members of the structure to which it points (omitted) or to access them in printUser() (also omitted), you will overrun the bounds of the allocated object. That's exactly the kind of thing that might cause a segfault.
An excellent idiom for expressing an allocation such as that uses the receiving variable to establish the amount of space to allocate:
User novoUser = malloc(sizeof(*novoUser));
Note that I have also removed the unneeded cast.
As I expressed in comments, however, it is poor style to hide pointer nature behind a typedef, as your User does, and personally, I don't much care even for most typedefs that avoid that pitfall.
Here is how you could do it with a better typedef:
typedef struct {
int id, followers, following, public_gists;
int public_repos;
char *login;
char *type;
char *created_at;
int *follower_list;
int *following_list;
} User; // not a pointer
// ...
User *criarUser(char *str) {
// ...
User *novoUser = malloc(sizeof(*novoUser)); // Note: no change on the right-hand side
// ...
return novoUser;
}
But this is how I would do it, without a typedef:
struct user {
int id, followers, following, public_gists;
int public_repos;
char *login;
char *type;
char *created_at;
int *follower_list;
int *following_list;
};
// ...
struct user *criarUser(char *str) {
// ...
struct user *novoUser = malloc(sizeof(*novoUser)); // still no change on the right-hand side
// ...
return novoUser;
}
In answer to you question I wrote a short example that illustrates what is needed. In essence you need to allocate storage for string and pass the address of string to criarUser(). You cannot use an array because the type passed to criarUser() would be pointer-to-array not pointer-to-pointer. (note: you can use the array so long as it is allowed to decay to a pointer so taking the address does not result in pointer-to-array -- example at end below)
An (close) example of corrigirFicheiro() with needed changes to use with allocated storage is:
void corrigirFicheiro(FILE *file, FILE *newFile)
{
char *string = malloc (200000); /* allocate for string */
/* valdiate allocation here */
char *original_ptr = string; /* save original pointer */
while ((fgets(string, 200000, file))) {
if (string[0] != '\0') {
User *user = criarUser(&string); /* pass address of string */
if (user != NULL) {
printUser(user, newFile);
}
free(user);
}
}
free (original_ptr); /* free original pointer */
}
An abbreviated struct was used in the following example, but the principal is the same. For sample input, you can simply pipe a couple of lines from printf and read on stdin and write to stdout. I used:
$ printf "one;two;3\nfour;five;6\n" | ./bin/strsep_example
A short MCVE (where I have taken liberty to remove the typedef'ed pointer) would be
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 128
#define DELIM ";\r\n"
#define NFIELDS 3
typedef struct {
char login[MAXC];
char type[MAXC];
int public_repos;
} User;
void printUser (User *user, FILE *newFile)
{
fprintf (newFile, "\nlogin : %s\ntype : %s\nrepo : %d\n",
user->login, user->type, user->public_repos);
}
User *criarUser(char **str)
{
User *novoUser = malloc(sizeof *novoUser);
/* validate allocation here */
for(int i = 0; i<NFIELDS; i++) {
char *p = strsep(str, DELIM);
switch (i) {
case 0: strcpy (novoUser->login, p);
break;
case 1: strcpy (novoUser->type, p);
break;
case 2: novoUser->public_repos = atoi(p);
break;
}
if (strlen(p) == 0) {
free(novoUser);
return NULL;
}
}
return novoUser;
}
void corrigirFicheiro(FILE *file, FILE *newFile)
{
char *string = malloc (200000); /* allocate for string */
/* valdiate allocation here */
char *original_ptr = string; /* save original pointer */
while ((fgets(string, 200000, file))) {
if (string[0] != '\0') {
User *user = criarUser(&string); /* pass address of string */
if (user != NULL) {
printUser(user, newFile);
}
free(user);
}
}
free (original_ptr); /* free original pointer */
}
int main (int argc, char **argv) {
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
corrigirFicheiro(fp, stdout);
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Example Use/Output
$ printf "one;two;3\nfour;five;6\n" | ./bin/strsep_example
login : one
type : two
repo : 3
login : four
type : five
repo : 6
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ printf "one;two;3\nfour;five;6\n" | valgrind ./bin/strsep_example
==6411== Memcheck, a memory error detector
==6411== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6411== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6411== Command: ./bin/strsep_example
==6411==
login : one
type : two
repo : 3
login : four
type : five
repo : 6
==6411==
==6411== HEAP SUMMARY:
==6411== in use at exit: 0 bytes in 0 blocks
==6411== total heap usage: 5 allocs, 5 frees, 205,640 bytes allocated
==6411==
==6411== All heap blocks were freed -- no leaks are possible
==6411==
==6411== For counts of detected and suppressed errors, rerun with: -v
==6411== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have questions.
Using An Array
When you pass the array as a parameter it decays to a pointer by virtue of array/pointer conversion. If that is done and the "array" type is no longer associated with the pointer you can then take the address and use automatic storage with strsep() as pointed out by #thebusybee.
The changes to the program above to do so would be:
User *criarUser(char *str)
{
User *novoUser = malloc(sizeof *novoUser);
/* validate allocation here */
for(int i = 0; i<NFIELDS; i++) {
char *p = strsep(&str, DELIM);
switch (i) {
case 0: strcpy (novoUser->login, p);
break;
case 1: strcpy (novoUser->type, p);
break;
case 2: novoUser->public_repos = atoi(p);
break;
}
if (strlen(p) == 0) {
free(novoUser);
return NULL;
}
}
return novoUser;
}
void corrigirFicheiro(FILE *file, FILE *newFile)
{
char string[200000];
while ((fgets(string, 200000, file))) {
if (string[0] != '\0') {
User *user = criarUser(string); /* pass string, as pointer */
if (user != NULL) {
printUser(user, newFile);
}
free(user);
}
}
}
But note, without the benefit of array/pointer conversion when passing string as a parameter, you must use allocated storage. Up to you, but know the caveat.
Hey :) I need some help with my code, which I think is mostly correct but I am having trouble figuring out where I am going wrong.
#include <stdio.h>
#include <stdlib.h>
int num_count(FILE* ptr){
int count = 0;
int numHolder = 0;
while((fscanf(ptr, "%d", &numHolder)) == 1){
count++;
}
return count;
}
void load_nums(FILE* ptr, int *codedPtr, int ncount){
int number = 0;
ncount = ncount - 1;
for(int i = 0; i <= ncount; i++){
fscanf(ptr, "%d", &number);
printf("%d", number);
*(codedPtr + i) = number;
}
return;
}
void decode(int *codedPtr, int ncount, char *decodedPtr){
char temp;
ncount = ncount - 1;
for(int i = 0; i <= ncount; i++){
temp = ((*(codedPtr + i) + *(codedPtr + (ncount - i))) + '0');
*decodedPtr = temp;
decodedPtr++;
}
return;
}
int main(int argc, char *argv[]){
int *codedPtr;
char *decodedPtr;
FILE *fp;
if (argc == 2){
fp = fopen(argv[1], "r+");
}
if(argc <= 1){
printf("Invalid command line: cmd infile outfile\n");
}
int numCount = num_count(fp);
printf("%d", *codedPtr);
codedPtr = (int*)calloc(numCount, sizeof(int));
decodedPtr = (char*)calloc(numCount, sizeof(char));
load_nums(fp, codedPtr, numCount);
decode(codedPtr, numCount, decodedPtr);
printf("\n%s\n\n", decodedPtr);
fclose(fp);
return(0);
}
I added some print functions to trouble shoot, and during the load_nums function the printf functions continuously prints 0's, it is not reading in the correct integer values from the file pointed to.
Could any of you help particularly with the load_nums function? Thank you all and let me know if you need any extra information. "-6 -76 53 -34 32 79 142 55 177 78" is what is in the file pointed to.
You are making things much more complicated than they need to be. You are dynamically allocating storage for both codedPtr and decodedPtr, there is no need to make two passes through the file (one to count integers, and one to read after allocation). Your decode is much more complex than necessary and there is a logic error. Adding '0' (it's not necessary in this case -- though normally it is to convert a decimal digit to its ASCII character value)
To address load_nums, change the return type to int * and allocate for codedPtr within load_nums using realloc as needed to increase the size of your allocated block of memory. Then return a pointer to the allocated block of memory holding your int values. Pass ncount as a pointer (e.g. int *ncount) so you can update the value at that address with the number of integers read so that the count is available back in the calling function (main() here).
Approaching allocation in this manner reduces your file I/O to a single-pass through the file (and file I/O is one of the most time consuming operations) Further, you completely eliminate the need for a num_count() function.
Putting those pieces together, you could do:
/* read integers from fp, dynamically allocating storage as needed,
* return pointer to allocated block holding integers and make ncount
* available through update pointer value.
*/
int *load_nums (FILE* fp, int *ncount)
{
int *codedPtr, avail = 2; /* declare pointer & no. to track allocated ints */
*ncount = 0; /* zero the value at ncount */
/* allocate avail no. of int to codedPtr - validate EVERY allocation */
if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
perror ("malloc-codedPtr");
return NULL;
}
while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) { /* read each int */
if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
/* always realloc to a temporary pointer */
void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
if (!tmp) { /* validate that realloc succeeds */
perror ("realloc-codedPtr");
return codedPtr; /* original codedPtr vals available on failure */
}
codedPtr = tmp; /* assign new block of mem to codedPtr */
avail *= 2; /* update avail with no. of int allocated */
}
}
return codedPtr; /* return pointer to allocated block of memory */
}
You would call the function in main() as, codedPtr = load_nums (fp, &numCount). You can wrap it in an if(...) statement to determine whether the allocation and read succeeded or failed:
int *codedPtr = NULL, numCount = 0;
...
if (!(codedPtr = load_nums (fp, &numCount))) /* read file/validate */
return 1;
(there is no need to pass codedPtr from main(). You can further validate by checking numCount > 0 -- that is left to you)
For your decode function, simply set up the for loop use two loop variables to iterate from the beginning and end towards the middle. This greatly simplifies things, e.g.
void decode (int *codedPtr, int ncount, char *decodedPtr)
{
/* loop from ends to middle adding values, + '0' NOT required */
for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
decodedPtr[i] = codedPtr[i] + codedPtr[j];
}
(i starts at the first integer value and j at the last. Don't use *(codePtr + i) instead use codePtr[i] -- though equivalent, index notation is easier to read)
In main() you can alternatively open the file provided as the first argument to your program or read from stdin by default if no argument is provided (this is the way many Linux utilities work). Adding a simple ternary is all you need. Whether you are reading input or allocating memory (or using any function that is necessary for the continued correct operation of your code), you cannot use that function correctly unless you check the return to determine if the operation succeeded or failed. Lesson: validate, validate, validate....
Putting it altogether, you could do:
#include <stdio.h>
#include <stdlib.h>
/* read integers from fp, dynamically allocating storage as needed,
* return pointer to allocated block holding integers and make ncount
* available through update pointer value.
*/
int *load_nums (FILE* fp, int *ncount)
{
int *codedPtr, avail = 2; /* declare pointer & no. to track allocated ints */
*ncount = 0; /* zero the value at ncount */
/* allocate avail no. of int to codedPtr - validate EVERY allocation */
if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
perror ("malloc-codedPtr");
return NULL;
}
while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) { /* read each int */
if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
/* always realloc to a temporary pointer */
void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
if (!tmp) { /* validate that realloc succeeds */
perror ("realloc-codedPtr");
return codedPtr; /* original codedPtr vals available on failure */
}
codedPtr = tmp; /* assign new block of mem to codedPtr */
avail *= 2; /* update avail with no. of int allocated */
}
}
return codedPtr; /* return pointer to allocated block of memory */
}
void decode (int *codedPtr, int ncount, char *decodedPtr)
{
/* loop from ends to middle adding values, + '0' NOT required */
for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
decodedPtr[i] = codedPtr[i] + codedPtr[j];
}
int main(int argc, char *argv[]) {
int *codedPtr = NULL, numCount = 0;
char *decodedPtr = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!(codedPtr = load_nums (fp, &numCount))) /* read file/validate */
return 1;
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!(decodedPtr = malloc (numCount + 1))) { /* allocate/validate */
perror ("malloc-decodedPtr"); /* don't forget room for '\0' */
return 1;
}
decode (codedPtr, numCount, decodedPtr); /* decode the message */
decodedPtr[numCount] = 0; /* nul-terminate */
puts (decodedPtr); /* output decoded message */
free (codedPtr); /* don't forge to free what you allocate */
free (decodedPtr);
}
Example Use/Output
Testing your program, you find the decoded message is "Hello", e.g
$ echo "-6 -76 53 -34 32 79 142 55 177 78" | ./bin/codedptr
Hello
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ echo "-6 -76 53 -34 32 79 142 55 177 78" | valgrind ./bin/codedptr
==32184== Memcheck, a memory error detector
==32184== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32184== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==32184== Command: ./bin/codedptr
==32184==
Hello
==32184==
==32184== HEAP SUMMARY:
==32184== in use at exit: 0 bytes in 0 blocks
==32184== total heap usage: 7 allocs, 7 frees, 5,251 bytes allocated
==32184==
==32184== All heap blocks were freed -- no leaks are possible
==32184==
==32184== For counts of detected and suppressed errors, rerun with: -v
==32184== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I know this same question has been asked a hundred times before, but they either don't help me or they aren't answered.
I want to read a text file which has some integers in a format like this:
1;50
2;40
3;180
this file can go forever so I can't create an array with a fixed size. So far the summarized part of what I have done (full code isn't like this, I have checked if files aren't null, made file pointers, put them in different functions etc.) :
int **mymatrix;
mymatrix =(int **) malloc(sizeof(int*)*1);
fscanf(file, "%d", &mymatrix[0]);
fscanf(file, ";%d", &mymatrix[1]);
and to print that:
printf("%d", *mymatrix[0]);
printf(" %d", *mymatrix[0]);
I have looked some similar questions and learned the malloc line from them.
I have tried doing fscanf(file, "%d;%d", something) and replaced something with every possible combination of *, **, &, && and also tried [0], [1] but still can't read anything.
I don't need that print part on my code (also tried every possible combinations but no luck). I had placed breakpoints after scanf's but Visual Studio shows mymatrix as < unable to read memory >.
So, there should be a combination of scanf that I haven't tried. If anyone can help me on that part, I greatly appreciate it.
Like this:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *fp = fopen("matrix.txt", "r");
if(!fp){
perror("fopen");
return 1;
}
int d1, d2, rows = 0;
while(2 == fscanf(fp, "%d;%d", &d1, &d2))
++rows;
int **matrix = malloc(rows * sizeof(*matrix));
rewind(fp);
rows = 0;
while(2 == fscanf(fp, "%d;%d", &d1, &d2)){
matrix[rows] = malloc(2 * sizeof(**matrix));
matrix[rows][0] = d1;
matrix[rows++][1] = d2;
}
fclose(fp);
//print and free'd
for(int r = 0; r < rows; ++r){
printf("%d %d\n", matrix[r][0], matrix[r][1]);
free(matrix[r]);
}
free(matrix);
return 0;
}
realloc version.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *fp;
int **matrix = NULL;
int d1, d2, r, rows = 0;
fp = fopen("data.txt", "r");
if(!fp){
perror("fopen");
return 1;
}
while(2 == fscanf(fp, "%d;%d", &d1, &d2)){
matrix = realloc(matrix, (rows+1)*sizeof(*matrix));//In the case of large files increase the size that you want to extend. It must have control of the timing.
matrix[rows] = malloc(2 * sizeof(**matrix));
matrix[rows][0] = d1;
matrix[rows][1] = d2;
++rows;
}
fclose(fp);
//print and free'd
for(r = 0; r < rows; ++r){
printf("%d %d\n", matrix[r][0], matrix[r][1]);
free(matrix[r]);
}
free(matrix);
return 0;
}
First, int **mymatrix; is not a matrix/2D array and cannot represent one.
Said that, you should use a pointer to a 1D array/matrix:
// avoid magic numbers!
#define COLS 2
// Points to the matrix. Starts without matrix
int (*mymatrix)[COLS] = NULL;
Despite its type, it can point to a 2D array. As a general rule, a "pointer to an array of N dimensions" can be used to address an "array of N+1 dimensions.
// the amount to grow the array (min. 1, but that is inefficient)
#define GROW_LENGTH 10
// this holds the total number of rows in the array
size_t length = 0;
// row to store next entry
size_t row = 0;
// buffer for input data
int buffer[COLS];
// read data until failure
while ( scanf("%d;%d", &buffer[0], &buffer[1]) == 2 ) {
if ( row >= length ) {
// enlarge the array for another block
int (*p)[COLS] = realloc(mymatrix,
sizeof(*mymatrix) * (length + GROW_LENGTH));
if ( p == NULL ) {
// realloc failed
// release the matrix and terminate (can be changed to more inteligent behaviour)
free(mymatrix);
exit(1);
}
// update variables
mymatrix = p;
length += GROW_LENGTH;
}
// store the data into the matrix
mymatrix[row][0] = buffer[0];
mymatrix[row][1] = buffer[1];
// next position in buffer
row++;
}
if ( mymatrix == NULL ) {
// nothing has been read
}
// process the data.
// `row` contains the number of rows with data
Don't forget to release the array when done:
free(mymatrix);
The code above is a fragment. It requires some standard headers and a function, of course. Best is to wrap the reading part into its own function with a clean interface to the caller. It also reads from stdin; change to fscanf is simple.
Also the printing part is straight-forward, just loop over all rows. and print each column.
Note that this code will allocate at most GROW_LENGTH - 1 unused rows. Set to 1 to have no overhead at all, but that is less efficient, as realloc is called fore every row. The best balance depends on the application, OS, etc.
First, your arguments to fscanf don't match the format string. mymatrix[0] is an int *, so &mymatrix[0] is an int **. Compiling with -Wall -Wextra will warn you of this.
Also, you allocate space for a 1 element array of int *, but then you don't populate that pointer.
You need to allocate an array of 2 int to assign to the first element of mymatrix, then pass the address of each of those to fscanf:
int **mymatrix;
mymatrix = malloc(sizeof(int*)*1); // don't cast the return value of malloc
mymatrix[0] = malloc(sizeof(int)*2); // same here
fscanf(file, "%d", &mymatrix[0][0]);
fscanf(file, ";%d", &mymatrix[0][1]);
Then you print them like this:
printf("%d", mymatrix[0][0]);
printf(" %d", mymatrix[0][1]);
When reading each subsequent line, you'll need to realloc instead of malloc and keep track of how many lines you have and which line you're on.
In addition to all of the other fine answers, why not use a pointer to array of int 2 (or whatever the size of your elements are)? In your circumstance, this is an optimal approach. There is no need to use a pointer to pointer to int. That complicates allocation and freeing of the memory. A pointer to array gives you single allocation of blocks, single freeing of all allocated memory and the 2D indexing you desire.
If you are reading from your file and storing a collection of integer pairs, you need only use a pointer to array, e.g.
int (*arr)[2] = NULL;
That makes allocation with either malloc or calloc a single call to allocate storage an initial number of pairs, and makes freeing the memory a single call. For example, if you have a variable maxn of 64, then to allocate a block of memory to hold the first 64 pairs of integers read from the file, you need only:
arr = calloc (maxn, sizeof *arr);
There is no need for a separate call to allocate storage for every 2-integers, when your reach your initial limit of 64, you simply realloc your array and keep going. The following uses a constant MAXN to realloc for an additional 64 pairs each time the current index idx reaches the limit maxn (the new block of memory is also zeroed):
if (++idx == maxn) {
printf ("\n reallocating %zu to %zu\n", maxn, maxn + MAXN);
size_t szelem = sizeof *arr;
void *tmp = realloc (arr, (maxn + MAXN) * szelem);
if (!tmp) {
fprintf (stderr, "realloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
arr = tmp;
memset (arr + maxn * szelem, 0, MAXN * szelem);
maxn += MAXN;
}
Putting all the pieces together and using a few simply error checking functions for convenience, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* constants for number of columns, buffer chars, and initial allocation */
enum { NCOL = 2, MAXC = 32, MAXN = 64 };
void *xcalloc (size_t nmemb, size_t sz);
void *xrealloc (void *ptr, size_t psz, size_t *nelem);
FILE *xfopen (const char *fn, const char *mode);
int main (int argc, char **argv) {
char buf[MAXC] = {0};
char *fmt = "%d;%d";
int (*arr)[NCOL] = NULL;
size_t i, idx = 0, maxn = MAXN;
FILE *fp = argc > 1 ? xfopen (argv[1], "r") : stdin;
/* alloc mem for array of MAXN elements */
arr = xcalloc (maxn, sizeof *arr);
while (fgets (buf, MAXC, fp)) { /* read each line of input */
int a, b; /* parse line for values */
if (sscanf (buf, fmt, &a, &b) != NCOL) continue;
arr[idx][0] = a, arr[idx][1] = b;
if (++idx == maxn) /* realloc as needed */
arr = xrealloc (arr, sizeof *arr, &maxn);
}
if (fp != stdin) fclose (fp); /* close if not stdin */
for (i = 0; i < idx; i++)
printf (" array[%3zu][0] : %4d [1] : %d\n",
i, arr[i][0], arr[i][1]);
free (arr); /* free allocated memory */
return 0;
}
/** xcalloc allocates memory using calloc and validates the return. */
void *xcalloc (size_t nmemb, size_t sz)
{ register void *memptr = calloc (nmemb, sz);
if (!memptr) {
fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
return memptr;
}
/** realloc 'ptr' to array of elements of 'psz' to 'nelem + MAXN' elements */
void *xrealloc (void *ptr, size_t psz, size_t *nelem)
{ void *tmp = realloc ((char *)ptr, (*nelem + MAXN) * psz);
if (!tmp) {
fprintf (stderr, "realloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
memset (tmp + *nelem * psz, 0, MAXN * psz); /* zero new memory */
*nelem += MAXN;
return tmp;
}
/** fopen with error checking - short version */
FILE *xfopen (const char *fn, const char *mode)
{ FILE *fp = fopen (fn, mode);
if (!fp) {
fprintf (stderr, "xfopen() error: file open failed '%s'.\n", fn);
// return NULL; /* choose appropriate action */
exit (EXIT_FAILURE);
}
return fp;
}
Example Input
With an initial allocation for 64 pairs, reallocation is forces in order to read the entire file. (you can set the initial size at 1 and realloc on every iteration, but that is highly inefficient -- the initial size for MAXN must be at least 1, and should be set to some reasonably anticipated number of elements given your data)
$ cat dat/2d_data.txt
1;354
2;160
3;205
4;342
...
98;464
99;130
100;424
Example Use/Output
$ ./bin/array_ptr2array_realloc <dat/2d_data.txt
array[ 0][0] : 1 [1] : 354
array[ 1][0] : 2 [1] : 160
array[ 2][0] : 3 [1] : 205
array[ 3][0] : 4 [1] : 342
...
array[ 97][0] : 98 [1] : 464
array[ 98][0] : 99 [1] : 130
array[ 99][0] : 100 [1] : 424
Memory Use/Error Check
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated. For Linux valgrind is the normal choice.
$ valgrind ./bin/array_ptr2array_realloc <dat/2d_data.txt
==2796== Memcheck, a memory error detector
==2796== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==2796== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==2796== Command: ./bin/array_ptr2array_realloc
==2796==
array[ 0][0] : 1 [1] : 354
array[ 1][0] : 2 [1] : 160
array[ 2][0] : 3 [1] : 205
array[ 3][0] : 4 [1] : 342
...
array[ 97][0] : 98 [1] : 464
array[ 98][0] : 99 [1] : 130
array[ 99][0] : 100 [1] : 424
==2796==
==2796== HEAP SUMMARY:
==2796== in use at exit: 0 bytes in 0 blocks
==2796== total heap usage: 2 allocs, 2 frees, 1,536 bytes allocated
==2796==
==2796== All heap blocks were freed -- no leaks are possible
==2796==
==2796== For counts of detected and suppressed errors, rerun with: -v
==2796== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look over all the answers and let me know if you have any further questions. Using a pointer to array makes a whole lot of sense in a number of situations such as this. It simplifies allocation and freeing of memory and preserves your 2D indexing as well.
If mymatrix were defined as int mymatrix[3][2], you could read the first line doing:
fscanf(file, "%d;%d", &mymatrix[0][0], &mymatrix[0][1]);
The same code would work with mymatrix defined as int **mymatrix, if it would point to a dynamically initialized array of pointers to arrays of int as follows:
mymatrix=(int**)calloc(lines, sizeof(int*)); // intialize the array of array
for (inti=0; i<lines; i++)
mymatrix[i]=(int*)calloc(cols, sizeof(int)); //intialise each array
The number of lines and cols of the matrix could then be defined at runtinme.
You will have to malloc the memory to mymatrix[0].
mymatrix[0]= (int *) malloc ( sizeof (int) *2);
Also, in mymatrix malloc, 1 actually means the maximum number of rows you will have. So you can at max have 1 row as per that.
I am having trouble reading 3 strings from one line, moving to the next one, and putting them in my struct correctly.
My text file looks like this:
- John Johnson Math
- Eric Smith Biology
- etc
I need to arrange students by class they have chosen. How should i read first string as name then blank, second as last name, and third by class and do that for every line, then store correctly in my struct? Here is what I have right now:
#include <stdio.h>
#include <stdlib.h>
#define MAX 30
typedef struct students {
char nameFirst[MAX];
char NameLast[MAX];
char choClass[MAX];
struct students *next;
} Students;
int main() {
FILE *in;
Students *first = NULL, *New = NULL, *last = NULL, *old = NULL, *current = NULL;
in = fopen("studenti.txt", "r");
if (in == NULL)
{
printf("Cannot open file!\n");
return 1;
}
while (i = fgetc(in) != (int)(EOF))
{
New = calloc(1, sizeof(Students));
if (first == NULL)
first = New;
else
last->next = New;
j = 0;
while (i != (int)(' '))
{
New->nameFirst[j++] = (char)i;
i = fgetc(in);
}
New->nameFirst[j] = '\0';
}
}
Continuing from the comment, why are you approaching this problem with a linked-list? You can use a linked list, but the overhead and code complexity is more than required. A straight forward solution is a simply dynamic array of type students. The benefits of an array or significant. Direct access to all student, simple sorting with a single call to qsort, simple additions, etc..
Don't get me wrong, if you assignment is to use a linked-list, by all means do, but you should really look at an dynamic array of type student, where you can realloc as needed to add as many students as you may like.
For example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 30, MAXS = 60, MAXLN = 128 };
typedef struct students {
char first[MAXC];
char last[MAXC];
char class[MAXC];
} students;
int main (int argc, char **argv) {
students *array = NULL;
size_t i, idx = 0, maxs = MAXS;
char buf[MAXLN] = "";
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* allocate/validate maxs students in array */
if (!(array = malloc (maxs * sizeof *array))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
while (fgets (buf, MAXLN, fp)) { /* read each line into buf */
/* separate in to struct members */
if (sscanf (buf, "- %s %s %s", array[idx].first,
array[idx].last, array[idx].class) != 3)
continue;
if (++idx == maxs) { /* check against current allocations */
void *tmp = realloc (array, (maxs + MAXS) * sizeof *array);
if (!tmp) { /* valdate realloc array succeeded */
fprintf (stderr, "error: realloc memory exhausted.\n");
break; /* or break and use existing data */
}
array = tmp; /* assign reallocated block to array */
maxs += MAXS; /* update current allocations size */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
printf ("\nstudents:\n\n"); /* output formatted data */
for (i = 0; i < idx; i++) {
char tmp[2 * MAXC + 2] = "";
strcpy (tmp, array[i].last);
strcat (tmp, ", ");
strcat (tmp, array[i].first);
printf (" %-60s %s\n", tmp, array[i].class);
}
putchar ('\n');
free (array); /* free all allocated memory */
return 0;
}
(note: if your data file really doesn't begin each line with '- ', then just remove that from the sscanf format-string)
Example Input
$ cat dat/studentclass.txt
- John Johnson Math
- Eric Smith Biology
- etc.
Example Use/Output
$ ./bin/structstudents <dat/studentclass.txt
students:
Johnson, John Math
Smith, Eric Biology
Memory Error/Check
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.
For Linux valgrind is the normal choice. e.g.
$ valgrind ./bin/structstudents <dat/studentclass.txt
==14062== Memcheck, a memory error detector
==14062== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14062== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==14062== Command: ./bin/structstudents
==14062==
students:
Johnson, John Math
Smith, Eric Biology
==14062==
==14062== HEAP SUMMARY:
==14062== in use at exit: 0 bytes in 0 blocks
==14062== total heap usage: 1 allocs, 1 frees, 5,400 bytes allocated
==14062==
==14062== All heap blocks were freed -- no leaks are possible
==14062==
==14062== For counts of detected and suppressed errors, rerun with: -v
==14062== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look it over an let me know if the array approach fits your needs and if you have any questions.
Sorting by class member
In order to sort the array of struct students by the class, the easiest way is to use the qsort function. It is the standard sorting function provided by the C library (include stdio.h). You can sort by any member of the students struct. You can even sort by class, and by name.
The only problem new programmers have with qsort is writing the compare function to pass to qsort in order to have it sort in the desired order. The compare function will receive a pointer to two elements in your array of struct student. The parameters passes are passed as void * (const void * actually). Just like any void pointer, you must cast it to the proper type before you can dereference it. (in this case students *) Therefore, you simply need a function that casts the void pointers to students * and passes the values to strcmp. Example:
int compare (const void *a, const void *b)
{
return strcmp (((students *)a)->class, ((students *)b)->class);
}
The only thing left is calling qsort (after fclose in the code):
qsort (array, idx, sizeof *array, compare);
Your output is then sorted by class.
If you then want to sort further by last name after sorting by class, instead of returning on the strcmp for class, test if the result is not equal to zero, and return that result. If the result of the strcmp on class is zero, then you simply return strcmp (((students *)a)->last, ((students *)b)->last); That simply sorts by class first, but if the class is the same, sorts further by last. For example:
int compare (const void *a, const void *b)
{
int r;
if ((r = strcmp (((students *)a)->class, ((students *)b)->class)))
return r;
return strcmp (((students *)a)->last, ((students *)b)->last);
}
Example Input
$ cat dat/studentclass.txt
- Wade Williams Biology
- John Johnson Math
- Eric Smith Biology
- etc.
Example Use/Output
$ ./bin/structstudents <dat/studentclass.txt
students:
Smith, Eric Biology
Williams, Wade Biology
Johnson, John Math
Take the time to learn qsort.
I have an Array of Strings inside a Struct that looks like this
#define ID_LEN 20
struct Person{
char name[ID_LEN];
int num_items;
char **items;
};
int main(){
int num_persons = 10;
struct Person *ptr[num_persons];
I start with an array of 10 persons. Initially all persons have 0 items so their list of items is an malloc(0).
for(int i = 0; i < num_persons; i++){
ptr[i] = (struct Person *) malloc(sizeof(struct Person) + 1);
ptr[i]->num_items = 0;
ptr[i]->items = malloc(0 * sizeof(char*));
}
At some point I want to name those persons and add them some items like this.
strcpy(ptr[0]->name, "John");
ptr[0]->num_items = 1;
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));
strcpy(ptr[0]->items[0], "pencil");
printf("Name: %s\n", ptr[0]->name);
printf("Number of items: %d\n", ptr[0]->num_items);
for(int i = 0; i < ptr[0]->num_items; i++){
printf("Item %d is %s\n", i, ptr[0]->items[i]);
}
I'm getting segmentation fault: 11. I'm not sure if I have done realloc correctly or not.
You have several problems here
First your ID_LEN
nowhere do you check if by copying to name you surpass the ID_LEN
so instead of
strcpy(ptr[0]->name, "John");
use
strcpy_s(ptr[0]->name,sizeof(ptr[0]->name),"John or whatever"); // C11
You allocate
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
but
you should not cast return value of malloc/realloc (search on web for
explanation, it has been described ad nauseum)
when using realloc, you should first check the return value, it may fail.
char** tmp = realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
if (tmp != NULL)
{
ptr[0]->items = tmp;
}
else
{
abort();
}
The memory realloc returns is partly uninitialized (old pointers remain but
new ones are uninitialized. In your case you did not have any previous
pointers so that one items[0] is uninitialized.
so when you do
strcpy(ptr[0]->items[0], "pencil");
it will fail since items[0] is pointing to some arbitrary memory location.
after you realloc pointers you need initialize them to point to a
memory large enough to accommodate your string
E.g.
ptr[0]->items[0] = strdup("pencil"); // does malloc then copies string
It is not so efficient to use realloc everytime you need to add one new
item, instead allocate a bunch of items but set the ones you are not using
to NULL then keep track of how many are left, once they run out allocate
another bunch
All the problem in your code revolves around using memory which is not allocated.
Consider
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));//line 1
strcpy(ptr[0]->items[0], "pencil");//line 2
In line 1 you have allocated memory to hold ptr[0]->num_items number of pointers to c strings.But you have not actually allocated memory to store c strings i.e. those pointers are not pointing to actual memory.
When in line 2 you try to access ptr[0]->items[0] its just char* there is no memory allocated to it.
I added the below line before line 2 and your code worked fine.
for(int i=0;i<ptr[0]->num_items;i++)
ptr[0]->items[i]=malloc(sizeof(char)*10);
In addition to what the other answers provide, there are several other considerations relevant to what you are doing. The first, don't 0 allocate anything. That is a holdover from days gone by and not necessary.
Next, following your static declaration of a variable length array of num_persons pointers to type struct Person, you must decide whether you will allocate storage for all your pointers at once, or whether you will allocate storage for the struct, only when adding a person. That will have implications when you split your code up into functions.
Since the remainder of your data structure will be allocated dynamically, you must validate each allocation succeeds before copying/initializing, whatever.
When you allocate your empty structure, take a close look at calloc instead of malloc. This is one instance where the default initialization provided by calloc can help.
The remainder of your task is just an accounting problem -- that is accounting for which members require allocation, the current state and size of each allocation, and then finally to free the memory you have allocated when it is no longer needed.
Take for example the code you might want for adding a person with an item to your array of pointers. To validate the addition, you will need to know that ptr is a valid pointer and whether you have reached your num_persons limit before you begin to allocate memory. You next must allocate storage for a struct Person and assign the address for that block to ptx[X] (for whatever index X you are working with).
Once allocated, you can initialize defaults (or if calloc was used, know that all values were initialized to 0/NULL). Following that you can copy name (limited to ID_LEN) and allocate pointers and storage for each item you wish to hold in your pointer-to-pointer-to-char* items (which you will handle similar to ptr accept that the pointers for items are allocated as well) Note there is no need to realloc (items,... at this point as this is the first time items is allocated.
If you put all of that together, you can come up with a function to add a person together with their first item similar to the following, where max is the maximum number of persons allowed, nm is the name, itm is the item, and idx is a pointer to the current index for that person:
/* add person with item */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx)
{
if (!ptr || *idx + 1 == max) return NULL;
int i = *idx;
/* allocate storage for struct */
if (!(ptr[i] = calloc (1, sizeof **ptr))) {
fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i);
return NULL;
}
strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */
/* allocate/validate pointer to char* ptr[i]->items */
if (!(ptr[i]->items =
malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
(*idx)++;
return ptr[i];
}
(note: you must assign the return of the function to ptr[X] in the calling function. Also note, C-style recommends all lower-case names, which is what you see above, leave CamelCase names to C++)
Once you have a person added, you will want the ability to add items to the list of items associated with that person. This is where realloc comes in to allow you to resize the number of pointers-to-items for that person. You can do something similar to add person, but the index you pass no longer needs to be a pointer as it will not be updated within the function. e.g.
/* add item to person at index 'i' */
struct person *additem (struct person **ptr, int i, char *itm)
{
if (!ptr) return NULL;
void *tmp;
/* allocate/realloc/validate pointer to char* ptr[i]->items */
if (!(tmp = realloc (ptr[i]->items,
(ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->items = tmp; /* assign tmp on successful realloc */
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
return ptr[i];
}
You are now able to add both persons and items to the list, but need a way to iterate over all values in the list if you are going to make use of them. Whether searching, printing, or freeing memory, all iteration functions will have similar form. To print all persons and items you could do something similar to the following:
/* print the list of persons and items */
void prndetail (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items);
for (i = 0; i < ptr[p]->num_items; i++)
printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]);
putchar ('\n');
}
}
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. When you are done with your list, you will need to free it. Similar to printing, it can be done as follows:
/* free all allocated memory */
void deletelist (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
for (i = 0; i < ptr[p]->num_items; i++)
free (ptr[p]->items[i]);
free (ptr[p]->items);
free (ptr[p]);
}
// free (ptr); /* if array of pointers allocated, not static */
}
That's really all there is to the accounting lesson. If you keep track of each part of the data structure you are using, managing the memory is straight forward. Putting all the pieces together in a short example, you could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { NPER = 10, ID_LEN = 20 };
struct person {
char name[ID_LEN];
int num_items;
char **items;
};
/* add person + item - to add both person and item at once */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx);
/* add item to existing person */
struct person *additem (struct person **ptr, int i, char *itm);
void prndetail (struct person **ptr, int idx);
void deletelist (struct person **ptr, int idx);
int main (void) {
int idx = 0;
struct person *ptr[NPER];
/* allocate storage for struct, add person + item */
if (!(ptr[idx] = addpwitem (ptr, NPER, "John", "pencils", &idx))) {
fprintf (stderr, "error: adding ptr[%d] failed.\n", idx);
return 1;
}
printf ("\nadded John:\n");
prndetail (ptr, idx); /* print contents of persons & items */
additem (ptr, idx - 1, "pens"); /* add next item */
printf ("\nadded item 'pens' for John:\n");
prndetail (ptr, idx); /* print contents of persons & items */
/* add next person + item */
if (!(ptr[idx] = addpwitem (ptr, NPER, "Martha", "paper", &idx))) {
fprintf (stderr, "error: adding ptr[%d] failed.\n", idx);
return 1;
}
printf ("\nadded Martha:\n");
prndetail (ptr, idx); /* print contents of persons & items */
deletelist (ptr, idx); /* free all allocated memory */
return 0;
}
/* add person with item */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx)
{
if (!ptr || *idx + 1 == max) return NULL;
int i = *idx;
/* allocate storage for struct */
if (!(ptr[i] = calloc (1, sizeof **ptr))) {
fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i);
return NULL;
}
strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */
/* allocate/validate pointer to char* ptr[i]->items */
if (!(ptr[i]->items =
malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
(*idx)++;
return ptr[i];
}
/* add item to person at index 'i' */
struct person *additem (struct person **ptr, int i, char *itm)
{
if (!ptr) return NULL;
void *tmp;
/* allocate/realloc/validate pointer to char* ptr[i]->items */
if (!(tmp = realloc (ptr[i]->items,
(ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->items = tmp; /* assign tmp on successful realloc */
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
return ptr[i];
}
/* print the list of persons and items */
void prndetail (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items);
for (i = 0; i < ptr[p]->num_items; i++)
printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]);
putchar ('\n');
}
}
/* free all allocated memory */
void deletelist (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
for (i = 0; i < ptr[p]->num_items; i++)
free (ptr[p]->items[i]);
free (ptr[p]->items);
free (ptr[p]);
}
// free (ptr); /* if array of pointers allocated */
}
Example Use/Output
$ ./bin/struct_p2p2c
added John:
John 1 pencils
added item 'pens' for John:
John 2 pencils, pens
added Martha:
John 2 pencils, pens
Martha 1 paper
Memory Error Check
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.
It is simple to do:
$ valgrind ./bin/struct_p2p2c
==7618== Memcheck, a memory error detector
==7618== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7618== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==7618== Command: ./bin/struct_p2p2c
==7618==
added John:
John 1 pencils
added item 'pens' for John:
John 2 pencils, pens
added Martha:
John 2 pencils, pens
Martha 1 paper
==7618==
==7618== HEAP SUMMARY:
==7618== in use at exit: 0 bytes in 0 blocks
==7618== total heap usage: 8 allocs, 8 frees, 115 bytes allocated
==7618==
==7618== All heap blocks were freed -- no leaks are possible
==7618==
==7618== For counts of detected and suppressed errors, rerun with: -v
==7618== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look over this and the rest of the answers. There are many good suggestions between them. Let us know if you have further questions.
You are allocating for the list of items, but you are not allocating space for each individual item string. Your
strcpy(ptr[0]->items[0], "pencil");
will fail.