سؤال

أنا أتساءل كيف يمكنني تحديد كائن في 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);
}

بالطبع هذا لا يزال غير وثيق الصلة ، لأنه يمكنني استخدام #F في حالتي. يتضمن المشروع في الواقع هياكل كبيرة ، والكثير من الملفات ، وعدد قليل من المترجمين ، وبعض أهداف وحدة المعالجة المركزية ، والعديد من المبرمجين الذين يولدون الأخطاء مع احتمال أسي لتعقيد بناء الجملة الذي يستخدمونه. هذا هو السبب في أنني أرغب في إعلان ماكرو بسيط عن كائن FOO الخاص بي.

هل كانت مفيدة؟

المحلول

أنت تحاول إنشاء رمز مع عنوان صفر. مثالك الأخير هو على الأرجح الطريقة الوحيدة للقيام بذلك ضمن برنامج التحويل البرمجي / اللغة C.

النهج الذي من المرجح أن يحل مشكلتك هو النظر في ملف الإدخال إلى برنامج الارتباط. تتيح لك معظم الروابط تحديد التسمية FOO على أنها صفر.

في برنامج نصي Unix LD هذا فقط: foo = 0 ؛

نصائح أخرى

يجب أن أفتقد شيئًا ما ، لكن ما لا يعمل void * bar = NULL?

في صفك ، يمكنك تجاوز & المشغل أو العامل:

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)

ما تريده هو مرجع يحمل عنوانًا فارغًا. هذه ممارسة سيئة للغاية ، ولكن هنا يذهب:

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

من فضلك ، لا تفعل ذلك بهذه الطريقة. قد يكون Auto_ptr خيارًا أفضل.

حسنًا ، يجب أن يكون السؤال ، لكنك تريد أن يكون لديك مؤشر بقيمة 0؟

ماذا عن

void * bar = (void*) 0;

ليس عليك أن تفعل كل ما في العبث: المؤشر هو مجرد رقم يربط المترجم به نوعًا ؛ إذا كنت تريد أن يكون الرقم 0 ، فاضرف 0.

أوه ، والإجابة على سؤال آخر ، ليس الأمر أن اللغة لها أي علاقة به ، فهي لا تعرف بالضرورة ما هو موجود في الموقع 0. واجهنا الكثير من المتاعب مع رمز BSD في اليوم لأنه في وقت مبكر من BSD الوحدات ، *0 == 0 كان صحيحا بشكل موثوق. لذلك يكتب الناس أشياء مثل

while(*c) doSomething();

لأنه عندما قاموا بتدوير 0x00 في نهاية السلسلة ، نظر إلى الموقع 0 الذي كان له القيمة 0. لسوء الحظ ، لم يكن هذا صحيحًا بالضرورة على منصات أخرى.

أشك حقًا في أن هناك طريقة للقيام بذلك في C99 Standard. في C ++ القياسية ، سيتم وضعك بجد للرجوع إلى الكائن بمجرد إنشاؤه ، إذا كان بإمكانك إنشائه ، حيث أن إزالة المؤشر الفارغ لا هو محدد ، وامتداد ثابتة ثابتة 0 في ثابت المؤشر هو ثابت مؤشر فارغ. (نعم ، يمكنك المحاولة int i; i = 0; foo * p = reinterpret_cast<foo *>i; p->doSomething(); لكن المعيار لا يحدد ما الذي يقوم به REINTERPRET_CACT <> ، بخلاف طريقة غامضة وتعتمد على التنفيذ.)

لذا ، سؤالي التالي هو ما تحاول إنجازه هنا. إذا كنت تحاول تحريف اللغة بطرق غريبة ومثيرة للاهتمام ، فهذا لا يلف بهذه الطريقة وفقًا للمعيار. إذا كان لديك استخدام مشروع لهذا ، فماذا سيكون؟

أعتقد أنك قريب ، مجرد خطوة من عدم التوجيه المؤشر بعيدا.

إذا حكمنا من خلال عينة الرمز الخاص بك ، فليست هناك حاجة إلى أخذ عنوان 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).

حسنًا ، هذا ما لدينا:

  • يسمح 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 وتعيينه لبعض المؤشر إلى بعض الكائنات.

انظر ، أنا آسف ، لكنك تجعل هذا الأمر صعبًا للغاية ومعقد للغاية.

إذا كنت ترغب في الاتصال بالشيء في 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. أو بالأحرى ، إنه غير محدد لأنه سيكون من الممكن بعد ذلك dereference 0 ، وهو غير محدد (أم أنه منصة محددة؟) في المعيار.

ماذا تحاول أن تفعل؟

إذا كنت تستخدم C ++ ، فيمكنك استخدام المراجع:

int *foo = 0;
int &bar = *foo;
int *foo_addr = &bar; // gives NULL
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top