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
قديمًا. ربما هو مترجم محددة. لذلك تحقق من رمز المجمع المُنتَج.
يعتمد ذلك على ما تحتاجه لقيمة. لقد تجاهلت أنت (وكل شخص آخر حتى الآن) البديل الثالث:
-
static const int var = 5;
-
#define var 5
-
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;
}
سوف يحل محلها المعالج الأولي ولن يتم تجميع الشفرة. ولهذا السبب ، يقترح نمط الترميز التقليدي جميع الأحرف الكبيرة التي تستخدم أحرف ثابتة لتجنب الصراع.