Код, скомпилированный с ICC, работает с -O2, предупреждая о strtok и сбое с -O1 - PullRequest
4 голосов
/ 26 марта 2019

У меня есть текстовый файл, который содержит три столбца данных в каждой строке.Первые два числа являются целыми числами, а последнее - двойным, то есть

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

Я использую следующий код C для чтения только первой строки из файла.

#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);
    ...
}

Однако, когда я пытаюсь скомпилировать код с помощью переключателей -std=c89 -Wall -Wextra -O1, я получаю следующие предупреждение сообщения для каждого strtok, и программа падает с ошибкой сегментации:

<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

Но когда я пытаюсь использовать переключатель -O2 или -O3, предупреждений вообще нет, и мой код работает без сбоев!

Я использую компилятор Intel 2019 и ОС Linux.

Я очень признателен, если кто-нибудь поможет мне решить эту проблему.

Ответы [ 2 ]

4 голосов
/ 26 марта 2019

Причина в том, что вы забыли #include <string.h>, который определяет прототип для strtok!

Эта ошибка прошла без уведомления только потому, что вы явно использовали режим C89 и , потому что C89 допускает неявные объявления функций (где предполагается, что необъявленная функция возвращает int) и потому что компилятор Intel C глючит!


Я упростил код до:

#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");
}

Когда скомпилировано с -std=c89 и -O1 ICC-отчеты

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

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

Если скомпилировано с -O2, предупреждение исчезло! Но это не соответствует C89 3.3.16.1 Простое назначение , которое говорит, что

Ограничения

Должно быть одно из следующего: [42]

  • левый операнд имеет квалифицированный или неквалифицированный арифметический тип, а правый имеет арифметический тип;

  • левый операнд имеет квалифицированную или неквалифицированную версию структуры или типа объединения, совместимого с типом правого;

  • оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов, а тип, на который указывает слева, содержит все квалификаторы типа, на который указывает справа;

  • один операнд является указателем на объект или неполный тип, а другой - указателем на квалифицированную или неквалифицированную версию void, а тип, на который указывает левый, имеет все квалификаторы типа, на который указывает право; или

  • левый операнд является указателем, а правый - константой нулевого указателя.

Ни одна из 5 пуль не соответствует. Поскольку ограничение нарушено, совместимый компилятор должен выдать диагностическое сообщение , которое ICC не выполняет .


Однако, если бы вы использовали режим -std=c11, даже на -O2 level , компилятор выведет диагностику для истинного виновника:

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

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

Compiler returned: 0

т.е. в отсутствие существующего объявления для strtok компилятор использовал правило C89 для неявного объявления функции и неявно предполагал, что функция будет объявлена ​​как

int strtok();

Но это нарушает ревизию 2011 года, которая больше не имеет неявных объявлений функций , и по этой причине выводится диагностическое сообщение.


Наконец, следует отметить, насколько действительно ужасно плох, диагностика ICC . Если вы используете -std=c89 -O2 -Wall -Wextra для моей программы, вы все равно не получите никаких предупреждений !

Основные выносы:

  • никогда не используйте режим C89 . Это 30 лет. Он того же возраста, что и Windows 2.1x и MSDOS 4.0 и Mac System 6 . Вы бы их тоже не использовали. Обратите внимание, что даже версия, более поздняя, ​​чем ICC 16.0.3, кажется, по умолчанию в режиме C89.

  • ICC не является стандартным компилятором Си. Это на самом деле плохо при диагностике. То, что он принимает -Wall как синоним для "-Wno-more" , недопустимо.

  • Поэтому всегда используйте другие компиляторы для разработки, а ICC только для тестируемых сборок кода, если это заметно быстрее.

3 голосов
/ 26 марта 2019

Я забыл вставить #include <string.h>. Спасибо всем!

...