الشبكة العربية لمطوري الألعاب

محترف مشرف عبد اللطيف حاجي علي مشاركة 11

حسناً قبل أن أنتقل إلى مناقشة مشاركة الأخ أنس، إليكم ملاحظاتي على مشاركة وسام:
1. +نقطة لصحة التنفيذ (مع الصفر)
2. +نقطة لتنظيم الكود بطريقة واضحة
3. +نقطة لتقليل عدد مرات الوصول إلى الذاكرة
4. +نقطة لسرعة التنفيذ وقلة عدد التعليمات
5. -نقطة لأستعمال أرقام سحرية جعلت الكود أقل وضوحاً (مثل الرقم 268435456 )
6. -نقطة لاستعمال التقسيم والضرب بدل الإزاحة
7. -نقطة لاستعمال مصفوفة من المحارف (char[]) بدل من (char*). هذا تفضيل شخصي
8. -نقطة لعدم وجود تعليقات
9. -نقطة لصعوبة تعديل الكود ليأخذ متغيراً من اي حجم
10. -نقطة للتسمية المبهمة itoah (رأي شخصي)
11. -نقطة لكتابة كود غير قياسي (main لا يعيد void) ☺

إذا لم يكن لدى أحدكم ما يدلي به فلننتقل إلى المشاركة التالية.

عبد اللطيف حاجي علي
مبرمج
In|Framez

محترف  انس مشاركة 12

نفس الشيئ بالنسبة للنقاط التالية  :1-2-3-4-8

محترف مشرف عبد اللطيف حاجي علي مشاركة 13

مشاركة أنس:
+نقطة لصحة التنفيذ (مع الصفر)
+نقطة لتنظيم الكود بطريقة واضحة
+نقطة لتقليل عدد مرات الوصول إلى الذاكرة
+نقطة لوجود تعليقات
+نقطة لسهولة تعديل الكود ليأخذ متغيراً من اي حجم
+نقطة لأن الخوارزمية هي نفسها التي توصل إليها وسام (تقريباً)
-نقطة لحجز مصفوفة المحارف داخل الإجراء مما يؤثر على الأداء ويسبب مشاكل في تحرير الذاكرة. هذا موجود فعلاً في البرنامج حيث هناك تسريب في الذاكرة
-نقطتين بسبب الكود التالي:
static char  Hexa_table[16] = "0123456789ABCDEF" ;

ذلك لأن مصفوفة المحارف هذه تحوي حقيقة 17 عنصر بالإضافة إلى الناهي الصفري
ولأن هذه المصفوفة يجب أن تكون const. (تفضبل شخصي)


-نقطة لاستخدام القسمة والضرب والباقي بدل العمليات المنطقية من إزاحة و AND
-نقطة لأن فشل حجز المصفوفة يؤدي إلى خطأ منطقي جسيم  في الزمن الحقيقي
-نقطة لاستخدام إجراء memset الجاهز. مع العلم أنه لا داعي له.
-نقطة لأن الإجراء يقوم بالطباعة على المدخل القياسي. أمر ليس من المفروض أن يفعله إجراء كهذا.

-نقطة لبطئ الأداء بسبب استخدام  أمور لا داعي لها البتة (memset, malloc, printf)
-نقطة بسبب الكود التالي:

if ( (number_string = malloc( (string_size) * sizeof(char) ) ) == NULL ){
ذلك أن العبارة '== NULL' لا داعي لها ويمكن الاستعاضة غنها بـ !. ولأن هناك مساواة داخل شرط الـ if وهو ما لا أفضله في أغلب الحالات وأخيراً بسبب العبارة sizeof(char) والمعرفة دوماً على أنها 1 فلا داعي لها.

أرجو أن تكون النقاط واضحة وأتمنى من البقية أن يبدوا برأيهم.

عبد اللطيف حاجي علي
مبرمج
In|Framez

محترف  انس مشاركة 14

لم افهم هذه النقاط هلا اعدت شرحها لي ؟

بتاريخ 23 سبتمبر 2009 02:35 م، قطب عبد اللطيف حاجي علي حاجبيه بشدة وهو يقول:

-نقطة لأن فشل حجز المصفوفة يؤدي إلى خطأ منطقي جسيم  في الزمن الحقيقي



وفي 23 سبتمبر 2009 02:35 م، أعرب عبد اللطيف حاجي علي عن رأيه بالموقف كالآتي:

-نقطة لأن الإجراء يقوم بالطباعة على المدخل القياسي. أمر ليس من المفروض أن يفعله إجراء كهذا.

محترف مشرف عبد اللطيف حاجي علي مشاركة 15

في 23 أيلول 2009 09:26 م، غمغم انس باستغراب قائلاً:

لم افهم هذه النقاط هلا اعدت شرحها لي ؟
-نقطة لأن فشل حجز المصفوفة يؤدي إلى خطأ منطقي جسيم  في الزمن الحقيقي
لديك الكود الآتي:


if ( (number_string = malloc( (string_size) * sizeof(char) ) ) == NULL ){

        printf("Error : can't allocate memorie\n");
        }
    else memset( number_string,'\0',string_size  ); // clean memorie


لو أن malloc أعادت القيمة NULL فإنه سيتم طباعة رسالة الخطأ إلا أن تنفيذ الإجراء سيستمر مع القيمة NULL في المتغير number_string مما سيؤدي على الكتابة في العنوان 0 عند تنفيذ هذا السطر لاحقاً
 number_string[i] = Hexa_table[rest] ;

هذا سيؤدي إلى رمي استثناء من نوع access violation

بتاريخ 23 أيلول 2009 09:26 م، قطب انس حاجبيه بشدة وهو يقول:

-نقطة لأن الإجراء يقوم بالطباعة على المدخل القياسي. أمر ليس من المفروض أن يفعله إجراء كهذا.
هذا الإجراء يقوم بأخذ رقم ويعيد نصاً يحوي التمثيل الست عشري للرقم. تخيل ماذا سيكون شعور المستخدم إذا أراد تنفيذ شيء كهذا:

printf("My number is: ");
printf("%s", Dicimal_to_hex(0x452));

هذا الكود سيعطي الخرج التالي:
My number is: rest 2
rest 5
rest 4
rest 0
0452

أو في حال حدوث خطأ (إذا أهملنا الاستثناء الذي سيلي ذلك):
My number is: Error : can't allocate memorie

أستطيع أن أقول لك إن المستخدم لن يكون سعيداً.
بشكل عام ليس من حق الإجراء الكتابة على الخرج القياسي كون هذا الإجراء هو إجراء في مكتبة وسوف يستخدم في برامج عديدة لاحقاً وليس برنامجاً مستقلاً بحد ذاته.


ممم... يبدو أنني نسيت نقطتين:
-نقطة لأن حجز الذاكرة للمصفوفة في number_string لا يأخذ بعين الاعتبار الناهي الصفري
-نقطة لأن الرقم 0xA يعيد 0A بدلاً من A على سبيل المثال

عبد اللطيف حاجي علي
مبرمج
In|Framez

محترف  انس مشاركة 16

وفي 23 سبتمبر 2009 10:34 م، أعرب عبد اللطيف حاجي علي عن رأيه بالموقف كالآتي:

هذا الإجراء يقوم بأخذ رقم ويعيد نصاً يحوي التمثيل الست عشري للرقم. تخيل ماذا سيكون شعور المستخدم إذا أراد تنفيذ شيء كهذا:
شكرا جزيلا على التنبيه، بالنسبة للكتابة على المخرج القياسي، فهي سهوة لانني استعملتها لتجريب الكود ☺ .

في 23 سبتمبر 2009 10:34 م، قال عبد اللطيف حاجي علي بهدوء وتؤدة:

-نقطة لأن الرقم 0xA يعيد 0A بدلاً من A على سبيل المثال
شكرا جزيلا على التنبيه، استطعت الان اكتشاف المشكلة.
    //i    = string_size ; wrong
    i    = string_size-1 ;



   // convert Decimal into Hexadecimal
    while( i >= 0 ) {

       rest    = number%16  ;
       number /= 16         ;
       number_string[i] = Hexa_table[rest] ;
       i--     ;

        }

لي سؤال لو تفضلت بشرحه من فضلك. اذا اردت ان اكتب اجراء يرسل عنوان سلسلة محارف فهل من حل لتحرير الذاكرة اذا لم يتم استقبال هذا العنوان من مؤشر ما ؟
مثال :

printf("%s \n", Dicimal_to_hex (1254); // تحرير الذاكرة
ام ان الحل الوحيد هو تمرير مؤشر لدالة و هي تقوم بالعمل الازم من تحقق من حجم السلسلة الخ ؟

شكرا جزيلا على الملاحظات القيمة.

سلام

محترف مشرف عبد اللطيف حاجي علي مشاركة 17

برأيي الشخصي الأفضل هو أن يقوم هذا الإجراء بشيء واحد وواحد فقط: تحويل الأرقام إلى تمثيلها الست عشري.
دون حجز ذاكرة ودون تصفيرها أو غير ذلك.
يتم ذلك بأن تجعل المستخدم يعطيك العنوان الذي يريدك أن تكتب فيه ربما مع الحجم الأقصى الذي يسمحه لك. هذا العنوان قد يكون مجوزاً بشكل ديناميكي أو ستاتيكي. المهم أن المستخدم هو المسؤول عن إدارة الذاكرة. كمثال انظر إلى مشاركتي ومشاركة وسام.

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


في 24 أيلول 2009 02:53 ص، قال انس بهدوء وتؤدة:

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

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

عبد اللطيف حاجي علي
مبرمج
In|Framez

خبير  أحمد عبد الغني مشاركة 18

ما شاء الله مسابقة جميلة.

وفي 04/شوال/1430 09:35 ص، قال عبد اللطيف حاجي علي متحمساً:

وأخيراً بسبب العبارة sizeof(char) والمعرفة دوماً على أنها 1 فلا داعي لها.

اختلف معاك يا عبد اللطيف 😒
استخدام sizeof هو مثالية حميدة برأيي ولن يخسر أي شيء باستخدامه لها. فالتعليمة في النهاية ستتحول إلى الرقم الصحيح أثناء بناء البرنامج.
 
سؤال: هل هناك منصات تتعامل مع char على أنه أكبر من بايت؟
 
أوافقك على بقية النقاط.
 
بالنسبة لمشاركة وسام، ففيها فكرة جميلة جداً لم تكن لتخطر على بالي. فوسام يتعامل مع الرقم الست عشري بنفس منطلق الرقم العشري أو غيره (حالة عامة). فرياضياً، الرقم  142 هو رقم عشري. لو قسمنا على 10 لأزحنا الأرقام خانة واحدة إلى اليمين فيصبح الرقم 14.2 أو 14 بعد تجاهل الفاصلة.
لو ضربنا بـ 10 لأصبح الرقم 1420 أي تمت إزاحته لليسار خانة.
 
في حالة الأعداد الثنائية فإن نفس المفهوم يعمل: 1001 اضربها بـ 10(ثنائي) فتصبح 10010  أو اقسمها على 10(ثنائي) فتصبح 100.1 = 100 بعد الجبر.
 
وأخيراً للأعداد الست عشرية: AB54 ضرب 10(ست عشري) تساوي AB540 و تقسيم 10(ست عشري) تصبح AB5.4 وبعد الجبر AB5.
 
😄
 
أنا أعطي نقطة لهذه الفكرة حتى ولو كان يستخدم القسمة والضرب في الحسابات ☺  (رأيي الشخصي)

اللهم انصر أهلنا في فلسطين وآجرنا أن نكون عوناً لهم

محترف  انس مشاركة 19

السلام عليكم.

حسنا هاهو الكود معدل.



char* Dicimal_to_hex ( unsigned int number , char* user_string ) {

    unsigned int temp_number = number ;
    int string_size = 0      ;
    int rest        = 0      ;
    int i           = 0      ;

    static const char  Hexa_table[17] = "0123456789ABCDEF" ;

    // exception with 0
    if ( number == 0 ) {
        user_string[0]= '0';
        user_string[1] = '\0';
        return user_string;

       }

    // get the string size
    while( temp_number > 0 ) {

       rest = temp_number%16;
       temp_number >>= 4;
       string_size++   ;
        }

    // be sure to put the '\0' at the end of the string
    user_string[ string_size ] = '\0';

    // initialisation of the variable
    rest = 0;
    i    = string_size-1 ;

   // convert Decimal into Hexadecimal
    while( i >= 0 ) {

       rest     = number%16  ;
       number >>= 4          ;
       user_string[i] = Hexa_table[rest] ;
       i--     ;

        }

    return user_string;
    }
المشكلة هي انني حذفت التعليمات التي تتحقق من حجم سلسلة المحارف.اذن فل يتحمل المستخدم مسؤلية ذلك ☺ .
اظن ان الكود الان احسن اليس كذلك ؟



Write Code Here

محترف مشرف عبد اللطيف حاجي علي مشاركة 20

أما في 24 أيلول 2009 04:26 م، فقد تنهد أحمد عبد الغني بارتياح وهو يرد:

سؤال: هل هناك منصات تتعامل مع char على أنه أكبر من بايت؟
نعم لكن حتى في هذه الحالات فإن sizeof ستعيد 1. حيث أن sizeof لا تعيد الحجم بالبايتات وإنما الحجم بالـ characters

في 24 أيلول 2009 04:26 م، غمغم أحمد عبد الغني باستغراب قائلاً:

استخدام sizeof هو مثالية حميدة برأيي ولن يخسر أي شيء باستخدامه لها. فالتعليمة في النهاية ستتحول إلى الرقم الصحيح أثناء بناء البرنامج.
هذا كان رأيي منذ زمن أيضاً. لكني اقتنعت بعد ذلك أن هذا يوازي أن يقوم أحدنا بكتابة شيء مثل:

int i = 10*1;

ثم إن كان هذا صحيحاً وكون sizeof() تعيد الحجم بالـ character وليس بالبايتات فلم لا نقوم بالتالي:
size_t sMySize = sizeof(int) * sizeof(char);

هذا هو النقاش بشكل عام الذي أقنعني شخصياً بعدم جدوى استخدام العبارة sizeof(char). لاحظ أن العبارة التالية لا زالت حميدة برأيي حتى لو كان النوع char:
char buffer[50];
size_t mySize = sizeof(buffer) / sizeof(buffer[0]);

ذلك أن تعديل نوع buffer إلى int مثلاً لن يضطرنا إلى تعديل الكود بأكمله.


بتاريخ 24 أيلول 2009 04:26 م، قطب أحمد عبد الغني حاجبيه بشدة وهو يقول:

بالنسبة لمشاركة وسام، ففيها فكرة جميلة جداً لم تكن لتخطر على بالي. فوسام يتعامل مع الرقم الست عشري بنفس منطلق الرقم العشري أو غيره (حالة عامة). فرياضياً، الرقم  142 هو رقم عشري. لو قسمنا على 10 لأزحنا الأرقام خانة واحدة إلى اليمين فيصبح الرقم 14.2 أو 14 بعد تجاهل الفاصلة.لو ضربنا بـ 10 لأصبح الرقم 1420 أي تمت إزاحته لليسار خانة.
صحيح.
+نقطة لسهولة تعديل الكود ليأخذ أي أساس (وضع 16 في متغير ثابت سيسهل العملية بشكل أكبر) 😄



ملاحظة عن المصطلحات قد لا تفيد أحداً: لاحظ أننا لا نقوم بتجاهل الفاصلة، وإنما عملية التقسيم أصلاً لا تعطي فاصلة كونها تعمل في مجموعة الأرقام صحيحة.

عبد اللطيف حاجي علي
مبرمج
In|Framez