Как проверить, является ли указатель в C определенного типа? - PullRequest
13 голосов
/ 04 июня 2009

Как проверить, относится ли указатель к определенному типу?

Использование sizeof недостаточно.

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

Ответы [ 11 ]

39 голосов
/ 04 июня 2009

Вы не можете.

Указатель просто хранит адрес и ничего не относится к содержимому этого адреса.

14 голосов
/ 04 июня 2009

«Я стараюсь не вводить идентификаторы в свои структуры, чтобы определить их тип». Не избегай этого. Если вы действительно хотите иметь возможность проверять тип, поместите typeID в качестве первого элемента каждой структуры. Ваш импульс не был плохим.

9 голосов
/ 04 июня 2009

Gcc не помещает определение структуры нигде во время выполнения. Это означает, что вы не можете в качестве стандарта.

Это может зависеть от того, для чего вы используете информацию о типе. Два основных приложения могут быть:

  1. Отладка или аналогичная проверка во время выполнения
  2. Сериализация и десериализация структур данных

В первом случае часто доступна информация, которая хранится в символах, выводимых компилятором, и прикрепляется к исполняемому файлу (во многих средах).

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

Для типов сериализации часто помечаются значения, как вы предлагаете. Эти теги не должны храниться вместе с данными в памяти. Они могут быть добавлены программой вывода.

7 голосов
/ 04 июня 2009

Указатель является типом. В более общем смысле, C не дает возможности делать самоанализ. Нет программного способа определения типа переменной.

5 голосов
/ 05 июня 2009

Все ответы, опубликованные здесь, говорят: «Вы не можете». Они правы. Вы не можете.

Но, и я стесняюсь даже упомянуть об этом, - это игры, в которые можно играть. Эти игры - плохая идея. Я не рекомендую их в любой ситуации.

Что за игра? Вбивание стековых дополнительных битов данных в неиспользуемые части адреса и удаление их, где бы вы ни использовали адрес.

Итак, представьте, что у вас есть указатели на структуру или класс размером 32 байта. Если вы убедитесь, что ваши выделения памяти выровнены по 32-байтовому адресу (что легко для динамических распределений, но сложнее гарантировать для стековых), младшие биты адреса будут равны 0. Это означает, что в 5 свободных битах внизу адреса, достаточно для вас поставить флаги, идентификационные номера, значения статуса и т. д. Хранилище бесплатно! Даже если у вас странный размер структуры, практически любой компилятор C или C ++ и ОС всегда будут выравнивать каждый адрес до 4 байтов.

В 64 битах обычно можно найти значительное количество свободных бит в верхнем конце адресов ... очень вероятно, 16 свободных неиспользованных битов ждут вас. Это зависит от ОС, конечно. А также плохая плохая идея для рассмотрения. Тебе нравится опасность?

Два недостатка:

  1. Вы должны обязательно замаскировать ценности, прежде чем пытаться разыменовать указатель или передать его ко всему, что может попробовать. Это делает некрасивый код.
  2. Вы танцуете за гранью непереносимость обрыва тупого опасности. Это как пить бутылка текилы и ходьба потертая канат над голодными тиграми. Nude.

Your fate if you follow my advice
(источник: ecoworldly.com )

Есть так много способов, которые могут привести к ошибкам, сбоям и боли. Помните, как я сказал, что компиляторы выравнивают память как минимум до 4 байтов? Ну, это правда. Пока вы не найдете новую ОС, которой нет. И вы едите тигра.

Так что не делай этого.

Но, тем не менее, это действительно способ добавить немного дополнительной информации, такой как номер типа, в каждый адрес. Вы можете увидеть эту технику в коде каждого байта или обфусцированном C.

PS: Действительно, я имею в виду, не делайте этого. Даже если вы любите тигров.

4 голосов
/ 04 июня 2009

Нет, нигде нет информации о типе среды выполнения.

Вы должны написать свои функции так, чтобы сигнатура функции содержала информацию о типе и позволяла компилятору проверять типы статически. Например, void burp_foo(struct foo *thefoo) говорит, что аргумент является указателем на struct foo. Это зависит от звонящего. Конечно, с помощью приведения типов можно указывать любой указатель, указывающий на struct foo, но тогда это проблема вызывающего, а не ваша.

2 голосов
/ 05 июня 2009

Чтобы избежать добавления элемента ID в вашу структуру, используйте полиморфизм, такой как:

typedef struct {
        char const *name;
        int id;
        /* ... */
} object_info_t;

typedef struct {
        object_info_t *info;
} object_t;

typedef struct {
        object_t object;
        int a;
} foo_t;

typedef struct {
        object_t object;
        int b;
} bar_t;

int object_get_id(object_t *object)
{
        return object->info->id;
}

Обратите внимание, что вы можете увеличить object_info_t с помощью указателей функций и вообще не проверять id s:

typedef struct {
        char const *name;
        int id;
        int (*do_something)(object_t *);
} object_info_t;

int object_do_something(object_t *self)
{
        return self->info->do_something(self);
}
2 голосов
/ 04 июня 2009

Вы не можете сделать это в C. На самом деле компилятор C ++ делает что-то вроде того, что вы хотите (сохраняя информацию о типе класса в выделенной памяти). В C у вас нет классов, а есть только структуры, которые не содержат никаких накладных расходов.

1 голос
/ 04 июня 2009

В Си нет ничего подобного. Указатель либо определенного типа, либо void*. Там нет перегрузки функций или встроенного полиморфизма во время выполнения, как в C ++. Однако вы можете выполнять псевдообъект-ориентированные трюки, используя указатели функций, например:


typedef void (*cmd_func)( int );
struct command
{
    int      arg;
    cmd_func action;
};
0 голосов
/ 13 сентября 2013

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

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