Может ли неиспользованное определение типа или определения структуры когда-либо вызвать изменение поведения в программе на Си? - PullRequest
1 голос
/ 10 октября 2019

Предположим, у кого-то есть код в следующих строках:

struct Container1
{
   int data;
};

typedef int Container2;

int main ()
{
   // code that does stuff but never
   // utilizes any of the 'Container'
   // types.
}

В этом примере ни Container1, ни Container2 не используются. Я должен уточнить, что это общий пример. У меня вопрос, есть ли ЛЮБЫЕ обстоятельства в известном «неопределенном поведении» от определенных компиляторов или , в предложении стандарта C или явная ошибка компилятора, которая привела бы к присутствиюнеиспользуемой структуры, имеющей явное изменение поведения скомпилированной программы.

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

Я ожидаю, что есть жесткий ответ NO , но яВ прошлом люди мимоходом упоминали, что видели необычные ошибки из-за удаления или изменения определения или даже изменения имени полностью неиспользованного определения типа или определения структуры на 100%. У меня нет таких примеров, и поэтому я скажу, что я принимаю претензии с долей соли и небольшим любопытством о том, почему / как это может произойти.

Ответы [ 4 ]

4 голосов
/ 10 октября 2019

Краткий ответ: нет . И компилятор должен создавать идентичный (или эквивалентный) объектный код с и без неиспользуемой структуры.

У каждого опытного инженера есть история о какой-то странной ошибке в результате изменения чего-то безобидного. Но подобные случаи - это из-за какой-то другой ошибки, а не из-за неиспользуемого типа данных.

3 голосов
/ 10 октября 2019

Вопрос, который у меня возникает, заключается в том, есть ли ЛЮБЫЕ обстоятельства в известном «неопределенном поведении» определенных компиляторов, или в предложении стандарта C, или в явной ошибке компилятора, которая может привести к тому, что неиспользуемая структура будет иметьявное изменение поведения скомпилированной программы

Только если эти идентификаторы Container1 и Container2 конфликтуют с другими идентификаторами в другом месте программы.

Но всегда можно придумать различные более или менее искусственные сценарии, которые могут быть исключениями из этого:

  • В некоторых случаях возникают синтаксические ошибкиЗаголовочный файл включен перед этими определениями, а определения как-то исправили ошибку. Пример:

    foo.h

    typedef
    

    foo.c

    #include "foo.h"
    
    struct Container1 
    {
       int data;
    };
    
    int x;
    

    Если структура удалена, x станет typedef для int. В противном случае x является переменной типа int.

  • Удаление структуры, очевидно, повлияет на такие вещи, как макрос __LINE__.

    #include <stdio.h>
    
    struct Container1
    {
       int data;
    };
    
    int main()
    {
      printf("%s", &"hello world"[__LINE__]); 
    }
    

    Вывод:d

    #include <stdio.h>
    
    int main()
    {
      printf("%s", &"hello world"[__LINE__]);
    }
    

    Выходные данные: world

  • C90 гарантируется только до 6 уникальных символов внешнего идентификатора. Таким образом, если бы в другом месте был какой-то код с другой структурой, называемой Container2, и некоторым внешним идентификатором, таким как extern struct Container2, то наличие / отсутствие определения структуры Container1 могло бы дать другие результаты, поскольку только часть Contaiидентификатор должен быть уникальным.

  • И так далее ... C - сложный язык, наполненный странными лазейками и особыми случаями.

2 голосов
/ 10 октября 2019

Да, в теории. Рассмотрим этот код:

int a, b;
printf("%d\n", &a+1 == &b);

В соответствии с C 2018 6.5.9 6, &a+1 == &b истинно тогда и только тогда, когда b случается, если немедленно следует a в адресном пространстве. ,Это определенное поведение, но поведение реализации C может привести к тому, что результат будет либо 0, либо 1.

Теперь предположим, что реализация C управляет своими идентификаторами через некоторую причудливую структуру данных. Он читает a и запоминает информацию о нем, запоминает b и запоминает информацию о нем и так далее. Позже, когда он назначает хранилище объектам, он считывает из своей базы данных идентификаторы. В отсутствие дополнительных ограничений, таких как размер и выравнивание, влияющих на то, как он выделяет хранилище, он может просто распределять хранилище в любом порядке, в котором база данных производит идентификаторы при переходе по базе данных, чтобы получить их.

ЭтоМожно предположить, что простое изменение того, какие идентификаторы находятся в базе данных, может изменить порядок, в котором другие создаются при чтении из базы данных. Таким образом, введение нового имени типа может изменить порядок, в результате чего b будет следовать за a в памяти, когда оно не без нового имени.

Другими словами, поведение здесь, относительно того, как именауправляемый и то, как распределено хранилище, по крайней мере частично, неконтролируемо - оно не спроектировано так или иначе. И поэтому оно может измениться, основываясь на принципиально не относящихся к делу вещах.

0 голосов
/ 10 октября 2019

явная ошибка компилятора

Если ваш компилятор глючит, то может произойти все что угодно. Например, ошибка в компиляторе может заключаться в том, что если определена typedef с именем «Container2», то она вставит «rm -rf /» в вашу основную функцию.

Видите, теперь неиспользованная typedef имеетэффект.

...