The program could be more useful if it accepted arguments for the input file name,name; currently the input file name is hardwired into main().
When doing C program development it is always a good practice to compile with the -Wall and -Wextra flags. These flags will help you find possible errors in the code. When I was learning how to program in C on Unix we had a program callcalled lint which helped identify possible problems in the code. I always ran lint on my code. To some extent the -Wall and -Wextra flags have replaced lint.
If the program had been compilecompiled with the -Wall andd -Wextra flags it would have found the following issues:
nn.c: In function ‘tData’:
nn.c:78:21: warning: format ‘%f’ expects argument of type ‘float *’, but argument 3 has type ‘IO_Neuron *’ [-Wformat=]
78 | fscanf(fp,"%f",&ret.training_in[i][inIndex]);
| ~^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | IO_Neuron *
| float *
nn.c:91:21: warning: format ‘%f’ expects argument of type ‘float *’, but argument 3 has type ‘IO_Neuron *’ [-Wformat=]
91 | fscanf(fp,"%f",&ret.training_out[i][outIndex]);
| ~^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | IO_Neuron *
| float *
nn.c: In function ‘train’:
nn.c:189:23: warning: unused parameter ‘input_layer’ [-Wunused-parameter]
189 | void train(IO_Neuron* input_layer,Neuron* hidden_layer,IO_Neuron* output_layer,IO_Neuron** input_training,IO_Neuron** output_training,int training_samples,int iterations)
| ~~~~~~~~~~~^~~~~~~~~~~
nn.c: In function ‘main’:
nn.c:272:9: warning: unused variable ‘j’ [-Wunused-variable]
272 | int i,j;
| ^
nn.c: In function ‘tData’:
nn.c:78:21: warning: format ‘%f’ expects argument of type ‘float *’, but argument 3 has type ‘IO_Neuron *’ [-Wformat=]
78 | fscanf(fp,"%f",&ret.training_in[i][inIndex]);
| ~^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | IO_Neuron *
| float *
nn.c:91:21: warning: format ‘%f’ expects argument of type ‘float *’, but argument 3 has type ‘IO_Neuron *’ [-Wformat=]
91 | fscanf(fp,"%f",&ret.training_out[i][outIndex]);
| ~^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | IO_Neuron *
| float *
nn.c: In function ‘train’:
nn.c:189:23: warning: unused parameter ‘input_layer’ [-Wunused-parameter]
189 | void train(IO_Neuron* input_layer,Neuron* hidden_layer,IO_Neuron* output_layer,IO_Neuron** input_training,IO_Neuron** output_training,int training_samples,int iterations)
| ~~~~~~~~~~~^~~~~~~~~~~
nn.c: In function ‘main’:
nn.c:272:9: warning: unused variable ‘j’ [-Wunused-variable]
272 | int i,j;
| ^
To prevent this undefined behavior a best practice is to always follow the memory allocation statement with a test that the pointer that was returned is not NULLnull.
In the above memory allocations, since the code seems to be allocating arrays of IO_NeuronsIO_Neuron or NeuronsNeuron it might be better to use calloc(size_t num, size_t size)calloc(size_t num, size_t size) rather than malloc(). One of the benefits of using calloc(size_t num, size_t size) idis that all the memory allocated is zeroed out.
When using malloc()malloc(), calloc()calloc() or realloc()realloc() in C a common convention is to sizeof(*PTR)sizeof *PTR rather sizeof(PTR_TYPE), this makesizeof (PTR_TYPE). This makes the code easier to maintain and less error prone, since less editing is required if the type of the pointer changes.
Input Optimization
#Input Optimization
ItIt would be better to input an entire line at one time using getline(char **lineptr, size_t *n, FILE *stream)getline(char **lineptr, size_t *n, FILE *stream) rather than using fscanf() multiple times per line. The getline() fuctionfunction will input the entire line at once and then it can be processed using string functions or character manipulation. The optimization is that lessfewer system calls are used to get the input. It itmightmight also be easier to create a function to get a line of input and process it this way.
There is a programming principle called the Don't Repeat Yourself Principle sometimes referred to as DRY code. If you find yourself repeating the same code mutiplemultiple times it is better to encapsulate it in a function. If it is possible to loop through the code, that can reduce repetition as well.
Three of the functions, main(), TData tData(const char* filename) and void train(...) are too complex (do too much). All three of these functions should be broken into smaller functions.