Reading and classifying lines from a file I'm new to C which I'm learning in university now, and I'm not sure if the following is considered in C good practices or not. 

For an assignment in a simple Classification problem I wrote the following code:

int train(FILE *file, const dim dim, const int m, vector *linearSeparator)
{
    size_t len = 0;
    char *line;
    bool stop = false;

    Sample *sample = NULL;
    for(int i = 0; i &lt; m &amp;&amp; getline(&amp;line, &amp;len, file) != -1 &amp;&amp; !stop; i++)
    {
        if (strToSample(line, SEPARATOR, dim, sample) != EXIT_SUCCESS ||
            calibrateLinearSeparator(sample, linearSeparator, dim) != EXIT_SUCCESS)
        {
            printError(TRAINING, "Failed processing line");
            stop = true;
        }
        free(line);
        freeSample(sample);
    }
    line = NULL;
    return stop ? EXIT_FAILURE : EXIT_SUCCESS;
}


The functions:


strToSample gets a char* line and constructs a Sample object out of it, setting the *sample pointer. It returns EXIT_SUCCESS if succeeded and an error code if failed.
calibrateLinearSeparator uses the sample set by the previous function to manipulate the linearSeparator object (vector - a custom struct to represent a double[]). This function too returns EXIT_SUCCESS if succeeded and an error code if failed.


Both these functions print details about their internal errors and at this level I'm printing in what sample id that internal error occurred.

My question is: 


Is this way of writing the if statement a good practice? Is it readable? Should I do it differently?

It feels as if these functions are having side effects. They get pointers to set but are used in an if statement to check if they succeeded in a way that order matters.

The idea behind writing it this way is that if either of these functions fail I want to print an error and not continue the loop. If the first one fails then the second is not performed (which is what I want). In any case of success or failure I want to free the allocated memory.
Is it a good practice in C to write the for condition the way I did? Having 3 conditions in it? (checking i, trying to bring next line and !stop?) It seems to me that this helps making the code cleaner as there are less "details" lines (of increasing i++ for example if the loop was a while loop on getLine)
Code has bug. After free'ing line, set len = 0; line = NULL;

    free(line);
    line = NULL; // add
    len = 0; // add


Further, it would be better to initialize line and move this free();  ... to outside the for()   loop.  Also line = NULL; len = 0; would not be needed there.

size_t len = 0;
char *line = NULL;
...
for(...; ... getline(&amp;line, &amp;len, file) ...; ...) {
  ...
}
free(line);





  Is this way of writing the if statement a good practice? Is it readable? Should I do it differently?


It is not clear that the  { printError(... true; } block is part of if() and not a free standing block.  Consider the below.  The first lacks clarity that the first block is part of the if() and the 2nd block simply a stand-along block

    if (strToSample(line, SEPARATOR, dim, sample) != EXIT_SUCCESS ||
        calibrateLinearSeparator(sample, linearSeparator, dim) != EXIT_SUCCESS)
    {
        printError(TRAINING, "Failed processing line");
        stop = true;
    }
    {
        printError(TRAINING, "Status");
    }
    free(line);
    freeSample(sample);


vs. this form.  The 1st block is part of the if() and the 2nd block is not nested like the first.

    if (strToSample(line, SEPARATOR, dim, sample) != EXIT_SUCCESS ||
        calibrateLinearSeparator(sample, linearSeparator, dim) != EXIT_SUCCESS)
      {
        printError(TRAINING, "Failed processing line");
        stop = true;
      }
    {
        printError(TRAINING, "Status");
    }
    free(line);
    freeSample(sample);





  Is it a good practice in C to write the for condition the way I did? 


Why read a line if stop is true?  Certainly this is surprising functionality.  Given 3 stopping conditions, it appears to me that the combination of them occurring in groups, the side effects of getline() , and the short-circuit nature of &amp;&amp; led to weak function flow.

for(int i = 0; i &lt; m &amp;&amp; getline(&amp;line, &amp;len, file) != -1 &amp;&amp; !stop; i++)
// vs. 
for(int i = 0; i &lt; m &amp;&amp; !stop &amp;&amp; getline(&amp;line, &amp;len, file) != -1; i++)


IMO, a for() loop is best as for (initialize_index, index_test; index_delta).  Another other stopping condition belongs in the body.  Example:

for (int i = 0; i &lt; m; i++) {
  if (stop || getline(&amp;line, &amp;len, file) == -1) {
    break;
  }
  ...
}


Notice this form avoid ! in 2 places.  Negation adds to complexity.  Don't you not think it is not so and not so when not done?