Как определить объект с нулевым адресом? - PullRequest
0 голосов
/ 16 января 2009

Мне интересно, как я могу определить объект в C, ссылка на который будет нулевым?

// definition of foo 
...
void * bar = &foo; // bar must be null

Есть несколько способов, которые я мог бы найти, чтобы сделать это, но ни один не соответствовал моим потребностям.

__attribute__((weak)) extern int foo; //not working with cygwin/gcc 3.4
__attribute__((at(0))) int foo;       //only with rvds
#define foo (*(int*) 0)               //cannot be embedded in a macro

На самом деле, я бы предпочел стандартное совместимое решение (c99), но все, что работает все будет в порядке.


Отредактировано: причина для этого в том, что бар не всегда будет нулевым. Вот более подходящий пример:

// macro that will define foo to a real object or to *null
DECL(foo);

int * bar = &foo;

if(bar) {
  // we can call func
  func(bar);
} else {
  // bar undefined
  exit(-1);
}

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

Ответы [ 11 ]

16 голосов
/ 16 января 2009

Я должен что-то упустить, но что не работает в void * bar = NULL?

2 голосов
/ 16 января 2009

То, что вы хотите, это ссылка, которая содержит нулевой адрес. Это очень плохая практика, но здесь говорится:

#ifdef NON_NULL
Foo realFoo;
Foo &foo = realFoo;
#else
Foo &foo = *(Foo*)NULL;
#endif

Пожалуйста, не делай так. auto_ptr может быть лучшим вариантом.

2 голосов
/ 16 января 2009

В вашем классе вы можете переопределить оператор &:

class MyClass
{
    public:
        MyClass() :
            m_isNull(true)
        {
        }

        MyClass(int value) :
            m_isNull(),
            m_value(value)
        {
        }

        int value() const
        {
            /* If null, throw exception, maybe? */

            return m_value;
        }

        bool isNull() const
        {
            return m_isNull;
        }

        /////////////////////////
        // Here's the "magic". //
        /////////////////////////
        MyClass *operator&()
        {
            if(m_isNull)
                return 0;
            return this;
        }

    private:
        bool m_isNull;
        int m_value;
};

Это вызывает поведение, которое пользователь MyClass, вероятно, не ожидал бы. Я не уверен, где эта «особенность» потребуется или даже понадобится.

Если вы хотите взять реальный адрес экземпляра MyClass, вы можете использовать boost (как указано в комментариях к этому ответу):

MyClass foo;

MyClass *fooptr = &foo; // fooptr == NULL
fooptr = boost::addressof(foo); // fooptr = &foo (the real address)

Или вы можете использовать кастинг, поэтому MyClass::operator&() не называется:

struct DummyStruct {};

MyClass foo;

MyClass *fooptr = &foo; // fooptr == NULL
fooptr = &reinterpret_cast<DummyStruct>(foo); // fooptr = &foo (the real address)
1 голос
/ 16 января 2009

Вы пытаетесь создать символ с нулевым адресом. Ваш последний пример, вероятно, единственный способ сделать это в компиляторе / языке C.

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

В сценарии Unix ld это просто: foo = 0;

0 голосов
/ 19 января 2009

Слушай, извини, но ты делаешь это слишком сложным и слишком сложным.

Если вы хотите вызвать объект в C, вам нужен указатель на функцию. Так, например, у вас есть

/* ptr to 0-ary function returning int */
int (*func)() = NULL ;    /* That's all you need. */

// elsewhere ....
int myfunc() { /* do a good thing */ }
func = myfunc ;       /* function name *is* its address */

Теперь, в следующем коде вы можете сделать

if(!func) 
    (*func)();
else
    /* function not defined */

Вам не нужно связываться с загрузчиком, и вам не нужно - и не следует использовать - никаких макросов zippy, если вы действительно действительно есть веская причина для этого. Это все стандартные C.

0 голосов
/ 18 января 2009

Ну, вот что мы имеем:

  • C ++ допускает перегрузку operator& и имеет шаблоны для выполнения работы за вас, но не позволяет разыменовывать нулевой указатель.
  • C позволяет разыменовать нулевой указатель, если после этого берется адрес. Это также позволяет присваивать void* любому указателю на объект.

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

#define foo_ (*(void*) 0)

Теперь часть C ++. Поместите материал в nullp.hpp:

struct nullp { 
    struct proxy { 
        template<typename T> operator T*() { 
            return 0; 
        } 
    }; 

    proxy operator&() { 
        return omg(); 
    } 
} foo; 

Теперь все, что нам нужно сделать, это склеить вещи:

#ifdef __cplusplus
#include "nullp.hpp"
#else
#define foo (*(void*) 0)
#endif

Теперь вы можете использовать &foo и назначить его некоторому указателю на некоторый тип объекта.

0 голосов
/ 16 января 2009

Я думаю, что вы близко, всего в шаге от перенаправления указателя.

Судя по вашему примеру кода, не нужно брать адрес foo. Вы можете достичь желаемого, создав функцию, которая выделяет и создает bar.

Так что вместо:

DECL(foo);

int * bar = &foo;

Вы можете иметь:

#define FOO_IS_NULL 0

int * getFoo() 
{  
    if( FOO_IS_NULL )
    {
        return 0;
    }

    int *tmp = malloc(sizeof(int));
    *tmp = 1234;
    return tmp;
}

int * bar = getFoo();

Обратите внимание, что это полностью избавляет от переменной foo.

Единственное предостережение в том, что теперь вам нужно free(bar).

0 голосов
/ 16 января 2009

Если вы используете C ++, вы можете использовать ссылки:

int *foo = 0;
int &bar = *foo;
int *foo_addr = &bar; // gives NULL
0 голосов
/ 16 января 2009

Я действительно сомневаюсь, что есть способ сделать это в стандартном C99. В стандартном C ++ вам будет трудно сослаться на созданный объект, если вы сможете его создать, поскольку разыменование константы нулевого указателя не определено, а константа 0 в константе указателя является константой нулевого указателя. (Да, вы можете попробовать int i; i = 0; foo * p = reinterpret_cast<foo *>i; p->doSomething();, но стандарт не определяет, что делает reinterpret_cast <>, кроме как в расплывчатом и зависящем от реализации виде.)

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

0 голосов
/ 16 января 2009

Хорошо, это вопрос, но вы хотите иметь указатель со значением 0?

как насчет

void * bar = (void*) 0;

Вам не нужно заниматься всем этим безобразием: указатель - это просто число, с которым компилятор связывает тип; если вы хотите, чтобы число было 0, присвойте 0.

Да, и отвечая на другой вопрос, дело не в том, что язык имеет к этому какое-то отношение, просто вы не обязательно знаете, что находится в местоположении 0. У нас было много проблем с кодом BSD в тот день, потому что на ранних версиях BSD * 0 == 0 было достоверно верно. Чтобы люди писали что-то вроде

while(*c) doSomething();

потому что, когда они разыменовывали 0x00 в конце строки, он смотрел на LOCATION 0, который имел ЗНАЧЕНИЕ 0. К сожалению, это не всегда верно для других платформ.

...