Related
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'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.
I have a function in which I'm trying to split a string but somehow it's stopping when spaces are read.
input.csv:
18820218,Northern Ireland,England,0,13,Friendly,Belfast,Ireland,FALSE
output.txt:
18820218,Northern,(null),(null),(null),(null),(null),(null),(null)
typedef struct
{
long int date;
char *h_team;
char *a_team;
int home_score;
int away_score;
char *reason;
char *city;
char *country;
char *neutral_field;
}Data;
void open_output(char *string, FILE **output)
{
if((*output=fopen(string, "w")) == NULL)
{
printf("%s not found\n", string);
exit(1);
}
}
void alloc_Data(Data *d, int size)
{
d->line1 = (char*)malloc(50*sizeof(char));
d->h_team = (char*)malloc(30*sizeof(char));
d->a_team = (char*)malloc(30*sizeof(char));
d->reason = (char*)malloc(30*sizeof(char));
d->city = (char*)malloc(30*sizeof(char));
d->country = (char*)malloc(30*sizeof(char));
d->neutral_field = (char*)malloc(9*sizeof(char));
}
void store(Data *d, FILE *output)
{
char *string = "18820218,Northern Ireland,England,0,13,Friendly,"
"Belfast,Ireland,FALSE";
char *char_date = malloc(10*sizeof(char));
char *char_hscore = malloc(20*sizeof(char));
char *char_ascore = malloc(3*sizeof(char));
char *token;
token = strtok(string, ",");
char_date = token;
token = strtok(NULL, ",");
d->h_team = token;
token = strtok(NULL, ",");
d->a_team = token;
token = strtok(NULL, ",");
char_hscore = token;
token = strtok(NULL, ",");
char_ascore = token;
token = strtok(NULL, ",");
d->reason = token;
token = strtok(NULL, ",");
d->city = token;
token = strtok(NULL, ",");
d->country = token;
token = strtok(NULL, ",");
d->neutral_field = token;
d->date = atoi(char_date);
d->home_score = atoi(char_hscore);
d->away_score = atoi(char_ascore);
fprintf(output, "%li,%s,%s,%d,%d,%s,%s,%s,%s\n", d->date, d->h_team,
d->a_team, d->home_score, d->away_score, d->reason, d->city,
d->country, d->neutral_field );
free(string);
free(char_date);
free(char_hscore);
free(char_ascore);
}
int main(int argc, char *argv[])
{
FILE *output;
char *string = "saida.txt";
open_output(string, &output);
Data *d;
d = (Data*)malloc(sizeof(Data));
alloc_Data(d);
store(d, output);
free(d);
return 0;
}
Ana, I have watched your questions change over the past several iterations, and it is clear you know what pieces you need to put together, but you are somewhat making it more difficult on yourself than it needs to be by how you are trying to fit them together.
The purpose for dynamically allocating your structures or data are (1) handle larger amounts of data than will fit within your program stack (not an issue here), (2) to allow you to grow or shrink the amount of storage you are using as your data needs fluctuate over the course of your program (also not an issue here), or (3) to allow you to tailor your storage needs based on data in use in your program. This last part seems to be what you are attempting, but by allocating a fixed size for your character arrays -- you completely lose the benefit of tailoring your allocation to the size of your data.
In order to allocate storage for each of the strings contained in your data, you need to get the length of each string, and then allocate length + 1 characters for storage (the +1 for the nul-terminating character). While you can use malloc and then strcpy to accomplish the allocation and copy to the new block of memory, if you have strdup, that can do both for you in one function call.
The quandary you are faced with is "Where do I store the data before I get the lengths and allocate an copy?" You can handle this in a number of ways. You can declare a jumble of different variables, and parse the data into individual variables to begin with (somewhat messy), you can allocate one struct with the fixed values to initially store the values (a good option, but calling malloc for 30 or 50 chars doesn't make a lot of sense when a fixed array will do), or you can declare a separate temporary struct with fixed array sizes to use (that way to collect the jumble of separate variables into a struct that can then easily be passed to your allocate function) Consider each, and use the one that works best for you.
Your function return types do not make a whole lot of sense as they are. You need to choose a meaningful return type that will allow the function to indicate whether it succeeded or failed, and then return a value (or pointer to a value) that provides useful information to the remainder of your program. Gauging success/failure of a function is particularly important with functions that allocate memory or handle input or output.
In addition to the return type you choose, you need to think through the parameters you are passing to each function. You need to think about what variables need to be available where in your function. Take your FILE* parameter. You never user the file outside of your store() function - so why do you have it declared in main() which causes you to have to worry about returning the open stream through a pointer -- that you don't use.
With that in mind we can look at putting the pieces of your program together in a slightly manner.
First, do not use magic numbers sprinkled throughout your code. (e.g. 9, 10, 20, 30, 50, etc..) Instead,
#define MAXN 9 /* if you need constants, define one (or more) */
#define MAXC 30
#define MAXL 50
(or you can use an enum for the same purpose)
For the purpose of the example, you can use a struct that you will dynamically allocate for efficient storage of your data, and a temporary struct to aid in parsing values from your line of data. For example:
typedef struct { /* struct to hold dynamically allocated data */
long date; /* sized to exact number of chars required. */
int home_score,
away_score;
char *h_team,
*a_team,
*reason,
*city,
*country,
*neutral_field;
} data_t;
typedef struct { /* temp struct to parse data from line */
long date; /* sized to hold largest anticipated data */
int home_score,
away_score;
char h_team[MAXC],
a_team[MAXC],
reason[MAXC],
city[MAXC],
country[MAXC],
neutral_field[MAXN];
} data_tmp_t;
Next, the entire purpose of your open_output function is to open a file for writing. It should return the open file stream on success or NULL otherwise, e.g.
/* pass filename to open, returns open file stream pointer on
* success, NULL otherwise.
*/
FILE *open_output (const char *string)
{
FILE *output = NULL;
if ((output = fopen (string, "w")) == NULL)
fprintf (stderr, "file open failed. '%s'.\n", string);
return output;
}
Your alloc_data function is allocating a data struct and filling its values. It should return a pointer to the fully allocated and filled struct on success, or NULL on failure, e.g.
/* pass temporary struct containing data, dynamic struct allocated,
* each member allocated to hold exact number of chars (+ terminating
* character). pointer to allocated struct returned on success,
* NULL otherwise.
*/
data_t *alloc_data (data_tmp_t *tmp)
{
data_t *d = malloc (sizeof *d); /* allocate structure */
if (d == NULL)
return NULL;
d->date = tmp->date;
/* allocate each string member with strdup. if not available,
* simply use malloc (strlen(str) + 1), and then strcpy.
*/
if ((d->h_team = strdup (tmp->h_team)) == NULL)
return NULL;
if ((d->a_team = strdup (tmp->a_team)) == NULL)
return NULL;
d->home_score = tmp->home_score;
d->away_score = tmp->away_score;
if ((d->reason = strdup (tmp->reason)) == NULL)
return NULL;
if ((d->city = strdup (tmp->city)) == NULL)
return NULL;
if ((d->country = strdup (tmp->country)) == NULL)
return NULL;
if ((d->neutral_field = strdup (tmp->neutral_field)) == NULL)
return NULL;
return d; /* return pointer to allocated struct */
}
Any time you are allocating multiple values that are nested within a struct (or nested structs), get in the habit of writing a free_data function to free the memory you allocate in alloc_data. It is far better to write one free function to properly handle a complex structure you have allocated compared to sprinkling individual free calls around your code. There is no return to check when freeing a variable, so you can use a void function here:
/* frees each allocated member of d, and then d itself */
void free_data (data_t *d)
{
free (d->h_team);
free (d->a_team);
free (d->reason);
free (d->city);
free (d->country);
free (d->neutral_field);
free (d);
}
Your store() function is where most of the decisions and validation checks take place. The purpose of your code is to parse and then store a string in a filename. That should get you thinking about what parameters are required. The remainder of the file handling can all be internal to store() since the FILE is not used further in the calling function. Now, depending on how many writes you are doing, it may make perfect sense to declare and open the FILE once in main() and then pass an open (and validated) FILE* parameter, which would then require only a single fopen call and a final close in main(). For purposes here, all will be handled in store so you can check for any stream error after each write by checking the return of fclose.
Since you are allocating and storing a struct which may be needed for further use in the calling function, choosing to return a pointer to the caller (or NULL on failure) makes for a good choice of return type for store(). You could do something like:
/* parses data in string into separate values and stores data in string
* to filename (note: use mode "a" to append instead of "w" which
* truncates). returns pointer to fully-allocated struct on success,
* NULL otherwise.
*/
data_t *store (const char *string, const char *filename)
{
data_tmp_t tmp = { .date = 0 };
data_t *d = NULL;
FILE *output = open_output (filename); /* no need to pass in */
/* not used later in main */
if (output == NULL) { /* validate file open for writing */
return NULL;
}
/* parse csv values with sscanf - avoids later need to convert values
* validate all values successfully converted.
*/
if (sscanf (string, "%ld,%29[^,],%29[^,],%d,%d,%29[^,],%29[^,],"
"%29[^,],%8[^\n]",
&tmp.date, tmp.h_team, tmp.a_team, &tmp.home_score,
&tmp.away_score, tmp.reason, tmp.city, tmp.country,
tmp.neutral_field) != 9) {
fprintf (stderr, "error: failed to parse string.\n");
return NULL;
}
d = alloc_data (&tmp); /* allocate d and deep-copy tmp to d */
if (d == NULL) { /* validate allocation/copy succeeded */
perror ("malloc-alloc_data");
return NULL;
}
/* output values to file */
fprintf (output, "%ld,%s,%s,%d,%d,%s,%s,%s,%s\n", d->date, d->h_team,
d->a_team, d->home_score, d->away_score, d->reason, d->city,
d->country, d->neutral_field );
if (fclose (output) == EOF) /* always validate close-after-write */
perror ("stream error-output");
return d; /* return fully allocated/populated struct */
}
Your main() can then handle nothing more than your string that needs to be parsed, the filename to write the data to, and a pointer to the fully allocated struct resulting from the parse so it is available for further use. (it also takes the file to write to as the 1st argument to the program -- or it will write to "saida.txt" by default if no argument is provided, e.g.
int main (int argc, char *argv[])
{
char *string = "18820218,Northern Ireland,England,0,13,Friendly,"
"Belfast,Ireland,FALSE";
/* filename set to 1st argument (or "saida.txt" by default) */
char *filename = argc > 1 ? argv[1] : "saida.txt";
data_t *d = NULL;
d = store (string, filename); /* store string in filename */
if (d == NULL) { /* validate struct returned */
fprintf (stderr, "error: failed to store string.\n");
return 1;
}
/* output struct values as confirmation of what was stored in file */
printf ("stored: %ld,%s,%s,%d,%d,%s,%s,%s,%s\n", d->date, d->h_team,
d->a_team, d->home_score, d->away_score, d->reason, d->city,
d->country, d->neutral_field );
free_data (d); /* free all memory when done */
return 0;
}
While not mandated by the C Standard, the "standard" coding style for C avoids the use of camelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It is a matter of style -- so it is completely up to you, but failing to follow it can lead to the wrong first impression in some circles.
Putting it altogether, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 9 /* if you need constants, define one (or more) */
#define MAXC 30
#define MAXL 50
typedef struct { /* struct to hold dynamically allocated data */
long date; /* sized to exact number of chars required. */
int home_score,
away_score;
char *h_team,
*a_team,
*reason,
*city,
*country,
*neutral_field;
} data_t;
typedef struct { /* temp struct to parse data from line */
long date; /* sized to hold largest anticipated data */
int home_score,
away_score;
char h_team[MAXC],
a_team[MAXC],
reason[MAXC],
city[MAXC],
country[MAXC],
neutral_field[MAXN];
} data_tmp_t;
/* pass filename to open, returns open file stream pointer on
* success, NULL otherwise.
*/
FILE *open_output (const char *string)
{
FILE *output = NULL;
if ((output = fopen (string, "w")) == NULL)
fprintf (stderr, "file open failed. '%s'.\n", string);
return output;
}
/* pass temporary struct containing data, dynamic struct allocated,
* each member allocated to hold exact number of chars (+ terminating
* character). pointer to allocated struct returned on success,
* NULL otherwise.
*/
data_t *alloc_data (data_tmp_t *tmp)
{
data_t *d = malloc (sizeof *d); /* allocate structure */
if (d == NULL)
return NULL;
d->date = tmp->date;
/* allocate each string member with strdup. if not available,
* simply use malloc (strlen(str) + 1), and then strcpy.
*/
if ((d->h_team = strdup (tmp->h_team)) == NULL)
return NULL;
if ((d->a_team = strdup (tmp->a_team)) == NULL)
return NULL;
d->home_score = tmp->home_score;
d->away_score = tmp->away_score;
if ((d->reason = strdup (tmp->reason)) == NULL)
return NULL;
if ((d->city = strdup (tmp->city)) == NULL)
return NULL;
if ((d->country = strdup (tmp->country)) == NULL)
return NULL;
if ((d->neutral_field = strdup (tmp->neutral_field)) == NULL)
return NULL;
return d; /* return pointer to allocated struct */
}
/* frees each allocated member of d, and then d itself */
void free_data (data_t *d)
{
free (d->h_team);
free (d->a_team);
free (d->reason);
free (d->city);
free (d->country);
free (d->neutral_field);
free (d);
}
/* parses data in string into separate values and stores data in string
* to filename (note: use mode "a" to append instead of "w" which
* truncates). returns pointer to fully-allocated struct on success,
* NULL otherwise.
*/
data_t *store (const char *string, const char *filename)
{
data_tmp_t tmp = { .date = 0 };
data_t *d = NULL;
FILE *output = open_output (filename); /* no need to pass in */
/* not used later in main */
if (output == NULL) { /* validate file open for writing */
return NULL;
}
/* parse csv values with sscanf - avoids later need to convert values
* validate all values successfully converted.
*/
if (sscanf (string, "%ld,%29[^,],%29[^,],%d,%d,%29[^,],%29[^,],"
"%29[^,],%8[^\n]",
&tmp.date, tmp.h_team, tmp.a_team, &tmp.home_score,
&tmp.away_score, tmp.reason, tmp.city, tmp.country,
tmp.neutral_field) != 9) {
fprintf (stderr, "error: failed to parse string.\n");
return NULL;
}
d = alloc_data (&tmp); /* allocate d and deep-copy tmp to d */
if (d == NULL) { /* validate allocation/copy succeeded */
perror ("malloc-alloc_data");
return NULL;
}
/* output values to file */
fprintf (output, "%ld,%s,%s,%d,%d,%s,%s,%s,%s\n", d->date, d->h_team,
d->a_team, d->home_score, d->away_score, d->reason, d->city,
d->country, d->neutral_field );
if (fclose (output) == EOF) /* always validate close-after-write */
perror ("stream error-output");
return d; /* return fully allocated/populated struct */
}
int main (int argc, char *argv[])
{
char *string = "18820218,Northern Ireland,England,0,13,Friendly,"
"Belfast,Ireland,FALSE";
/* filename set to 1st argument (or "saida.txt" by default) */
char *filename = argc > 1 ? argv[1] : "saida.txt";
data_t *d = NULL;
d = store (string, filename); /* store string in filename */
if (d == NULL) { /* validate struct returned */
fprintf (stderr, "error: failed to store string.\n");
return 1;
}
/* output struct values as confirmation of what was stored in file */
printf ("stored: %ld,%s,%s,%d,%d,%s,%s,%s,%s\n", d->date, d->h_team,
d->a_team, d->home_score, d->away_score, d->reason, d->city,
d->country, d->neutral_field );
free_data (d); /* free all memory when done */
return 0;
}
Example Use/Output
$ ./bin/store_teams dat/saida.txt
stored: 18820218,Northern Ireland,England,0,13,Friendly,Belfast,Ireland,FALSE
Verify Output File
$ cat dat/saida.txt
18820218,Northern Ireland,England,0,13,Friendly,Belfast,Ireland,FALSE
Memory Use/Error Check
There is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?
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/store_teams dat/saida.txt
==16038== Memcheck, a memory error detector
==16038== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==16038== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==16038== Command: ./bin/store_teams dat/saida.txt
==16038==
stored: 18820218,Northern Ireland,England,0,13,Friendly,Belfast,Ireland,FALSE
==16038==
==16038== HEAP SUMMARY:
==16038== in use at exit: 0 bytes in 0 blocks
==16038== total heap usage: 8 allocs, 8 frees, 672 bytes allocated
==16038==
==16038== All heap blocks were freed -- no leaks are possible
==16038==
==16038== For counts of detected and suppressed errors, rerun with: -v
==16038== 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.
Hopefully this helps you understand how better to approach putting the puzzle pieces together in a less jumbled way and how to focus on what parameters are needed by each function, and how to think about choosing a meaningful type return for each of your functions. Look things over and let me know if you have further questions.
Code as shown will not compile-build for the following reasons:
The memberd->line1 does not exist in struct.
The function void alloc_Data(Data *d, int size) has two arguments,
but the call: alloc_Data(d); has only 1 argument.
Also, since definition for the function open_output(string, &output); is not provided, the code cannot be run by anyone attempting to help. (assumptions are made beyond this point)
Beyond that...
This:
token = strtok(NULL, ",");
d->h_team = token;
is effectively changing the address of a previously malloc'ed pointer, resulting in a memory leak. (This is because any subsequent calls to free(d->h_team); will be made to an address location that was never malloc'ed).
This:
token = strtok(NULL, ",");
strcpy(d->h_team,token);
results in assigning the contents residing at the address of token to the address located at d->h_team, meaning that you can still call free(d->h_team); when done using it. (avoiding a memory leak)
To get past the failure you are seeing, this might help:
char *string = "18820218,Northern Ireland,England,0,13,Friendly,Belfast,Ireland,FALSE";
char *workingbuf = '\0'
workingbuf = strdup(string);
token = strtok(string, ",");
...
One last thought, it is a good idea to check the output of strtok() before assuming token contains anything:
token = strtok(NULL, ",");
if(token)
{
d->h_team = token;
...
Edit
After implementing the changes I suggested above, including your addition of open_output, your code ran.
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'm not proficient in C programming so please excuse me if this isn't a strong question. In the following code, I can only allocate memory to samplesVecafter obtaining the value of nsamplepts, but I need to return the vector samplesVec to the main for further use (not yet coded). However, I'm getting the following error:
Error in Terminal Window:
ImportSweeps(3497,0x7fff7b129310) malloc: * error for object 0x7fdaa0c03af8: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
Abort trap: 6
I'm using Mac OS X Mavericks with the gcc compiler. Thanks for any help.
*EDITED!!! AFTER VALUABLE INPUTS FROM COMMENTATORS, THE FOLLOWING REPRESENTS A SOLUTION TO THE ORIGINAL PROBLEM (WHICH IS NO LONGER AVAILABLE) *
The following code modification seemed to solve my original questions. Thanks for the valuable inputs everyone!
/* Header Files */
#define LIBAIFF_NOCOMPAT 1 // do not use LibAiff 2 API compatibility
#include <libaiff/libaiff.h>
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <math.h>
/* Function Declarations */
void FileSearch(char*, char*, char*, char*, char*);
int32_t *ImportSweeps(char*);
/* Main */
int main()
{
char flag1[2] = "N";
char binname[20] = "bin1"; // dummy assignment
char buildfilename[40] = "SweepR";
char skeletonpath[100] = "/Users/.../Folder name/";
int k, len;
/* Find the sweep to be imported in the directory given by filepath */
FileSearch(skeletonpath, binname, buildfilename, skeletonpath, flag1);
if (strcmp(flag1,"Y")) {
printf("No file found. End of program.\n");
} else {
len = (int) strlen(skeletonpath);
char *filepath = malloc(len);
for (k = 0; k < len; k++) {
filepath[k] = skeletonpath[k];
}
printf("File found! Filepath: %s\n", filepath);
// Proceed to import sweep
int32_t *sweepRfile = ImportSweeps(filepath);
if (sweepRfile) {
printf("Success!\n");
// Do other things with sweepRfile
free(sweepRfile);
}
free(filepath);
}
return 0;
}
/* Sub-Routines */
void FileSearch(char *dir, char *binname, char *buildfilename, char* filepath, char* flag1)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"Cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name, &statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)
continue;
strcpy(binname,entry->d_name);
strcpy(buildfilename,"SweepR");
/* Recurse at a new indent level */
FileSearch(entry->d_name, binname, buildfilename, filepath, flag1);
}
else {
sprintf(buildfilename, "%s%s.aiff", buildfilename, binname);
if (strcmp(entry->d_name,buildfilename)) {
strcpy(buildfilename,"SweepR");
} else {
sprintf(filepath, "%s%s/%s", filepath, binname, buildfilename);
strcpy(flag1,"Y");
break;
}
}
}
chdir("..");
closedir(dp);
}
int32_t *ImportSweeps(char *filepath)
{
char *filepathread = filepath;
/* Initialize files for importing */
AIFF_Ref fileref;
/* Intialize files for getting information about AIFF file */
uint64_t nSamples;
int32_t *samples = NULL;
int32_t *samplesVec = NULL;
int channels, bitsPerSample, segmentSize, ghost, nsamplepts;
double samplingRate;
/* Import Routine */
fileref = AIFF_OpenFile(filepathread, F_RDONLY) ;
if(fileref)
{
// File opened successfully. Proceed.
ghost = AIFF_GetAudioFormat(fileref, &nSamples, &channels, &samplingRate, &bitsPerSample, &segmentSize);
if (ghost < 1)
{
printf("Error getting audio format.\n");
AIFF_CloseFile(fileref); return (int32_t) 0;
}
nsamplepts = ((int) nSamples)*channels;
samples = malloc(nsamplepts * sizeof(int32_t));
samplesVec = malloc(nsamplepts * sizeof(int32_t));
ghost = AIFF_ReadSamples32Bit(fileref, samples, nsamplepts);
if (ghost) {
for (int k = 0; k < nsamplepts; k++) {
samplesVec[k] = *(samples+k);
}
}
free(samples);
AIFF_CloseFile(fileref);
}
return samplesVec;
}
So... as far as I can see... :-)
samplesVec, the return value of ImportSweeps is not initialized, if fileref is false. Automatic (== local) variables have no guarantees on its value if samplesVec are not explicitly initialized - in other words samplesVec could carry any address. If samplesVec is not NULL on luck (which on the other hand might be often the case), you try free a not allocated junk of memory, or by very bad luck an somewhere else allocated one.
If I'm correct with my guess you can easy fix this with:
int32_t *samples;
int32_t *samplesVec = NULL;
It is a good idea anyway to initialize any variable as soon as possible with some meaningful error or dummy value, if you not use it in the very next line. As pointers are horrible beasts, I always NULL them if I don't initialize them with a useful value on declaration.
Edit: Several minor small changes for a readable approximation to English. :-)
If AIFF_OpenFile fails, ImportSweeps returns an undefined value because samplesVec wasn't initialized. If that value is non-NULL, main will try to free it. You can either initialize samplesVec = NULL, or you can reorganize the code as
fileref = AIFF_OpenFile(filepathread, F_RDONLY) ;
if(!fileref) {
{
// print error message here
return NULL;
}
// File opened successfully. Proceed.
...
There are people who will insist a functon that should only have one exit -- they are poorly informed and voicing a faulty dogma handed down from others who are likewise uninformed and dogmatic. The check for error and return above is known as a guard clause. The alternate style, of indenting every time a test succeeds, yields the arrow anti-pattern that is harder to read, harder to modify, and more error prone. See http://blog.codinghorror.com/flattening-arrow-code/ and http://c2.com/cgi/wiki?ArrowAntiPattern for some discussion.