سلوك غير متوقع عند طباعة البايت البايت البايت 4 بايت

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

سؤال

لدي رمز العينة هذا لتحويل الأعداد الصحيحة 32 بت إلى عناوين IP.


#include <stdio.h>
int main()
{
 unsigned int c ;
 unsigned char* cptr  = (unsigned char*)&c ;
 while(1)
 {
  scanf("%d",&c) ;
  printf("Integer value: %u\n",c);
  printf("%u.%u.%u.%u \n",*cptr, *(cptr+1), *(cptr+2), *(cptr+3) );
 }
}

يعطي هذا الرمز إخراج غير صحيح للإدخال 2249459722 وبعد ولكن عندما استبدل

scanf("%d",&c) ;
بواسطة
scanf("%u",&c) ;
الناتج يخرج ليكون صحيحا.

ملاحظة : وأنا أعلم عن inet_ntop و inet_pton.
أتوقع إجابات بخلاف تشير تلك.

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

المحلول

أنت ترميز "سوى'(إصدار عدد من الأخطاء التي ستؤذيك عاجلا أم آجلا - في الغالب عاجلا). أولا، تفترض أن عدد صحيح هو من نيس نيس الصحيح. على بعض الآلات، سوف تكون خاطئا - إما على آلات إنتل أو على آلات PowerPC أو SPARC.

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


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

#include <stdio.h>
int main(void)
{
    unsigned int c = 2249459722;
    unsigned char* cptr  = (unsigned char*)&c;
    printf("Integer value:  %10u\n", c);
    printf("Integer value:  0x%08X\n", c);
    printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
    return(0);
}

عند تجميعها على جهاز Mac (Intel، Little-endian)، فإن الإخراج هو:

Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 

عند تجميعها على شمسي (SPARC، Big-endian)، فإن الإخراج هو:

Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 134.20.8.10 

(باستخدام GCC 4.4.2 على SPARC، أحصل على تحذير:

xx.c:4: warning: this decimal constant is unsigned only in ISO C90

باستخدام GCC 4.2.1 على جهاز Mac - مع الكثير من التحذيرات ممكن (gcc -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Werror) - أنا لا أحصل على هذا التحذير، وهو مثير للاهتمام.) يمكنني إزالة ذلك عن طريق إضافة U لاحقة إلى ثابت عدد صحيح.


يتم توضيح طريقة أخرى للنظر في المشكلات مع التعليمات البرمجية التالية وإعدادات مترجم Fussion الموضحة أعلاه:

#include <stdio.h>

static void print_value(unsigned int c)
{
    unsigned char* cptr  = (unsigned char*)&c;
    printf("Integer value:  %10u\n", c);
    printf("Integer value:  0x%08X\n", c);
    printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}

int main(void)
{
    const char str[] = "2249459722";
    unsigned int c = 2249459722;

    printf("Direct operations:\n");
    print_value(c);

    printf("Indirect operations:\n");
    if (sscanf("2249559722", "%d", &c) != 0)
        printf("Conversion failed for %s\n", str);
    else
        print_value(c);
    return(0);
}

هذا فشل في ترجمة (بسبب -Werror الإعداد) مع الرسالة:

cc1: warnings being treated as errors
xx.c: In function ‘main’:
xx.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’

مسح ال -Werror الإعداد وتجميعه، ولكن بعد ذلك يظهر المشكلة التالية التي لديك - واحدة من عدم التحقق من مؤشرات الخطأ من الوظائف التي يمكن أن تفشل:

Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations:
Conversion failed for 2249459722

أساسا، sscanf() تقارير الوظيفة التي فشلت في تحويل السلسلة إلى عدد صحيح موقد (نظرا لأن القيمة كبيرة جدا لتناسب - انظر التحذير من دول مجلس التعاون الخليجي 4.4.2)، لكن رمزك لم يتحقق من الخطأ مرة أخرى sscanf(), ، لذلك كنت تستخدم أي قيمة حدثت أن تترك في c في الموعد.

لذلك، هناك مشاكل متعددة مع الكود الخاص بك:

  • يفترض بنية معينة (إينيان قليلا بدلا من الاعتراف بأن الإناود الكبير موجود أيضا).
  • لا تجميعها نظيفة عند استخدام مترجم مع الكثير من التحذيرات ممكن - لسبب وجيه.
  • لا تحقق من أن الوظائف التي يمكن أن تفشل بالفعل.

تعليق alok

نعم، اختبار sscanf() خطأ. لهذا السبب لديك مراجعات رمز، ولهذا السبب يساعد في نشر الرمز الذي تقوم باختباره.

أنا الآن في حيرة قليلا - الحصول على سلوك متسق لا أستطيع أن أشرح على الفور. مع التنقيح الواضح (اختبار على مجموعة منجمات Macos X 10.6.2، GCC 4.2.1، 32 بت و 64 بت)، أحصل على إجابة واحدة غير عاقلة للغاية. عندما أعد كتابة أكثر وحدات، أحصل على إجابة عاقلة.

+ cat yy.c
#include <stdio.h>

static void print_value(unsigned int c)
{
    unsigned char* cptr  = (unsigned char*)&c;
    printf("Integer value:  %10u\n", c);
    printf("Integer value:  0x%08X\n", c);
    printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}

int main(void)
{
    const char str[] = "2249459722";
    unsigned int c = 2249459722;

    printf("Direct operations:\n");
    print_value(c);

    printf("Indirect operations:\n");
    if (sscanf("2249559722", "%d", &c) != 1)
        printf("Conversion failed for %s\n", str);
    else
        print_value(c);
    return(0);
}


+ gcc -o yy.32 -m32 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes yy.c
yy.c: In function ‘main’:
yy.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’


+ ./yy.32
Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations:
Integer value:  2249559722
Integer value:  0x86158EAA
Dotted decimal: 170.142.21.134 

ليس لدي تفسير جيد للقيمة 170.142.11.134؛ لكنه متسق على جهازي، في الوقت الحالي.

+ gcc -o yy.64 -m64 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes yy.c
yy.c: In function ‘main’:
yy.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’


+ ./yy.64
Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations:
Integer value:  2249559722
Integer value:  0x86158EAA
Dotted decimal: 170.142.21.134 

نفس القيمة - حتى في 64 بت بدلا من 32 بت. ربما المشكلة هي أنني أحاول شرح سلوك غير محدد، وهو أكثر أو أقل بحكم التعريف غير قابل للتفسير (لا يمكن تفسيره).

+ cat xx.c
#include <stdio.h>

static void print_value(unsigned int c)
{
    unsigned char* cptr  = (unsigned char*)&c;
    printf("Integer value:  %10u\n", c);
    printf("Integer value:  0x%08X\n", c);
    printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}

static void scan_value(const char *str, const char *fmt, const char *tag)
{
    unsigned int c;
    printf("Indirect operations (%s):\n", tag);
    fmt = "%d";
    if (sscanf(str, fmt, &c) != 1)
        printf("Conversion failed for %s (format %s \"%s\")\n", str, tag, fmt);
    else
        print_value(c);
}

int main(void)
{
    const char str[] = "2249459722";
    unsigned int c = 2249459722U;

    printf("Direct operations:\n");
    print_value(c);
    scan_value(str, "%d", "signed");
    scan_value(str, "%u", "unsigned");

    return(0);
}

باستخدام وسيطة الوظيفة مثل هذا يعني أن دول مجلس التعاون الخليجي لا يمكن أن تكتشف تنسيق الزجاجة بعد الآن.

+ gcc -o xx.32 -m32 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes xx.c


+ ./xx.32
Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations (signed):
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations (unsigned):
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 

النتائج متوافقة هنا.

+ gcc -o xx.64 -m64 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes xx.c


+ ./xx.64
Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations (signed):
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations (unsigned):
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134

وهذه هي نفس الحالة 32 بت. أنا رسميا رسميا. تظل الملاحظات الرئيسية دقيقة - كن حذرا، تحذيرات مترجم مفيد (وإلغاء تحذيرات المترجم)، ولا تفترض أن "جميع العالم يعمل على رقائق إنتل" (اعتاد أن يكون "لا تفترض أن كل العالم هو VAX "، مرة واحدة منذ وقت طويل!).

نصائح أخرى

٪ D هو للأعداد الصحيحة الموقعة

٪ يو للأعداد الصحيحة غير الموقعة

يحرر:

يرجى تعديل البرنامج الخاص بك على النحو التالي لمعرفة كيفية تفسير إدخالك حقا:

#include <stdio.h>
int main()
{
 unsigned int c ; 
 unsigned char* cptr  = (unsigned char*)&c ;
 while(1)
 {
  scanf("%d",&c) ;
  printf("Signed value: %d\n",c);
  printf("Unsigned value: %u\n",c);
  printf("%u.%u.%u.%u \n",*cptr, *(cptr+1), *(cptr+2), *(cptr+3) );
 }
}

ما يحدث عند توفير رقم أكبر من INT_MAX هو الجزء الأيمن هو 1. هذا يشير إلى أنه عدد صحيح موقع به قيمة سالبة. ثم يفسر الرقم كما هو متمم ثنائي

للإجابة على سؤالك الرئيسي:

scanf("%d", &c);

scanf()السلوك غير محدد عندما لا يمكن تمثيل المدخلات التي يتم تحويلها بنوع البيانات. 2249459722 على جهازك لا يصلح في int, ، وبالتالي scanf() يمكن أن تفعل أي شيء، بما في ذلك تخزين القمامة في c.

في ج، int اكتب نوع أن تكون قادرا على تخزين القيم في النطاق -32767 ل +32767. وبعد أ unsigned int يتم ضمان القيم بين 0 و 65535. وبعد لذلك، على هذا النحو، 2249459722 لا تحتاج إلى ملاءمة حتى unsigned int. unsigned long, ومع ذلك، يمكن تخزين القيم حتى 4294967295 (232-1)، لذلك يجب عليك استخدام unsigned long:

#include <stdio.h>
int main()
{
    unsigned long c ;
    unsigned char *cptr  = (unsigned char*)&c ;
    while(1)
    {
        if (scanf("%lu", &c) != 1) {
            fprintf(stderr, "error in scanf\n");
            return 0;
        }
        printf("Input value: %lu\n", c);
        printf("%u.%u.%u.%u\n", cptr[0], cptr[1], cptr[2], cptr[3]);
    }
    return 0;
}

إذا كان لديك مترجم C99، يمكنك #include <inttypes.h> ثم استخدم uint32_t بدلا من unsigned long. وبعد ال scanf() الدعوة تصبح scanf("%" SCNu32, &c);

الطريقة الصحيحة النخاعية الصحيحة لكتابة هذا هو

printf("Dotted decimal: %u.%u.%u.%u \n", (c >> 24) & 0xff, (c >> 16) & 0xff, (c >> 8) & 0xff, (c >> 0) & 0xff);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top