والسي - هل يمكن التعليمة البرمجية الصالحة في كل من C و C++ لإنتاج سلوك مختلف عند تجميعها في كل لغة؟




تعلم لغة البرمجة c من الصفر حتى الاحتراف (11)

C90 vs. C ++ 11 ( int vs. double ):

#include <stdio.h>

int main()
{
  auto j = 1.5;
  printf("%d", (int)sizeof(j));
  return 0;
}

في C auto يعني المتغير المحلي. في C90 ، يمكنك حذف نوع المتغير أو الوظيفة. افتراضات int . في auto C ++ 11 يعني شيئًا مختلفًا تمامًا ، فإنه يخبر المترجم باستنتاج نوع المتغير من القيمة المستخدمة لتهيئة هذا المتغير.

لدى C و C ++ العديد من الاختلافات ، وليس كل C رمز صالح رمز C ++ صالح.
(تعني كلمة "صالح" رمزًا موحدًا بسلوك محدد ، أي ليس تطبيقًا محددًا / غير محدد / وما إلى ذلك).

هل هناك أي سيناريو يمكن أن ينتج فيه جزء من الكود صالح في كل من C و C ++ سلوك مختلف عند تجميعه مع مترجم قياسي في كل لغة؟

لجعلها مقارنة معقولة / مفيدة (أحاول تعلم شيء مفيد عمليا ، وليس محاولة العثور على ثغرات واضحة في السؤال) ، دعنا نفترض:

  • لا علاقة بين #ifdef __cplusplus (مما يعني عدم وجود قرصنة مع #ifdef __cplusplus ، pragmas ، إلخ.)
  • أي شيء محدد بالتطبيق هو نفسه في كلتا اللغتين (على سبيل المثال الحدود الرقمية ، إلخ.)
  • نحن نقارن الإصدارات الحديثة بشكل معقول من كل معيار (على سبيل المثال يقول ، C ++ 98 و C90 أو أحدث)
    إذا كانت الإصدارات مهمة ، فالرجاء ذكر إصدارات أي منها تنتج سلوكًا مختلفًا.

آخر sizeof فخ: التعابير منطقية.

#include <stdio.h>
int main() {
    printf("%d\n", (int)sizeof !0);
}

تساوي sizeof(int) في C ، لأن التعبير من النوع int ، ولكنه عادة 1 في C ++ (على الرغم من أنه ليس مطلوبًا أن يكون). من الناحية العملية فهي دائما مختلفة تقريبا.


بالنسبة لـ C ++ مقابل C90 ، توجد طريقة واحدة على الأقل للحصول على سلوك مختلف غير محدد للتنفيذ. لا يحتوي C90 على تعليقات سطر واحد. مع القليل من العناية ، يمكننا استخدام ذلك لإنشاء تعبير بنتائج مختلفة تمامًا في C90 و C ++.

int a = 10 //* comment */ 2 
        + 3;

في C ++ ، كل شيء من // إلى نهاية السطر هو تعليق ، لذلك يعمل هذا على النحو التالي:

int a = 10 + 3;

نظرًا لأن C90 لا يحتوي على تعليقات سطر واحد ، فقط /* comment */ هو تعليق. الأول / و 2 كلاهما جزء من التهيئة ، لذلك يخرج إلى:

int a = 10 / 2 + 3;

لذا ، سيعطي مترجم C ++ الصحيح 13 ، لكن مترجم C صحيح 8. بالطبع ، لقد اخترت أرقامًا عشوائية هنا - يمكنك استخدام أرقام أخرى كما تراه مناسبًا.


كستناء قديم يعتمد على مترجم C ، ولا يعترف بتعليقات نهاية السطر C ++ ...

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...

ما يلي ، صحيح في C و C ++ ، سيؤدي (على الأرجح) إلى قيم مختلفة في i في C و C ++:

int i = sizeof('a');

راجع حجم الحرف ('a') في C / C ++ للحصول على شرح الفرق.

واحد آخر من هذا المقال :

#include <stdio.h>

int  sz = 80;

int main(void)
{
    struct sz { char c; };

    int val = sizeof(sz);      // sizeof(int) in C,
                               // sizeof(struct sz) in C++
    printf("%d\n", val);
    return 0;
}

مثال آخر لم أره بعد ، وهذا يسلط الضوء على فرق ما قبل التشغيل:

#include <stdio.h>
int main()
{
#if true
    printf("true!\n");
#else
    printf("false!\n");
#endif
    return 0;
}

هذا يطبع "false" في C و "true" في C ++ - في C ، تقييم أي ماكرو غير معرفة إلى 0. في C ++ ، هناك استثناء 1: "true" بتقييم إلى 1.


واحد آخر مدرج من قبل C ++ Standard:

#include <stdio.h>

int x[1];
int main(void) {
    struct x { int a[2]; };
    /* size of the array in C */
    /* size of the struct in C++ */
    printf("%d\n", (int)sizeof(x)); 
}

وفقًا لمعيار C ++ 11:

ا. ينفّذ عامل الفاصلة تحويلًا من lvalue إلى rvalue في C وليس C ++:

   char arr[100];
   int s = sizeof(0, arr);       // The comma operator is used.

في C ++ ستكون قيمة هذا التعبير 100 وفي C هذا سيكون sizeof(char*) .

ب. في C ++ نوع العداد هو التعداد الخاص به. في C نوع العداد هو int.

   enum E { a, b, c };
   sizeof(a) == sizeof(int);     // In C
   sizeof(a) == sizeof(E);       // In C++

وهذا يعني أن sizeof(int) قد لا يكون مساوياً لـ sizeof(E) .

ج. في C ++ دالة معلنة بقائمة المعلمات الفارغة لا تأخذ أي حجج. في C params فارغ قائمة يعني أن عدد ونوع المعلمات الدالة غير معروف.

   int f();           // int f(void) in C++
                      // int f(*unknown*) in C

لغة برمجة C ++ (الطبعة الثالثة) تعطي ثلاثة أمثلة:

  1. sizeof ('a') ، كما ذكرAdam Rosenfield ؛

  2. // comments تُستخدم لإنشاء رمز مخفي:

    int f(int a, int b)
    {
        return a //* blah */ b
            ;
    }
    
  3. هياكل الخ إخفاء الأشياء في النطاقات خارج ، كما هو الحال في المثال الخاص بك.


#include <stdio.h>

int main(void)
{
    printf("%d\n", (int)sizeof('a'));
    return 0;
}

في C ، يطبع هذا مهما كانت قيمة sizeof(int) على النظام الحالي ، والذي عادة ما يكون 4 في معظم الأنظمة الشائعة الاستخدام اليوم.

في C ++ ، يجب أن تتم طباعة 1.


struct abort
{
    int x;
};

int main()
{
    abort();
    return 0;
}

يتم إرجاع رمز الخروج من 0 في C ++ أو 3 في C.

ربما يمكن استخدام هذه الخدعة للقيام بشيء أكثر إثارة للاهتمام ، لكني لم أستطع التفكير في طريقة جيدة لإنشاء منشئ يمكن أن يكون مستساغًا لـ C. جربت تقديم مثال ممل على نحو مماثل لمنشئ النسخة ، والذي من شأنه أن يسمح بحجة يتم تمريرها ، وإن كان ذلك بطريقة غير محمولة إلى حد ما:

struct exit
{
    int x;
};

int main()
{
    struct exit code;
    code.x=1;

    exit(code);

    return 0;
}

رفض VC ++ 2005 ترجمة أنه في وضع C ++ ، مع ذلك ، يشكو كيفية إعادة تعريف "رمز الإنهاء". (أعتقد أن هذا خطأ مترجم ، ما لم أنسى فجأة كيفية البرنامج.) خرجت مع رمز إنهاء عملية من 1 عند تجميع C على الرغم من ذلك.





c