تحذير - مقارنة بين تعبيرات عدد صحيح موقّع وغير موقّع

StackOverflow https://stackoverflow.com/questions/3660901

  •  01-10-2019
  •  | 
  •  

سؤال

أنا أعمل حاليا من خلال تسارع C ++ واجهت مشكلة في التمرين 2-3.

نظرة عامة سريعة على البرنامج - يأخذ البرنامج اسمًا بشكل أساسي ، ثم يعرض تحية داخل إطار من النجمة - أي مرحبًا! محاط بتأطير من *.

التمرين - في برنامج المثال ، يستخدم المؤلفون const int لتحديد الحشوة (المساحات الفارغة) بين التحية والعلامات النجمية. ثم يطلبون من القارئ ، كجزء من التمرين ، أن يطلبوا من المستخدم إدخالها حول حجم الحشوة الكبيرة.

كل هذا يبدو سهلاً بما فيه الكفاية ، وأمضي قدماً أسأل المستخدم عن أعداد صحيحة (int) وتخزينها وتغيير البرنامج لاستخدام هذه الأعداد الصحيحة ، وإزالة تلك التي يستخدمها المؤلف ، عند التجميع على الرغم من أنني أحصل على التحذير التالي ؛

تمرين 2-3.CPP: 46: تحذير: مقارنة بين تعبيرات عدد صحيح موقّع وغير موقّع

بعد بعض الأبحاث ، يبدو أن هذا الرمز يحاول مقارنة أحد الأعداد الصحيحة المذكورة أعلاه (int) إلى string::size_type, ، وهو ما يرام. لكنني كنت أتساءل - هل هذا يعني أنني يجب أن أغير أحد الأعداد الصحيحة إلى unsigned int؟ هل من المهم أن أذكر صراحة ما إذا كانت الأعداد الصحيحة موقعة أم غير موقعة؟

 cout << "Please enter the size of the frame between top and bottom you would like ";
 int padtopbottom;
 cin >> padtopbottom;

 cout << "Please enter size of the frame from each side you would like: ";
 unsigned int padsides; 
 cin >> padsides;

 string::size_type c = 0; // definition of c in the program
 if (r == padtopbottom + 1 && c == padsides + 1) { // where the error occurs

أعلاه هي أجزاء الكود ذات الصلة ، c من النوع string::size_type لأننا لا نعرف المدة التي قد تستغرقها التحية - ولكن لماذا أحصل على هذه المشكلة الآن ، عندما لم يحصل رمز المؤلف على المشكلة عند الاستخدام const int؟ بالإضافة إلى ذلك - لأي شخص قد يكون قد أكمل تسارع C ++ - هل سيتم شرح هذا لاحقًا في الكتاب؟

أنا على Linux Mint باستخدام G ++ عبر Geany ، إذا كان ذلك يساعد أو يحدث فرقًا (كما قرأت أنه يمكن عند تحديد ماذا string::size_type هو).

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

المحلول

عادة ما يكون من الجيد إعلان المتغيرات unsigned أو size_t إذا تم مقارنتها بالأحجام ، لتجنب هذه المشكلة. كلما كان ذلك ممكنًا ، استخدم النوع الدقيق الذي ستقارنه (على سبيل المثال ، استخدمه std::string::size_type عند المقارنة مع أ std::stringطول).

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

unsigned u = GetSomeUnsignedValue();
int i = GetSomeSignedValue();

if (i >= 0)
{
    // i is nonnegative, so it is safe to cast to unsigned value
    if ((unsigned)i >= u)
        iIsGreaterThanOrEqualToU();
    else
        iIsLessThanU();
}
else
{
    iIsNegative();
}

نصائح أخرى

واجهت نفس المشكلة بالضبط بالأمس في العمل من خلال المشكلة 2-3 في تسارع C ++. المفتاح هو تغيير جميع المتغيرات التي ستقارنها (باستخدام مشغلي Boolean) لأنواع متوافقة. في هذه الحالة ، هذا يعني string::size_type (أو unsigned int, ، ولكن نظرًا لأن هذا المثال يستخدم السابق ، فسألتزم بذلك على الرغم من أن الاثنين متوافقان تقنيًا).

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

ما يجعل هذا المثال أكثر تعقيدًا هو أن متغيرات الحشو المختلفة (الجوانب و padtopbottom) ، وكذلك جميع العدادات ، يجب ايضا يتم تغييرها إلى string::size_type.

عند الوصول إلى مثالك ، فإن الرمز الذي نشرته سينتهي به الأمر مثل هذا:

cout << "Please enter the size of the frame between top and bottom";
string::size_type padtopbottom;
cin >> padtopbottom;

cout << "Please enter size of the frame from each side you would like: ";
string::size_type padsides; 
cin >> padsides;

string::size_type c = 0; // definition of c in the program

if (r == padtopbottom + 1 && c == padsides + 1) { // where the error no longer occurs

لاحظ أنه في الشرطية السابقة ، ستحصل على الخطأ إذا لم تقم بتهيئة المتغير R كـ string::size_type في ال for عقدة. لذلك تحتاج إلى تهيئة الحلقة باستخدام شيء مثل:

    for (string::size_type r=0; r!=rows; ++r)   //If r and rows are string::size_type, no error!

لذلك ، في الأساس ، بمجرد تقديم ملف string::size_type متغير في المزيج ، في أي وقت تريد فيه إجراء عملية منطقية على هذا العنصر ، يجب أن يكون لجميع المعاملات نوع متوافق لتجميعها دون تحذيرات.

الفرق المهم بين INTs الموقعة وغير الموقعة هو تفسير البت الأخير. تمثل الشيء الأخير في الأنواع الموقعة علامة الرقم ، بمعنى: على سبيل المثال:

0001 هو واحد موقّع وغير موقّع 1001 موقّع و 9 غير موقّع

(لقد تجنبت القضية المكملة بأكملها لوضوح التفسير! هذا ليس بالضبط كيف يتم تمثيل INT في الذاكرة!)

يمكنك أن تتخيل أنه يحدث فرقًا لمعرفة ما إذا كنت تقارن بـ -1 أو مع +9. في كثير من الحالات ، يكون المبرمجون كسولًا جدًا لإعلان حساب INTs غير موقعة (انتفاخ رأس الحلقة Fi) عادة ما لا يمثل مشكلة لأنه مع وجود ints يجب أن تعول على 2^31 حتى تعضك بترك. لهذا السبب هو مجرد تحذير. لأننا كسولون جدًا في كتابة "غير موقعة" بدلاً من "int".

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

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

او استعمل مكتبة الرأس هذه واكتب:

// |notEqaul|less|lessEqual|greater|greaterEqual
if(sweet::equal(valueA,valueB))

ولا تهتم بأحجام موقعة/غير موقعة أو مختلفة

المشكلة الأساسية هي أن الأجهزة الأساسية ، وحدة المعالجة المركزية ، لديها فقط تعليمات لمقارنة قيمتين موقّعتين أو مقارنة قيمتين غير موقّعتين. إذا قمت بتمرير تعليمات المقارنة غير الموقعة ، فسيتم تعاملها مع القيمة السالبة ، فستعاملها كرقم إيجابي كبير. لذلك ، -1 ، يصبح نمط البتات مع جميع البتات (مكملة Twos) ، الحد الأقصى لقيمة غير موقعة لنفس العدد من البتات.

8 بتات: -1 تم توقيعه هو نفس البتات مثل 255 بتات غير موقعة: -1 موقعة هي نفس البتات مثل 65535 غير موقعة وما إلى ذلك.

لذا ، إذا كان لديك الرمز التالي:

int fd;
fd = open( .... );

int cnt;
SomeType buf;

cnt = read( fd, &buf, sizeof(buf) );

if( cnt < sizeof(buf) ) {
    perror("read error");
}

ستجد أنه في حالة فشل استدعاء القراءة (2) بسبب أن يصبح واصف الملف غير صالح (أو خطأ آخر) ، فسيتم ضبط CNT على -1. عند مقارنة بحجم (BUF) ، وهي قيمة غير موقعة ، ستكون عبارة IF () خاطئة لأن 0xffffffff لا تقل عن حجم () بعض (معقول ، لا يتم إعداده ليكون بحجم أقصى).

وبالتالي ، عليك أن تكتب ما سبق إذا ، لإزالة التحذير الموقّع/غير الموقّع على النحو التالي:

if( cnt < 0 || (size_t)cnt < sizeof(buf) ) {
    perror("read error");
}

هذا فقط يتحدث بصوت عال عن المشاكل.

1.  Introduction of size_t and other datatypes was crafted to mostly work, 
    not engineered, with language changes, to be explicitly robust and 
    fool proof.
2.  Overall, C/C++ data types should just be signed, as Java correctly
    implemented.

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

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