سؤال

أثناء إعادة بناء التعليمات البرمجية وتخليص نفسي من كل تلك #التعريفات التي تعلمنا الآن أن نكرهها، صادفت هذا الجمال المستخدم لحساب عدد العناصر في البنية:

#define STRUCTSIZE(s) (sizeof(s) / sizeof(*s))

مفيد جدًا كما هو ولكن هل يمكن تحويله إلى وظيفة أو قالب مضمّن؟

حسنًا، سيكون ARRAYSIZE اسمًا أفضل ولكن هذا رمز قديم (ليس لدي أي فكرة عن مصدره، فهو عمره 15 عامًا على الأقل) لذا قمت بلصقه "كما هو".

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

المحلول

كما ذكرنا سابقًا، فإن الكود يحسب فعليًا عدد العناصر في المصفوفة، وليس البنية.أود فقط أن أكتب قسم sizeof() بشكل صريح عندما أريد ذلك.إذا كنت سأجعلها وظيفة، أود أن أوضح في تعريفها أنها تتوقع مصفوفة.

template<typename T,int SIZE>
inline size_t array_size(const T (&array)[SIZE])
{
    return SIZE;
}

ما سبق مشابه ل xtofl's, ، إلا أنه يحمي من تمرير مؤشر إليه (يشير إلى مصفوفة مخصصة ديناميكيًا) والحصول على إجابة خاطئة عن طريق الخطأ.

يحرر:مبسطة حسب جون ماك جي. يحرر:في النسق.

لسوء الحظ، ما ورد أعلاه لا يوفر إجابة وقت الترجمة (حتى لو كان المترجم مضمنًا وقام بتحسينه ليكون ثابتًا تحت الغطاء)، لذلك لا يمكن استخدامه كتعبير ثابت لوقت الترجمة.أي.لا يمكن استخدامه كحجم للإعلان عن صفيف ثابت.ضمن C++0x، تختفي هذه المشكلة إذا تم استبدال الكلمة الأساسية في النسق بواسطة com.constexpr (constexpr مضمن ضمنيًا).

constexpr size_t array_size(const T (&array)[SIZE])

jwfearn's يعمل الحل لوقت الترجمة، ولكنه يتضمن وجود typedef الذي "يحفظ" حجم المصفوفة بشكل فعال في إعلان الاسم الجديد.يتم بعد ذلك تحديد حجم المصفوفة عن طريق تهيئة ثابت عبر هذا الاسم الجديد.في مثل هذه الحالة، يمكن للمرء ببساطة حفظ حجم المصفوفة في ثابت من البداية.

مارتن يورك يعمل الحل المنشور أيضًا في وقت الترجمة، ولكنه يتضمن استخدام الحلول غير القياسية نوع من() المشغل أو العامل.الحل البديل لذلك هو إما انتظار C++ 0x واستخدامه com.decltype (بحلول ذلك الوقت لن يحتاجه المرء فعليًا لهذه المشكلة كما سنفعل com.constexpr).البديل الآخر هو استخدام Boost.Typeof، وفي هذه الحالة سننتهي به

#include <boost/typeof/typeof.hpp>

template<typename T>
struct ArraySize
{
    private:    static T x;
    public:     enum { size = sizeof(T)/sizeof(*x)};
};
template<typename T>
struct ArraySize<T*> {};

ويستخدم عن طريق الكتابة

ArraySize<BOOST_TYPEOF(foo)>::size

أين foo هو اسم المصفوفة.

نصائح أخرى

لم يقترح أحد حتى الآن طريقة محمولة للحصول على حجم المصفوفة عندما يكون لديك فقط مثيل للمصفوفة وليس نوعها.(typeof و_countof ليسا محمولين لذا لا يمكن استخدامهما.)

سأفعل ذلك بالطريقة التالية:

template<int n>
struct char_array_wrapper{
    char result[n];
};

template<typename T, int s>
char_array_wrapper<s> the_type_of_the_variable_is_not_an_array(const T (&array)[s]){
}


#define ARRAYSIZE_OF_VAR(v) sizeof(the_type_of_the_variable_is_not_an_array(v).result)

#include <iostream>
using namespace std;

int main(){
    int foo[42];
    int*bar;
    cout<<ARRAYSIZE_OF_VAR(foo)<<endl;
    // cout<<ARRAYSIZE_OF_VAR(bar)<<endl;  fails
}
  • إنه يعمل عندما تكون القيمة موجودة فقط.
  • إنه محمول ويستخدم فقط std-C++.
  • فشل مع رسالة خطأ وصفية.
  • ولا يقيم القيمة.(لا أستطيع التفكير في موقف قد يمثل فيه هذا مشكلة لأنه لا يمكن إرجاع نوع المصفوفة بواسطة دالة، ولكن من الأفضل أن تكون آمنًا من أن تكون آسفًا.)
  • تقوم بإرجاع الحجم كثابت وقت الترجمة.

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

KTCالحل نظيف ولكن لا يمكن استخدامه في وقت الترجمة ويعتمد على تحسين برنامج التحويل البرمجي لمنع تضخم التعليمات البرمجية والحمل الزائد لاستدعاء الوظائف.

يمكن للمرء حساب حجم المصفوفة باستخدام دالة تعريفية لوقت الترجمة فقط بدون تكلفة وقت تشغيل. BCS كان على المسار الصحيح ولكن هذا الحل غير صحيح.

هذا هو الحل الخاص بي:

// asize.hpp
template < typename T >
struct asize; // no implementation for all types...

template < typename T, size_t N >
struct asize< T[N] > { // ...except arrays
    static const size_t val = N;
};

template< size_t N  >
struct count_type { char val[N]; };

template< typename T, size_t N >
count_type< N > count( const T (&)[N] ) {}

#define ASIZE( a ) ( sizeof( count( a ).val ) ) 
#define ASIZET( A ) ( asize< A >::val ) 

مع رمز الاختبار (باستخدام Boost.StaticAssert لإثبات استخدام وقت الترجمة فقط):

// asize_test.cpp
#include <boost/static_assert.hpp>
#include "asize.hpp"

#define OLD_ASIZE( a ) ( sizeof( a ) / sizeof( *a ) )

typedef char C;
typedef struct { int i; double d; } S;
typedef C A[42];
typedef S B[42];
typedef C * PA;
typedef S * PB;

int main() {
    A a; B b; PA pa; PB pb;
    BOOST_STATIC_ASSERT( ASIZET( A ) == 42 );
    BOOST_STATIC_ASSERT( ASIZET( B ) == 42 );
    BOOST_STATIC_ASSERT( ASIZET( A ) == OLD_ASIZE( a ) );
    BOOST_STATIC_ASSERT( ASIZET( B ) == OLD_ASIZE( b ) );
    BOOST_STATIC_ASSERT( ASIZE( a ) == OLD_ASIZE( a ) );
    BOOST_STATIC_ASSERT( ASIZE( b ) == OLD_ASIZE( b ) );
    BOOST_STATIC_ASSERT( OLD_ASIZE( pa ) != 42 ); // logic error: pointer accepted
    BOOST_STATIC_ASSERT( OLD_ASIZE( pb ) != 42 ); // logic error: pointer accepted
 // BOOST_STATIC_ASSERT( ASIZE( pa ) != 42 ); // compile error: pointer rejected
 // BOOST_STATIC_ASSERT( ASIZE( pb ) != 42 ); // compile error: pointer rejected
    return 0;
}

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

يحتوي الماكرو على اسم مضلل للغاية - سيعيد التعبير الموجود في الماكرو عدد العناصر الموجودة في صفيف إذا تم تمرير اسم الصفيف كمعلمة ماكرو.

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

عادةً ما تتم تسمية هذا الماكرو بشيء مثل NUM_ELEMENTS() أو بشيء ما للإشارة إلى فائدته الحقيقية.ليس من الممكن استبدال الماكرو بوظيفة في لغة C، ولكن في لغة C++ يمكن استخدام القالب.

يعتمد الإصدار الذي أستخدمه على الكود الموجود في رأس Winnt.h الخاص بشركة Microsoft (يُرجى إخباري إذا كان نشر هذا المقتطف يتجاوز الاستخدام العادل):

//
// Return the number of elements in a statically sized array.
//   DWORD Buffer[100];
//   RTL_NUMBER_OF(Buffer) == 100
// This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc.
//
#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0]))

#if defined(__cplusplus) && \
    !defined(MIDL_PASS) && \
    !defined(RC_INVOKED) && \
    !defined(_PREFAST_) && \
    (_MSC_FULL_VER >= 13009466) && \
    !defined(SORTPP_PASS)
//
// RtlpNumberOf is a function that takes a reference to an array of N Ts.
//
// typedef T array_of_T[N];
// typedef array_of_T &reference_to_array_of_T;
//
// RtlpNumberOf returns a pointer to an array of N chars.
// We could return a reference instead of a pointer but older compilers do not accept that.
//
// typedef char array_of_char[N];
// typedef array_of_char *pointer_to_array_of_char;
//
// sizeof(array_of_char) == N
// sizeof(*pointer_to_array_of_char) == N
//
// pointer_to_array_of_char RtlpNumberOf(reference_to_array_of_T);
//
// We never even call RtlpNumberOf, we just take the size of dereferencing its return type.
// We do not even implement RtlpNumberOf, we just decare it.
//
// Attempts to pass pointers instead of arrays to this macro result in compile time errors.
// That is the point.
//
extern "C++" // templates cannot be declared to have 'C' linkage
template <typename T, size_t N>
char (*RtlpNumberOf( UNALIGNED T (&)[N] ))[N];

#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A)))

//
// This does not work with:
//
// void Foo()
// {
//    struct { int x; } y[2];
//    RTL_NUMBER_OF_V2(y); // illegal use of anonymous local type in template instantiation
// }
//
// You must instead do:
//
// struct Foo1 { int x; };
//
// void Foo()
// {
//    Foo1 y[2];
//    RTL_NUMBER_OF_V2(y); // ok
// }
//
// OR
//
// void Foo()
// {
//    struct { int x; } y[2];
//    RTL_NUMBER_OF_V1(y); // ok
// }
//
// OR
//
// void Foo()
// {
//    struct { int x; } y[2];
//    _ARRAYSIZE(y); // ok
// }
//

#else
#define RTL_NUMBER_OF_V2(A) RTL_NUMBER_OF_V1(A)
#endif

#ifdef ENABLE_RTL_NUMBER_OF_V2
#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V2(A)
#else
#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V1(A)
#endif

//
// ARRAYSIZE is more readable version of RTL_NUMBER_OF_V2, and uses
// it regardless of ENABLE_RTL_NUMBER_OF_V2
//
// _ARRAYSIZE is a version useful for anonymous types
//
#define ARRAYSIZE(A)    RTL_NUMBER_OF_V2(A)
#define _ARRAYSIZE(A)   RTL_NUMBER_OF_V1(A)

أيضًا، يحتوي كتاب ماثيو ويلسون "Imperfect C++" على معالجة لطيفة لما يحدث هنا (القسم 14.3 - الصفحة 211-213 - المصفوفات والمؤشرات - أبعاد ()).

تم تسمية الماكرو الخاص بك بشكل خاطئ، ويجب أن يسمى ARRAYSIZE.يتم استخدامه لتحديد عدد العناصر في المصفوفة التي يتم تحديد حجمها في وقت الترجمة.وإليك طريقة يمكن أن تعمل:

شار فو[128];// في الواقع ، سيكون لديك بعض التعبير الثابت أو المستمر باعتباره حجم الصفيف.

ل(غير الموقعة ط = 0؛أنا <structsize (foo) ؛++ط ) { }

إنه نوع من الهشاشة في الاستخدام، لأنه من الممكن أن ترتكب هذا الخطأ:

char* foo = new char[128];

ل(غير الموقعة ط = 0؛أنا <structsize (foo) ؛++ط ) { }

سوف تقوم الآن بالتكرار من i = 0 إلى <1 وتمزق شعرك.

يتم استنتاج نوع دالة القالب تلقائيًا، على عكس فئة القالب.يمكنك استخدامه بشكل أبسط:

template< typename T > size_t structsize( const T& t ) { 
  return sizeof( t ) / sizeof( *t ); 
}


int ints[] = { 1,2,3 };
assert( structsize( ints ) == 3 );

لكنني أوافق على أنه لا يعمل مع الهياكل:إنه يعمل للمصفوفات.لذلك أفضل أن أسميها Arraysize :)

تبسيط @KTC، نظرًا لأن لدينا حجم المصفوفة في وسيطة القالب:

template<typename T, int SIZE>
int arraySize(const T(&arr)[SIZE])
{
    return SIZE;
}

العيب هو أنه سيكون لديك نسخة من هذا في ملفك الثنائي لكل مجموعة Typename وSize.

  • وظيفة، لا توجد وظيفة القالب، نعم
  • القالب، أعتقد ذلك (ولكن C++
  • القوالب ليست شيئًا خاصًا بي)

يحرر: من كود دوج

template <typename T>
uint32_t StructSize()  // This might get inlined to a constant at compile time
{
   return sizeof(T)/sizeof(*T);
}

// or to get it at compile time for shure

class StructSize<typename T>
{
   enum { result = sizeof(T)/sizeof(*T) };
}

لقد قيل لي أن الثاني لا يعمل.يجب أن يكون شيء من هذا القبيل قابلاً للتطبيق، أنا لا أستخدم C++ بما يكفي لإصلاحه.

صفحة على قوالب C++ (و D) لتجميع العناصر الزمنية

أفضل طريقة التعداد التي اقترحها [BCS](in هل يمكن تحويل هذا الماكرو إلى وظيفة؟)

هذا لأنه يمكنك استخدامه حيث يتوقع المترجم ثابت وقت الترجمة.لا يسمح لك الإصدار الحالي من اللغة باستخدام نتائج الوظائف لقيم وقت الترجمة ولكن أعتقد أن هذا سيأتي في الإصدار التالي من المترجم:

المشكلة في هذه الطريقة هي أنها لا تولد خطأ في وقت الترجمة عند استخدامها مع فئة قامت بتحميل عامل التشغيل '*' بشكل زائد (انظر الكود أدناه للحصول على التفاصيل).

لسوء الحظ، الإصدار المقدم من "BCS" لا يتم تجميعه تمامًا كما هو متوقع، لذا فهذه هي النسخة الخاصة بي:

#include <iterator>
#include <algorithm>
#include <iostream>


template<typename T>
struct StructSize
{
    private:    static T x;
    public:      enum { size = sizeof(T)/sizeof(*x)};
};

template<typename T>
struct StructSize<T*>
{
    /* Can only guarantee 1 item (maybe we should even disallow this situation) */
    //public:     enum { size = 1};
};

struct X
{
    int operator *();
};


int main(int argc,char* argv[])
{
    int data[]                                  = {1,2,3,4,5,6,7,8};
    int copy[ StructSize<typeof(data)>::size];

    std::copy(&data[0],&data[StructSize<typeof(data)>::size],&copy[0]);
    std::copy(&copy[0],&copy[StructSize<typeof(copy)>::size],std::ostream_iterator<int>(std::cout,","));

    /*
     * For extra points we should make the following cause the compiler to generate an error message */
    X   bad1;
    X   bad2[StructSize<typeof(bad1)>::size];
}

لا أعتقد أن هذا يحدد بالفعل عدد العناصر الموجودة في الهيكل.إذا كانت البنية معبأة واستخدمت أشياء أصغر من حجم المؤشر (مثل char على نظام 32 بت)، فستكون نتائجك خاطئة.أيضًا، إذا كانت البنية تحتوي على بنية فأنت مخطئ أيضًا!

نعم يمكن عمل قالب بلغة C++

template <typename T>
size_t getTypeSize()
{
   return sizeof(T)/sizeof(*T);
}

ليستخدم:

struct JibbaJabba
{
   int int1;
   float f;
};

int main()
{
    cout << "sizeof JibbaJabba is " << getTypeSize<JibbaJabba>() << std::endl;
    return 0;
}

راجع منشور BCS أعلاه أو أدناه حول طريقة رائعة للقيام بذلك مع فصل دراسي في وقت الترجمة باستخدام بعض البرمجة الوصفية الخفيفة للقالب.

لدى xtofl الإجابة الصحيحة للعثور على حجم المصفوفة.لا ينبغي أن يكون هناك ماكرو أو قالب ضروري للعثور على حجم البنية، حيث أن sizeof() يجب أن يعمل بشكل جيد.

أوافق على المعالج المسبق هو الشر, ، ولكن هناك مناسبات حيث يكون الأمر كذلك الأقل شرا من البدائل.

كإجابة JohnMcG، لكن

العيب هو أنه سيكون لديك نسخة من هذا في ملفك الثنائي لكل مجموعة Typename وSize.

لهذا السبب ستجعله في النسق وظيفة القالب.

تمت الإجابة عليه بالتفصيل هنا:تحديد حجم المصفوفة الجزء 1و هنا:تحديد حجم المصفوفة الجزء 2.

خاص بالويندوز:

هناك الماكرو _countof() المقدمة من قبل CRT بالضبط لهذا الغرض.

رابط للمستند على MSDN

بالنسبة للمصفوفات ذات الطول المتغير بنمط C99، يبدو أن أسلوب الماكرو النقي (sizeof(arr) / sizeof(arr[0])) هو الأسلوب الوحيد الذي سيعمل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top