تحذير - مقارنة بين تعبيرات عدد صحيح موقّع وغير موقّع
-
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 ++ بشكل جيد ، ويجب أن تكون صريحًا للغاية بشأن كل شيء حول أنواعه كما هو مذكور في العديد من الإجابات الأخرى هنا.