Непрозрачная структура, которая объявлена, но не определена - PullRequest
2 голосов
/ 22 марта 2019

Скажите, у меня есть декларация

my_header.h

struct my_struct;

struct my_struct* my_struct_create();
void my_struct_use(struct my_struct*);
void my_struct_delete(struct my_struct*);

В случае, если мы определим их следующим образом в my_header.c:

struct some_struct_came_from_3rdparty_lib;

struct my_struct* my_struct_create(){
    struct some_struct_came_from_3rdparty_lib *ptr = //create object
    return (struct my_struct*) ptr; //valid pointer conversion, ok
}
//void my_struct_use and void my_struct_delete definition omitted

ВОПРОС : вызывает ли следующий код UB:

struct my_struct *ptr = my_struct_create();
my_struct_use(ptr);

Я думаю, что так и есть. Несмотря на то, что стандарт разрешает нам конвертировать указатели, которые правильно выровнены 6.3.2.3(p7):

Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель не правильно align68) для ссылочного типа поведение не определено.

struct my_struct* и struct some_struct_came_from_3rdparty_lib* являются несовместимыми типами 6.7.6.1(p2):

Для совместимости двух типов указателей оба должны быть одинаково квалифицированы и оба должны быть указателями на совместимые типы.

Таким образом, доступ (чтение) struct some_struct_came_from_3rdparty_lib * через lvalue типа struct my_struct * в операторе struct my_struct *ptr = my_struct_create() должен вызвать UB. Имеет ли эта причина какой-то смысл?

Ответы [ 2 ]

2 голосов
/ 22 марта 2019

Проще говоря, если ваш файл C имеет

typedef struct some_struct_came_from_3rdparty_lib my_struct; 

тогда все в порядке, в противном случае это неопределенное поведение.

В качестве альтернативы вы можете определить my_struct, чтобы содержать some_struct_came_from_3rdparty_lib в качестве первого члена.

Все, что имеет значение, это то, как вы конвертируете между указателями в файле .c и как вы получаете доступ к данным.

2 голосов
/ 22 марта 2019

Технически, это зависит от того, что делает my_struct_use.Однако, если это нормально, и все, что он делает, приведено к правильному типу, как это:

void my_struct_use(struct my_struct *ptr)
{
    struct some_struct_came_from_3rdparty_lib *use = (struct some_struct_came_from_3rdparty_lib*)ptr;
    // ... use ptr
}

, то не существует неопределенного поведения.Вы никогда не получите доступ к lvalue типа struct some_struct_came_from_3rdparty_lib * как любой другой тип.Внутри my_struct_use, ptr на самом деле имеет тип struct my_struct * (он был создан броском, который производит новое значение).Тогда вполне нормально привести это (обратно) к struct some_struct_came_from_3rdparty_lib *, снова производя новое значение этого типа.

Это будет Неопределенным поведением, если вы сделаете это вместо этого:

struct some_struct_came_from_3rdparty_lib * use = *(struct some_struct_came_from_3rdparty_lib**)(&ptr);

Тогда вы получите доступ к ptr, который является объектом типа struct my_struct*, через lvalue типа struct some_struct_came_from_3rdparty_lib* (создается разыменованием результата приведения).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...