programiz - variable in c




"const الثابت" مقابل "#define" مقابل "التعداد" (11)

أيهما أفضل لاستخدام بين العبارات أدناه في C؟

static const int var = 5;

أو

#define var 5

أو

enum { var = 5 };

إذا كنت تستطيع أن تفلت من static const ، فإن static const لديها الكثير من المزايا. وهو يطيع مبادئ النطاق الطبيعي ، ويكون مرئيًا في مصحح الأخطاء ، ويطيع عمومًا القواعد التي تطيع المتغيرات.

ومع ذلك ، على الأقل في المعيار C الأصلي ، فإنه ليس ثابتًا في الواقع. إذا كنت تستخدم #define var 5 ، يمكنك كتابة int foo[var]; كإعلان ، ولكن لا يمكنك القيام بذلك (باستثناء ما هو امتداد للامتداد البرمجي "بـ static const int var = 5; . هذه ليست الحالة في C ++ ، حيث يمكن استخدام إصدار static const أي مكان يمكن لـ #define version ، وأعتقد أن هذا هو الحال أيضا مع C99.

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


التعريف

const int const_value = 5;

لا تحدد دائمًا قيمة ثابتة. بعض compilers (على سبيل المثال tcc 0.9.26 ) فقط تخصيص ذاكرة تعريف بالاسم "const_value". باستخدام المعرف "const_value" لا يمكنك تعديل هذه الذاكرة. ولكن لا يزال بإمكانك تعديل الذاكرة باستخدام معرف آخر:

const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.

هذا يعني التعريف

#define CONST_VALUE 5

هي الطريقة الوحيدة لتحديد قيمة ثابتة لا يمكن تعديلها بأي وسيلة.


بالمناسبة ، هو بديل ل #define ، والذي يوفر تحديد نطاق مناسب ولكن يتصرف مثل ثابت "حقيقي" ، هو "التعداد". فمثلا:

enum {number_ten = 10;}

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

ومع ذلك ، فإن أحد التحذيرات المهمة للقيام بذلك هو: في C ++ ، تكون الأنواع التي تم تعدادها محدودة التوافق مع الأعداد الصحيحة. على سبيل المثال ، بشكل افتراضي ، لا يمكن للمرء أن يقوم بعمليات حسابية عليه. أجد أن يكون السلوك الافتراضي الغريب للتعداد ؛ بينما كان من اللطيف الحصول على نوع "التعداد الصارم" ، نظراً للرغبة في أن يكون C ++ متوافقاً بشكل عام مع C ، أعتقد أن السلوك الافتراضي لنوع "enum" يجب أن يكون قابلاً للتبادل مع الأعداد الصحيحة.


بشكل عام:

static const

لأنه يحترم النطاق وهو آمن من النوع.

التحذير الوحيد الذي استطعت رؤيته: إذا كنت تريد أن يتم تعريف المتغير على سطر الأوامر. لا يزال هناك بديل:

#ifdef VAR // Very bad name, not long enough, too general, etc..
  static int const var = VAR;
#else
  static int const var = 5; // default value
#endif

كلما كان ذلك ممكنًا ، بدلاً من استخدام الماكرو / علامة القطع ، استخدم بديلًا آمنًا من النوع.

إذا كنت في حاجة فعلاً إلى الذهاب باستخدام الماكرو (على سبيل المثال ، تريد __FILE__ أو __LINE__ ) ، فمن الأفضل أن تسمي الماكرو بعناية __FILE__ : في اتفاقية التسمية الخاصة بها ، يوصي Boost بكل الحالة العليا ، بداية من اسم المشروع ( هنا BOOST_) ، بينما تلاحق المكتبة ، ستلاحظ أن هذا (بشكل عام) يتبعه اسم منطقة معينة (مكتبة) ثم يحمل اسمًا ذا معنى.

عموما يجعل لأسماء طويلة :)


عيب آخر من const في C هو أنه لا يمكنك استخدام القيمة في تهيئة const آخر.

static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;

// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND 
                                     * NUMBER_OF_HANDS;

حتى هذا لا يعمل مع const حيث أن المترجم لا يراها ثابتة:

static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!

سأكون سعيدًا باستخدام const مكتوبة في هذه الحالات ، وإلا ...


فرق بسيط:

في وقت ما قبل المعالجة ، يتم استبدال الثابت بقيمته. لذلك لا يمكنك تطبيق عامل dereference على تعريف ، ولكن يمكنك تطبيق مشغل dereference على متغير.

كما افترض ، تحديد أسرع هذا const ثابت.

على سبيل المثال ، وجود:

#define mymax 100

لا يمكنك عمل printf("address of constant is %p",&mymax); .

لكن وجود

const int mymax_var=100

يمكنك عمل printf("address of constant is %p",&mymax_var); .

ليكون أكثر وضوحًا ، يتم استبدال التعريف بقيمته في مرحلة ما قبل المعالجة ، لذلك ليس لدينا أي متغير مخزّن في البرنامج. لدينا فقط رمز من جزء النص من البرنامج حيث تم استخدام التعريف.

ومع ذلك ، للفقرة ثابتة لدينا متغير يتم تخصيصه في مكان ما. بالنسبة إلى gcc ، يتم تخصيص ثوابت ثابتة في جزء النص من البرنامج.

وفوق ذلك ، أردت أن أخبر المشغل المرجعي ، لذلك استبدل الانحراف بالمرجعية.


في C ، على وجه التحديد؟ في الإجابة الصحيحة C: استخدم #define (أو ، إذا كان مناسبًا ، enum )

في حين أنه من المفيد الحصول على خصائص تحديد وطباعة كائن const ، إلا أن كائنات const في C (بدلاً من C ++) ليست ثوابت حقيقية وبالتالي فهي عديمة الفائدة في معظم الحالات العملية.

لذا ، في C يجب أن يتم تحديد الخيار من خلال كيفية التخطيط لاستخدام ثابتك. على سبيل المثال ، لا يمكنك استخدام كائن const int كعلامة case (أثناء عمل الماكرو). لا يمكنك استخدام كائن const int كعرض حقل بت (بينما سيعمل الماكرو). في C89 / 90 لا يمكنك استخدام كائن const لتحديد حجم صفيف (أثناء عمل ماكرو). حتى في C99 لا يمكنك استخدام كائن const لتحديد حجم صفيف عندما تحتاج إلى صفيف VLA .

إذا كان هذا مهمًا بالنسبة لك ، فسيحدد اختيارك. في معظم الأحيان ، لن يكون أمامك خيار سوى استخدام #define في C. ولا تنسى بديلاً آخر ، ينتج ثوابت حقيقية في C - enum .

في كائنات const C ++ ثوابت true ، لذلك في C ++ دائماً الأفضل دائماً يفضل متغير static (لا حاجة لـ static صريح في C ++ بالرغم من ذلك).


كتبت برنامج اختبار سريع لإظهار فارق واحد:

#include <stdio.h>

enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};

#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32

int main(int argc, char *argv[]) {

   printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);

   return(0);
}

هذا يجمع مع هذه الأخطاء والتحذيرات:

main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
      ^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
      ^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
        ^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
        ^

لاحظ أن التعداد يعطي خطأ عندما يعطي التعريف تحذيرًا.


نظرنا إلى رمز المجمّع المُنتَج على MBF16X ... كلا المتغيرين ينتجان نفس الكود للعمليات الحسابية (ADD فوري ، على سبيل المثال).

لذا ، يفضل البحث عن const int للتحقق من النوع بينما يكون #define قديمًا. ربما هو مترجم محددة. لذلك تحقق من رمز المجمع المُنتَج.


يعتمد ذلك على ما تحتاجه لقيمة. لقد تجاهلت أنت (وكل شخص آخر حتى الآن) البديل الثالث:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

تجاهل المشكلات المتعلقة باختيار الاسم ، ثم:

  • إذا كنت بحاجة إلى تمرير مؤشر ، يجب عليك استخدام (1).
  • بما أن (2) خيار على ما يبدو ، فأنت لا تحتاج إلى تمرير المؤشرات.
  • لدى كل من (1) و (3) رمز في جدول رمز مصحح الأخطاء - يجعل التصحيح أسهل. من الأرجح أن (2) لن يكون لديك رمز ، مما يجعلك تتساءل ما هو.
  • (1) لا يمكن استخدامه كبعد للمصفوفات في النطاق العالمي ؛ كلا (2) و (3) يمكن.
  • (1) لا يمكن استخدامه كبعد للمصطلحات ثابتة في نطاق الوظيفة ؛ كلا (2) و (3) يمكن.
  • تحت C99 ، يمكن استخدام كل هذه للصفائف المحلية. من الناحية الفنية ، فإن استخدام (1) يعني استخدام VLA (مصفوفة متغيرة الطول) ، على الرغم من أن الأبعاد المشار إليها بـ "var" ستكون بالطبع ثابتة بحجم 5.
  • (1) لا يمكن استخدامها في أماكن مثل عبارات التبديل؛ كلا (2) و (3) يمكن.
  • (1) لا يمكن استخدامها لتهيئة المتغيرات الثابتة ؛ كلا (2) و (3) يمكن.
  • (2) يمكن تغيير التعليمات البرمجية التي لا تريد تغييرها لأنها تستخدم من قبل المعالج الأولي ؛ لن يكون لكل من (1) و (3) آثار جانبية غير متوقعة كهذه.
  • يمكنك اكتشاف ما إذا تم تعيين (2) في المعالج الأولي. لا (1) ولا (3) يسمح بذلك.

لذا ، في معظم السياقات ، تفضل "التعداد" على البدائل. خلاف ذلك ، من المرجح أن تكون النقاط الأولى والأخيرة هي العوامل المسيطرة - وعليك أن تفكر مليًا إذا احتجت إلى الإشباع في وقت واحد.

إذا كنت تسأل عن C ++ ، فأنت تستخدم الخيار (1) - ثابت ثابت - في كل مرة.


#define var 5 سوف يسبب لك مشكلة إذا كان لديك أشياء مثل mystruct.var .

فمثلا،

struct mystruct {
    int var;
};

#define var 5

int main() {
    struct mystruct foo;
    foo.var = 1;
    return 0;
}

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





constants