الطباعة غير موقعة لفترة طويلة باستخدام ٪ د
-
20-09-2019 - |
سؤال
لماذا أحصل على -1 عندما أقوم بطباعة ما يلي؟
unsigned long long int largestIntegerInC = 18446744073709551615LL;
printf ("largestIntegerInC = %d\n", largestIntegerInC);
أعلم أنني يجب أن أستخدم llu
بدلاً من d
, ، ولكن لماذا أحصل على -1 بدلاً من 18446744073709551615LL؟
هل هو بسبب الفائض؟
المحلول
في C (99) ، LLONG_MAX
, ، الحد الأقصى لقيمة long long int
النوع مضمون ليكون على الأقل 9223372036854775807
. الحد الأقصى لقيمة unsigned long long int
مضمون أن يكون على الأقل 18446744073709551615
, ، وهو 264−1 (0xffffffffffffffff
).
لذلك ، يجب أن يكون التهيئة:
unsigned long long int largestIntegerInC = 18446744073709551615ULL;
(لاحظ ال ULL
.) حيث largestIntegerInC
من النوع unsigned long long int
, ، يجب عليك طباعته باستخدام محدد التنسيق الصحيح ، وهو "%llu"
:
$ cat test.c
#include <stdio.h>
int main(void)
{
unsigned long long int largestIntegerInC = 18446744073709551615ULL;
/* good */
printf("%llu\n", largestIntegerInC);
/* bad */
printf("%d\n", largestIntegerInC);
return 0;
}
$ gcc -std=c99 -pedantic test.c
test.c: In function ‘main’:
test.c:9: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘long long unsigned int’
الثاني printf()
أعلاه خطأ ، يمكنه طباعة أي شيء. انت تستخدم "%d"
, وهو ما يعني printf()
يتوقع int
, ، ولكن يحصل unsigned long long int
, ، وهو (على الأرجح) ليس بنفس حجم int
. سبب الحصول على -1
نظرًا لأن الإخراج الخاص بك يرجع إلى حظ (سيئ) ، وحقيقة أنه على جهازك ، يتم تمثيل الأرقام باستخدام تمثيل مكملين.
لمعرفة كيف يمكن أن يكون هذا سيئًا ، دعنا ندير البرنامج التالي:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(int argc, char *argv[])
{
const char *fmt;
unsigned long long int x = ULLONG_MAX;
unsigned long long int y = 42;
int i = -1;
if (argc != 2) {
fprintf(stderr, "Need format string\n");
return EXIT_FAILURE;
}
fmt = argv[1];
printf(fmt, x, y, i);
putchar('\n');
return 0;
}
على جهاز MacBook الخاص بي ، تشغيل البرنامج مع "%d %d %d"
يعطيني -1 -1 42
, وعلى جهاز Linux ، يعطيني البرنامج نفسه بنفس التنسيق -1 42 -1
. أُووبس.
في الواقع ، إذا كنت تحاول تخزين الأكبر unsigned long long int
الرقم في الخاص بك largestIntegerInC
متغير ، يجب أن تضم limits.h
والاستخدام ULLONG_MAX
. أو يجب عليك تخزين Assing -1
إلى متغيرك:
#include <limits.h>
#include <stdio.h>
int main(void)
{
unsigned long long int largestIntegerInC = ULLONG_MAX;
unsigned long long int next = -1;
if (next == largestIntegerInC) puts("OK");
return 0;
}
في البرنامج أعلاه ، كلاهما largestIntegerInC
و next
تحتوي على أكبر قيمة ممكنة unsigned long long int
يكتب.
نصائح أخرى
ذلك لأنك تمرر رقمًا مع جميع البتات المحددة إلى 1. عند تفسيرها على أنها رقم موقّع للاثنين ، يعمل على -1. في هذه الحالة ، من المحتمل أن ينظر فقط إلى 32 بت من هؤلاء البتات بدلاً من كل 64 ، لكن هذا لا يحدث أي فرق حقيقي.
في الحساب المكملة ، القيمة الموقعة -1 هي نفس القيمة غير الموقعة.
ضع في اعتبارك أنماط البت للأرقام السلبية في تكملة اثنين (أنا أستخدم أعداد صحيحة 8 بت ، لكن النمط ينطبق بغض النظر عن الحجم):
0 - 0x00
-1 - 0xFF
-2 - 0xFE
-3 - 0xFD
لذلك ، يمكنك أن ترى أن السلبي 1 يحتوي على نمط بت لجميع 1 وهو أيضًا نمط البت لأكبر قيمة غير موقعة.
لقد استخدمت تنسيقًا لرقم 32 بت موقّع ، لذلك حصلت على -1. printf()
لا يمكن معرفة داخليًا كم هو كبير الرقم الذي تم نقله فيه ، لذا فإنه يسحب فقط 32 بت من قائمة Varargs ويستخدمها كقيمة يتم طباعتها. نظرًا لأنك قدمت تنسيقًا موقّعًا ، فإنه يطبعه على هذا النحو ، و 0xffffffff هو التمثيل المكملة للثلاثين.
يمكنك (ينبغي) معرفة لماذا في التحذير المترجم. إذا لم يكن الأمر كذلك ، فحاول تعيين أعلى مستوى تحذير. مع VS حصلت على هذا التحذير: تحذير C4245: "التهيئة": التحويل من "__int64" إلى "غير موقعة __int64" ، غير متطابق/غير موقّع.
لا ، لا يوجد تدفق. هذا لأنه لا يطبخ القيمة بأكملها:
18446744073709551615 هو نفسه 0xffffffffffffffff. متى printf %d
العمليات التي تحصل على 32 بت فقط (أو 64 بت إذا كانت وحدة المعالجة المركزية 64 بت) للتحويل ، وتلك القيمة الموقعة -1.
إذا printf
كان التحويل %u
بدلاً من ذلك ، سيظهر إما 4294967295 (32 بت) أو 18446744073709551615 (64 بت).
يكون الفائض هو عندما تزداد القيمة إلى النقطة التي لن تتناسب فيها مع التخزين المخصص. في هذه الحالة ، القيمة هي مخصص على ما يرام ، ولكن ليس تماما استرجاع.