Search code examples
cstrtokfactorsstrtol

End condition for a factorising loop


Good evening,

I'm having trouble with an assignement.

Basically we're required to code a program which will calculate the prime factors of a given stdin. The data may only enter the program through its stdin, be it throuh an echo or a < file.txt. The stream of data will never be greater than 80 characters (they can be numbers or not).

The functions I use in the program are read(), strotol() and strtok(), and the "irrelevant" code flows as follows:

  1. Uses malloc to allocate 80 initial bytes of memory.
  2. Stores in an int, through read() the number of characters read (and I believe, the last \0).
  3. reallocates memory with realloc() so as to save as much memory (I know in this case it's trivial, but oh well...).

Now comes the tricky bit:

  1. Since the data has to be separated with spaces, the max amount of items to check are at most: (n/2)+1, where n is the number of characters read in the upper point nº 2.
  2. Creates an array of long with max size the number obtained in point nº 1.
  3. Fills numbers[0] with the result of: strtol(strtok(line, delim), &end, 10).
  4. Adds 1 to the counter and enters a while loop:

    while((numbers[counter] = strtol(strtok(NULL, delim), &end, 10)) != NULL) {
        if(!*end) {
            // Check whether it's 0, 1, negative, prime or extract its factors
        }
        else {
            fprintf(stderr, "\"%s\" is not a non-negative integer", end)
        }
        counter++;
    }
    

Now, here are some inputs and their outputs:

Input: echo 1 2 3 4 5 6 7 8 9 10 | ./factors

Output:

1
2    
3
    2    2
5
    2    3
7 
    2    2    2
    3    3
    2    5
Segmentation Fault (core dumped). 

Input ./factors < integers.txt Where integers contains a COLUMN of integers.

Output:

All the integers are factorised just fine and at the end it prints a:

Segmentation Fault (core dumped). 

Input: echo abc 12 13 14 15 | ./factors

Output:

"abc" is not a non-negative integer
13
    2    7
    3    5
Segmentation Fault (core dumped). 

Input: echo -1 -2 -3 -4 -5 | ./factors

Output:

"-1" is not a non-negative integer
"-2" is not a non-negative integer
"-3" is not a non-negative integer
"-4" is not a non-negative integer
"-5" is not a non-negative integer

Input: echo abc abc abc | ./factors

Output:

"abc" is not a non-negative integer

(And does not continue checking).

Input: echo 3 4 0 6 7 | ./factors

Output:

3
    2    2

(And does not continue checking).

So as far as I can see, it fails when it encounters a 0, more than one instance of a non-integer or basically at the end of a healthy integer-based stream of data.

Any idea how I could tackle this while and why is it failing so apparently randomly?

I should let you know I'm new to C...

Thank you very much in advanced.

====================================================

EDIT1: As per request, here are the code fragments which generate numbers[], and read from stdin:

char *line;
char *end;
char *delim = " \n";
int charsread, counter; 

line = malloc(80);
charsread = read(0, line, 81);
if (charsread == 0) {
    return EX_OK;
}
realloc(line, charsread);
maxelem = (charsread / 2) + 1;
long numbers[maxelem];
numbers[0] = strtol(strtok(line, delim), &end, 10);
if (!*end) {
    zeroone(numbers[0]);
}
else {
    fprintf(stderr, "\"%s\" is not a non-negative integer\n", end);
}
counter = 1;
while [...]

Solution

  • Thank you very much for your insightful response.

    In the end I just rewrote the whole thing up since it didn't look as clean as I expected. Since the manual from strtok specifies a return value of NULL if no token could be extracted, this is what I ended up with:

    long number;
    item = strtok(line, delim);
    while (item != NULL) {
           number = strtol(item, &rest, 10);
           if (*rest == 0) {
               zeroOne(number);
           }
           else {
               fprintf(stderr, "\"%s\": not a non-negative int.\n", item);
           }
           item = strtok(NULL, delim);
    }   
    

    Seems a cleaner approach and does consider the fact that the returned value from the strtok when it is NULL is returned BEFORE it tries to enter the while loop I had before. Then, this morning I came back here and read your response :)

    A follow-up question regarding your hard-coded input of a \0 to the line read: Does this mean that my last strtok actually outputs the \0 token and tries to enter the loop, or does it return a NULL when it reaches the entity right after my last introduced character? As a convention, when using read() (or maybe other reading functions such as fgets() should I always try to hard-code a \0 to the line read to allow other functions to check for EOL / EOF?

    If someone else should be having trouble using any of these functions (strtok and strtol) I recommend to check out these two questions posted on this very site:

    Regarding strtol's second argument: Strtol second argument

    Regarding strtok's output: Cannot concatenate strtok's output variable. strcat and strtok