Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code compiled with ICC works with -O2, warning about strtok and a crash with -O1

Tags:

c

icc

I have a text file that contains three columns of data in each line. The first two numbers are integer and the last one is double, i.e.

1 2 3.45
4 42 3.45
... and so forth...

I am using the following C code to read only the first line from the file.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    long int m, n;
    double val;
    FILE* f = fopen("input.txt", "r");
    char line[1024]; 
    char* pch;   
    fgets(line, sizeof(line), f);
    pch = strtok(line, " \t"); //** warning
    n = strtol(pch, NULL,10);
    pch = strtok(NULL, " \t");  //** warning
    m = strtol(pch, NULL,10);
    pch = strtok(NULL, " \t"); //** warning
    val = strtod(pch, NULL);
    ...
}

However, when I try to compile the code using -std=c89 -Wall -Wextra -O1 switches, I get the following warning messages for each strtok and the program crashes with segmentation fault:

<source>(9): warning #556: a value of type "int" cannot be assigned to an entity
of type "char *"

      pch = strtok(line, " \t");
          ^
...
Compiler returned: 0

But when I try the -O2 or -O3 switch, there are no warnings at all and my code works without crashes!

I am using the Intel 2019 compiler and Linux OS.

I really appreciate if anybody could help me resolve this issue.

like image 504
Ash Avatar asked Mar 05 '23 07:03

Ash


2 Answers

The reason is that you've forgotten to #include <string.h> which defines the prototype for strtok!

This bug happened to pass without notice only because you were explicitly using the C89 mode and because C89 allows implicit function declarations (where an undeclared function is assumed to return an int) and because the Intel C Compiler is buggy!


I've simplified the code to:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    FILE* f = fopen("foo", "r");
    char line[1024]; 
    char* pch;   
    fgets(line, sizeof(line), f);
    pch = strtok(line, " \t");
}

When compiled with -std=c89 and -O1 ICC reports

<source>(9): warning #556: a value of type "int" cannot be assigned to an entity 
of type "char *"

      pch = strtok(line, " \t");
          ^

If compiled with -O2 the warning is gone! But this does not comply to C89 3.3.16.1 Simple assignment which says that

Constraints

One of the following shall hold: [42]

  • the left operand has qualified or unqualified arithmetic type and the right has arithmetic type;

  • the left operand has a qualified or unqualified version of a structure or union type compatible with the type of the right;

  • both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;

  • one operand is a pointer to an object or incomplete type and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right; or

  • the left operand is a pointer and the right is a null pointer constant.

None of the 5 bullets is matched. Since a constraint is violated, a compliant compiler must issue a diagnostics message, which ICC does not do.


However, should you have used the -std=c11 mode, even on -O2 level the compiler will output diagnostics for the true culprit:

<source>(9): warning #266: function "strtok" declared implicitly

      pch = strtok(line, " \t");
            ^

Compiler returned: 0

i.e. in absence of an existing declaration for strtok the compiler used the C89 rule for implicit function declaration and implicitly assumed that the function would be declared as

int strtok();

But this violates the 2011 revision which no longer has the implicit function declarations, and for this reason a diagnostics message is output.


Finally it should be noted how really abysmally bad the ICC diagnostics are. If you use -std=c89 -O2 -Wall -Wextra for my program excerpt, you still receive no warnings whatsoever!

The main takeaways are:

  • never use the C89 mode. It is 30 years old. It is of same age as Windows 2.1x and MSDOS 4.0 and Mac System 6. You wouldn't use them either. Do note that even a version as late as ICC 16.0.3 seems to default to C89 mode.

  • ICC is not a standards-compliant C compiler. It is actually bad at diagnostics. That it accepts -Wall as a synonym for "-Wno-more" is unacceptable.

  • Therefore always use other compilers to develop and ICC only for tested code builds if it is demonstrably faster.

like image 114

I forgot to insert #include <string.h> . Thank you everyone!

like image 45
Ash Avatar answered Mar 09 '23 21:03

Ash