سؤال

لماذا أحصل على -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 بت).

يكون الفائض هو عندما تزداد القيمة إلى النقطة التي لن تتناسب فيها مع التخزين المخصص. في هذه الحالة ، القيمة هي مخصص على ما يرام ، ولكن ليس تماما استرجاع.

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