سامح أشرف
-
المساهمات
2934 -
تاريخ الانضمام
-
تاريخ آخر زيارة
-
عدد الأيام التي تصدر بها
56
إجابات الأسئلة
-
إجابة سامح أشرف سؤال في خطأ unexpected end of file while looking for precompiled header في ++C؟ كانت الإجابة المقبولة
جميع مترجمي Compilers لغة ++ C لديهم مشكلة في الأداء بالنسية لعملية تصريف ملفات التروسية headers حيث أنها عملية طويلة وبطيئة، خصوصًا ملفات التروسية الخاصة بـ Windows API حيث أنها ملفات ضخمة وتستغرق الكثير من الوقت لتصريفها إلى لغة الآلة، وبالتالي كلما حاولت أن تقوم بعمل برنامج بسيط سوف يستغرق الكثير من الوقت ليتم تصريفه إلى لغة الآلة (بسبب تصريف ملفات التروسية غير المستخدمه)، ولحل هذه المشكلة ولتوفير الوقت يتم تصريف هذه الملفات بشكل مسبق Precompild ويستعملها في كل مرة، ولإستعمال هذه الملفات نستخدم الملف stdafx.h ، ويجب أن يكون هذا الملف في أول ملف يتم إستدعائه.
لذلك يمكنك حل المشكلة من خلال إغلاق الخاصية Precompiled Header في بيئة التطوير الخاصة بك عبر الضغط على المشروع بزر الفأرة الأيمن وإختيار properties ثم من قائمة Precompiled Headers قم بإيقافها كما في الصورة التالية:
ملاحظة في الإصدارات الأحدث من Visual Studio يتم إيقاف هذه الخاصية بشكل إفتراضي.
أو يمكنك إلغاء هذه الميزة عندما تقوم بإنشاء المشروع من الأساس:
أو يمكنك فقط إستدعاء هذا الملف في بداية البرنامج الخاصة بك:
#include "stdafx.h" -
إجابة سامح أشرف سؤال في كيف يمكنني معرفة حجم كل النوع int أو أي نوع آخر في الذاكرة في ++C؟ كانت الإجابة المقبولة
حجم كل نوع يعتمد على المصرف Compiler لديك، ويمكنك معرفة حجم كل نوع من خلال إستخدام المعامل sizeof حيث يعيد هذا المعامل حجم النوع الممر له بالبايت، كالتالي:
std::cout << "int: " << sizeof(int) << " bytes\n"; // int: 4 bytes ويمكنك معرفة حجم أشهر الأنواع المستعملة في لغة ++C كالتالي:
#include <iostream> int main() { std::cout << "bool:\t\t" << sizeof(bool) << " bytes\n"; std::cout << "char:\t\t" << sizeof(char) << " bytes\n"; std::cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes\n"; std::cout << "char16_t:\t" << sizeof(char16_t) << " bytes\n"; std::cout << "char32_t:\t" << sizeof(char32_t) << " bytes\n"; std::cout << "short:\t\t" << sizeof(short) << " bytes\n"; std::cout << "int:\t\t" << sizeof(int) << " bytes\n"; std::cout << "long:\t\t" << sizeof(long) << " bytes\n"; std::cout << "long long:\t" << sizeof(long long) << " bytes\n"; std::cout << "float:\t\t" << sizeof(float) << " bytes\n"; std::cout << "double:\t\t" << sizeof(double) << " bytes\n"; std::cout << "long double:\t" << sizeof(long double) << " bytes\n"; return 0; } وستكون النتيجة كالتالي:
bool: 1 bytes char: 1 bytes wchar_t: 2 bytes char16_t: 2 bytes char32_t: 4 bytes short: 2 bytes int: 4 bytes long: 4 bytes long long: 8 bytes float: 4 bytes double: 8 bytes long double: 8 bytes قد تجد أن الأرقام السابقة مختلفة لديك، حيث يقوم المصرّّف نفسه بتحديد حجم كل نوع، لذلك يختلف الأمر من مصرَّف لآخر.
-
إجابة سامح أشرف سؤال في هل يمكنني العمل في أحد الشركات قبل الإنتهاء من دورة PHP بالكامل؟ كانت الإجابة المقبولة
أغلب الشركات تشترط أن يكون لدى المبرمج خبرة عدة عام أو عامين أو أكثر حسب الشركة، وبعض هذه الشركات قد لا تطلب أي خبرة سابقة (يسمى تدريب في بعض الأحيان ويكون بمرتب أيضًا)، لذلك قد تجد صعوبة في العمل في أحد الشركات خصوصًا إن لم يكن لديك مشاريع قد قمت بها بنفسك، لكن ليس من المستحيل أن تعمل في أحد الشركات، وهذا على عكس العمل الحر Freelancing بالطبع، حيث لا تحتاج إلى أي خبرة أو مشاريع سابقة لتعمل في أحد مواقع العمل الحر مثل مستقل (بالتأكيد وجود مشاريع في معرض أعمال سيزيد من فرصة الحصول على عمل أيضًَا).
لاحظ أيضًا أنك إن لم تنهي تعلم باقي الدورة وبدأت تبحث عن عمل قبل أن تتعلم باقي أساسيات Laravel من خلال إكمال باقي مشاريع الدورة، فقد تعمل على مشاريع في أحد الشركات ولا تعلم كيفية القيام بمهمة معنية وذلك لأنك لم تكمل تعلم أساسيات Laravel وبالتالي ستضطر للتعلم أثناء العمل على مشروع مما سيجعلك تحت ضغط وقلة الوقت، وبالتالي قد يتم إنتاج المشروع بشكل غير سليم أو ربما الكود لن يكون منظم للغاية أو غير نظيف Dirty Code (عكس Clean Code) وسيأثر على عملك في المجمل.
لاحظ أني لا أقصد أن هذه المشاريع التي ذكرتها في سؤالك مفيدة وسوف تستعملها بالفعل في أغلب المشاريع و المواقعالتي ستعمل عليها، ولكنها لا تكفي لعمل أي مشروع مهما كان، وهذا سبب وجود باقي الدروس والمسارات في الدورة (لتتعلم كل ما تحتاجه للعمل).
لذلك نتيجة لكل ما سبق، فربما تجد صعوبة في العثور على عمل، ولكن حتى إن قمت بالعمل في أحد الشركات فستحتاج إلى إكمال التعلم وإنهاء باقي المشاريع (وفي الغالب ستتعلم وأنت تعمل على مشاريع الشركة)، لذلك من الأفضل أن تكمل الدورة للنهاية قبل البحث عن عمل في أحد الشركات.
-
إجابة سامح أشرف سؤال في لماذا لا نستعمل define# بدلًا من constexpr في لغة ++C؟ كانت الإجابة المقبولة
عندما تستخدم موجهة Preprocessor Directive مثل define لتعريف قيمة ثابته مثل MAX_STUDENTS_PER_CLASS فإن هذه القيمة يتم إسبدالها في كل مكان في الملف وستكون النتيجة كالتالي:
int max_students { numClassrooms * 30 }; ولكن هذه الطريقة لها العديد من المشاكل، حيث أن الـ Preprocessor لا يفهم صيغة ++C على الإطلاق ويقوم بإستبدال الثابت MAX_STUDENTS_PER_CLASS بالقيمة 30 في كل مكان يجده في الملف بغض النظر عن أي مجال Scope تكون فيه، وبالتالي قد تجد أن القيمة 30 أصبحت في أماكن غير مرغوب فيها مما يسبب بعض الأخطاء أثناء عملية التصريف Compiling.
وبما أن الثابت MAX_STUDENTS_PER_CLASS يتم إستبدال قيمته قبل عملية التصريف من قِبل الـ Preprocessor فلا يمكن تتبع هذه القيمة من خلال مصحح الأخطاء debugger الموجود في بيئة التطوير لديك، وبالتالي لا يفضل أن يتم إستعمال هذه الطريقة لأنها قد تعرضك إلى أخطاء منطقية Logical Errors والتي يصعب إكتشاف سببها وحلها.
أيضًا قد يتعارض تعريف القيمة من خلال Preprocessor Directive مع الكود نفسه، فعلى سبيل المثال في الكود التالي سوف يتم إزالة كل كلمات beta وإستدالها بالقيمة 3:
#define beta 3 #include <iostream> int main() { int beta{ 5 }; std::cout << beta; return 0; } سوف يقوم الـ Preprocessor بتحويل الكود السابق إلى التالي:
#include <iostream> int main() { int 3{ 5 }; std::cout << 3; return 0; } لاحظ كيف تم تغير اسم المتغير beta في السطر 5 إلى القيمة 3، وفي بعض الأحيان قد يتم تعريف القيمة beta في ملف آخر مما يجعل من عملية إكتشاف سبب هذه المشكلة صعبًا.
وبناءً على ما سبق يفضل إستعمال const أو contexpr بدلًا من define لتفادي الأخظاء السابقة.
-
إجابة سامح أشرف سؤال في ما الفرق بين const و constexpr في لغة ++C؟ كانت الإجابة المقبولة
النوع الأول const عبارة عن ثابت يحمل قيمة من نوع معين، ويكتب على الصيغة التالية:
// const const_name = value; cosnt age = 18; وهذا الثابت يتم جسابه وتخزين قيمته أثناء وقت التشغيل Run Time مثل المتغيرات العادية، ولكن لا يمكن تغير قيمته لاحقًا، وبالتالي إن حاولت تنفيذ الكود التالي سوف يظهر لديك خطأ:
const int age{123}; age = 18; // Error: expression must be a modifiable lvalue بينما النوع الثاني constexpr هو ثابت أيضًا ولكن يتم حسابه وتخزينه أثناء عملية التصريف Compile Time ويجب أن تكون قيمته محددة بالفعل قبل تشغيل البرنامج حتى، وبالتالي يجب أن تكون قيمته معروفة من البداية، لذلك سوف يظهر لديك خطأ إن حاولت أن تقوم بتنفيذ الكود التالي:
int age{}; std::cin >> age; const int myAge{ age }; int age2{}; std::cin >> age2; constexpr int myAge{ age2 }; // Error: age2 is a runtime constant, not a compile-time constant الجزء الأول من الكود سوف يعمل بدون مشكلة، بينما الجزء الثاني سوف تظهر فيه مشكلة تخبرك بأن age2 عبارة عن ثابت يتم حسابه أثناء عملية التنفيذ run time.
لذلك نستنتج أنه:
يجب التصريح عن أي متغير لا يمكن تعديله بعد التهيئة والذي تمت تهيئته في وقت التصريف على أنه constexpr.
يجب التصريح عن أي متغير لا يمكن تعديله بعد التهيئة ومبدئه غير معروف في وقت التصريف على أنه const.
-
إجابة سامح أشرف سؤال في جعل مشروع Laravel قابل للتثبيت من خلال واجهة رسومية كانت الإجابة المقبولة
يمكنك أن تقوم بهذا الأمر لأي مشروع Laravel من خلال الحزمة rachidlaasri/laravel-installer والتي تسمح لك بإنشاء صفحات لتثبيت المشروع دون حاجة المستخدم لتعديل أي ملفات أو تثبيت أي خدمات خارجية في شكل عدد من الخطوات كما في الصورة التالية:
تستطيع تثبيت الحزمة من خلال الأمر التالي:
composer require rachidlaasri/laravel-installer وإذا كنت تستعمل Laravel بإصدار أقل من 5.4 فستحتاج إلى تعديل الملف config/app.php في قسم providers:
'providers' => [ RachidLaasri\LaravelInstaller\Providers\LaravelInstallerServiceProvider::class, ]; الأمر الأخير هو نشر ملفات الحزمة من خلال الأمر التالي:
php artisan vendor:publish --tag=laravelinstaller سيقوم الأمر السابق بإنشاء المجلد installer في public ويمكن أن يتم بدأ تثبيت المشروع من خلال الدخول على رابط التثبيت localhost/install (مع تغير رابط الموقع إن كان هناك نطاق خاص للخادم).
-
إجابة سامح أشرف سؤال في كيفية طباعة أرقام عشرية دقيقة أو أرقام ضحمة بشكل كامل في ++C؟ كانت الإجابة المقبولة
يتم تحديد دقة الرقم عند الطباعة حسب المصرّّف نفسه، في الغالب تكون دقة الأرقم 7 أرقام فقط، ويمكن أن تستخدم معالج الإخراج output manipulator المسمى std::setprecision لتعديل دقة الأرقام، في البداية تحتاج إلى إسندعاء ملف الترويسة iomanip لكي تستطيع إستعمال معالج الإخراج std::setprecision، يقبل معالج الإخراج هذا دقة الطباعة (عدد الأرقام التي سيتم طباعتها) كمعامل له.
ويمكنك أن تستعمله كالتالي:
#include <iostream> #include <iomanip> int main() { std::cout << std::setprecision(10); double x{ 1.234'567'89 }; std::cout << x << '\n'; // 1.23456789 double y{ 123'456'789 }; std::cout << y << '\n'; // 123456789 return 0; } ملاحظة: يفضل أن تفصل الأرقام الكبيرة بإستخدام علامة الإقتباس المفرد ' لتسهيل القراءة فقط، وسيتم معاملة الرقم 789'456'123 على أنه الرقم 123456789 بدون مشكلة.
-
إجابة سامح أشرف سؤال في كيفية طباعة رقم صحيح int كرقم ثنائي binary في ++C؟ كانت الإجابة المقبولة
تدعم لغة ++C طباعة الأرقام بنظام ثماني وست عشري بدون مشكلة، ومع ذلك فإن طباعة الأرقام بالنظام الثماني تحتاج إلى بعض الخطوات الأضافية، تحتوي مكتبة ++C القياسية على الكائن std::bitset (موجود في ملف الترويسة header المسمى <bitset>) .
في البداية يجب أن تحدد للكائن std::binset عدد الـ bits التي سيقوم بتخزينها (يجب أن يكون هذا العدد من نوع compile time constant وليس run time constant) ثم تقوم بتمرير الرقم الذي تريد تحويله إلى النظام الثنائي، كالتالي:
#include <bitset> // std::bitset< حجم ال bits > variable_name { unsigned int }; std::bitset<8> bin{ 13 }; // يمكن تمرير أرقام Hex و Oct و Binary أيضًا std::bitset<8> bin2{ 0xd }; std::bitset<8> bin3{ 015 }; std::bitset<8> bin4{ 0b0000'1101 }; ملاحظة: الرقم الذي تريد تحويله إلى النظام الثنائي يجب أن يكون من نوع unsigned أي ليس له إشارة (موجب فقط)، لذلك تحتاج إلى إستخدام static_cast لتحويل نوع المتغير age عند طباعة الرقم
#include <iostream> #include <bitset> int main() { std::cout << "Enter your age: "; int age{}; std::cin >> age; std::cout << "Your age in decimal is " << std::dec << age << '\n'; std::cout << "Your age in HEX is " << std::hex << age << '\n'; std::cout << "Your age in OCT is " << std::oct << age << '\n'; // تحويل المتغير إلى unsigned unsigned int unsigned_age = static_cast<unsigned int>(age); std::cout << "Your age in Binary is " << std::bitset<8>{unsigned_age} << '\n'; return 0; } وستكون النتيحة كالتالي:
Enter your age: 15 Your age in decimal is 15 Your age in HEX is f Your age in OCT is 17 Your age in Binary is 00001111 يمكنك أيضًا تحويل المتغير age إلى unsinged ثم تحويله إلى كود Binary مباشرة دون تخزينه في متغير جديد:
// تحويل المتغير إلى unsigned وطباعته كنظام ثنائي std::cout << "Your age in Binary is " << std::bitset<8>{static_cast<unsigned int>(age)} << '\n'; -
إجابة سامح أشرف سؤال في التابع cin لا يقوم بإستقبال مدخل مكون من أكثر من كلمة في ++C كانت الإجابة المقبولة
سبب المشكلة هو أن التابع std::cin يقةم بتقسيم المدخلات بالمسافات ويقوم بإستخدام أول جزء فقط (الكلمة "Mohssen") ويخزن الجزء الباقي، وعندما تستخدم std::cin مرة ثانية فإنه يجد الجزء الثاني (الكلمة "Ahmed") موجودة بالفعل، فيقوم بإستخدامها مباشرة دون إنتظار المستخدم لإدخال كلمات أخرى.
لحل هذه المشكلة يمكنك أن تقوم بإدخال القيمة من خلال التابع std::getline حيث يقوم هذا التابع بإستقبال سطر كامل (إي إلى أن يصل إلى n\) ويمكنك تستخدمه كالتالي:
std::cout << "Enter your full name: "; std::string name{}; std::getline(std::cin >> std::ws, name); // قراءة سطر كامل إلى المتغير name std::cout << "Enter your age: "; std::string age{}; std::getline(std::cin >> std::ws, age); // قراءة سطر كامل إلى المتغير age std::cout << "Your name is " << name << " and your age is " << age << '\n'; وستكون المدخلات كالتالي:
Enter your full name: Mohssen Ahmed Enter your age: 23 Your name is Mohssen Ahmed and your age is 23 ما هو std::ws؟
الكائن std::ws هو معالج الدخل input manipulator، أي أنه يقوم بتعديل المدخلات قبل أن يتم تخزينها، والهدف منه هو أن يجعل التابع std::getline يتجاهل أي محارف فارغة في بداية النص، فعلى سبيل المثال إذا لم نقم بإستخدامه في الكود التالي:
std::cout << "choose 1 or 2: "; int choice{}; std::cin >> choice; std::cout << "Enter your name: "; std::string name{}; std::getline(std::cin, name); // لاحظ أننا لم نستعمل std::ws هنا std::cout << "Hello, " << name << ", you picked " << choice << '\n'; ونتيجة الكود السابق سوف تكون:
Choose 1 or 2: 2 Enter your name: Hello, , you picked 2 عندما تقوم بإدخال رقم 2 ثم تضغط على Enter (أي أنك تقوم بإدخال n\ إلى نهاية النص)، يقوم التابع std::getline بأخذ القيمة الرقمية فقط وهي 2 ويترك n\ مخزنة، وعندما تقوم بإستخدام التابع std::getline مرة أخرى فإنه يجد القيمة n\ موجودة بالفعل ويقوم بتخزينها في المتغير name، لهذا يجب أن تستخدم معالج الدخل std::ws لكي يتم تجاهل أي محارف فارغة (المسافة n\ أو t\ .. إلخ) من بداية المدخل، فيتم تجاهل وجود n\ وينتظر حتى يقوم المستخدم بإدخال اسمه.
يمكنك أن تقرأ أكثر حول معالج الدخل من خلال هذه المقالة:
-
إجابة سامح أشرف سؤال في خطأ 'initializing': truncation from 'double' to 'float' في ++C؟ كانت الإجابة المقبولة
هذا تحذير warning وليس خطأ Error، ربما قمت بتفعيل "معاملة التحذيرات كأخطاء" في بيئة التطوير الخاصة بك IDE، لهذا لم يتم تصريف البرنامج. تستطيع أن تقوم بإيقاف هذا السلوك من خلال الضغط على المشروع وإختيار properties ثم قسم ++C/C قم بتغير treat warnings as errors إلى No
مع العلم أنه يفضل أن تبقى على هذا السلوك لأنه يخبرك بأي مشاكل حتى ولو كانت بسيطة أثناء عملية التصريف compiling مما يقلل المشاكل التي قد تحدث في المستقبل.
ويكمن سبب المشكلة في أنك تستخدم متغير من نوع float لتخزين القيمة (9.8) والتي هي عبارة عن double، ويمكنك تحديد أن الرقم عبارة عن float وليس double من خلال إضافة f إلى نهاية الرقم:
// Gravitational acceleration float gravity{ 9.8f }; بهذه الطريقة يمكن أن تقوم بإخبار المصرِّف compiler بأنك تستعمل هنا قيمة literal float وليس double
الإختلاف الرئيسي بين النوع float و النوع double في حجم الذاكرة المستهلك لتخزين القيمة، حيث يستخدم النوع float مساحة 4 بايت، بينما يستخدم النوع double 8 بايت
-
إجابة سامح أشرف سؤال في كيفية تخزين مدخلات المستخدم في متغير منطقي boolean في ++C؟ كانت الإجابة المقبولة
حسب مرجع ++C :
وهذا يعني أنه سيتم تخزين القيمة true إذا قام المستخدم بإدخال الرقم 1، بينما سيتم تخزين القيمة false إذا قام بإدخال الرقم 0، ولتغير هذا السلوك يمكنك أن تستخدم boolalpha لتخزين القيمة true عندما يدخل المستخدم النص true، وتخزين القيمة false عند يدخل النص false:
bool accepted{}; std::cin >> std::boolalpha >> accepted; // يجب أن تكون المدخلات كلمة true أو false std::cout << accepted; لاحظ كيف تم إستعمال boolalpha قبل تخزين القيمة في المتغير accepted، ولإيقاف هذا السلوك يمكنك أن تستعمل noboolalpha:
bool x{}; std::cin >> std::noboolalpha >> x; // يجب أن تكون المدخلات 1 أو 0 std::cout << x; ملاحظة: عند إستعمال boolalpha فسيتم معاملة كلا الرقمين 1 و 0 على أنهما قيمة false وبالتالي لا يمكن تخزين القيمة true في المتغير إلا إذا قام المستخدم بكتابة كلمة true فقط (بحروف صغيرة Lower case)
-
إجابة سامح أشرف سؤال في كيفية ربط كائنين std::vector معًا في ++C؟ كانت الإجابة المقبولة
يمكنك أن تستخدم التابع insert والذي يقوم بإضافة جزء من (أو كل) عناصر vector إلى كائن vector آخر، ويمكنك أن تستعمله كالتالي:
#include <iostream> #include <vector> int main() { std::vector<int> vector1 { 1, 2, 3, 4, 5 }; std::vector<int> vector2 { 6, 7, 8, 9, 10 }; vector1.insert(vector1.end(), vector2.begin(), vector2.end()); std::cout << "Vector1 size: " << vector1.size(); // 10 } لاحظ أن التابع insert يقبل ثلاث مدخلات، الأول هو المكان الذي سيتم إضافة العناصر فيه، وفي الكود السابق سوف يتم إضافة العناصر في نهاية الكائن vector1 والمدخل الثاني والثالث هما بداية ونهاية الجزء الذي سيتم إضافته من الكائن vector2.
-
إجابة سامح أشرف سؤال في ما الفرق بين n\ و std::endl في لغة ++C؟ كانت الإجابة المقبولة
عندما تحاول أن تقوم بطباعة أكثر من نص، لإغنه يتم تخزين هذه النصوص في ما يسمى بـ Buffer وبعد ذلك يتم عرض كل النصوص مرة واحدة على الشاشة (أو كتابتها في ملف) وتسمى هذه العملية بالصرف flushing، فعلى سبيل المثال:
#include <iostream> int main() { for (char i='A'; i <= 'Z'; i++) { std::cout << i << endl; } return 0; } الكود السابق يقوم بطباعة الحروف من A إلى Z وفي كل دورة يتم عمل صرف Flushing للنص وإظهاره على الشاشة. بينما في الكود التالي:
#include <iostream> int main() { for (char i='A'; i <= 'Z'; i++) { std::cout << i << "\n"; } return 0; } فإنه يتم تخزين كل الحروف الـ Buffer وبعد ذلك يتم عرض كل الحروف مرة واحدة.
بالتأكيد فإن الكود الثاني أفضل من ناحية الأداء لأن الكود يقوم بعرض الحروف مرة واحدة على الشاشة، بينما الكود الأول يقوم بعمل Flushing لكل حرف في كل دورة. لذلك يفضل دائمًا أن تستعمل الرمز n\ قدر الإمكان بدلًا من إستخدام std::endl
ملاحظة أخرى وهي أنه يمكن إستخدام الرمز n\ في نهاية النص كالتالي:
std::cout << "Hello\n"; std::cout << "Hello" << std::endl; لاحظ أن السطر الأول يقوم بإستخدام المعامل >> مرة واحدة بينما السطر الثاني فإن يقوم بإستدعاء المعامل >> مرتين، وهذا الأمر قد يؤثر على أداء البرامج التي تعتمد على الخيوط threads بشكل أساسي.
بالتوفيق، تحياتي.
-
إجابة سامح أشرف سؤال في كيفية تحويل الحروف الكبيرة Upper case إلى أحرف صغيرة Lower case في لغة ++C؟ كانت الإجابة المقبولة
يمكنك أن تقوم بهذا الأمر من خلال الدالة std::transform، على النحو التالي:
#include <algorithm> #include <string> #include <iostream> int main() { std::string str = "HELLO, WORLD"; std::transform(data.begin(), data.end(), data.begin(), [](unsigned char c) { return std::tolower(c); }); std::cout << str; // hello, world } بهذا الشكل سوف يتم تغير محتوى المتغير str إلى أحرف صغيرة.
ويمكنك أيضًا أن تستخدم التابع to_lower من مكتبة boost بشكل أسهل، كالتالي:
#include <boost/algorithm/string.hpp> #include <iostream> std::string str = "HELLO, WORLD"; boost::algorithm::to_lower(str); std::cout << str; // hello, wolrd
-
إجابة سامح أشرف سؤال في كيفية إزالة المسافات الزائدة من نص في ++C؟ كانت الإجابة المقبولة
يمكنك أن تقوم بعمل دالة left-trim ، كالتالي:
const char* wihte_spaces = " \t\n\r\f\v"; // إزالة المسافات الزائدة من بداية النص std::string& ltrim(std::string& str, const char* ws = wihte_spaces) { str.erase(0, str.find_first_not_of(ws)); return str; } تقوم الدالة السابقة بإستقبال النص كمدخل لها وتعيد نفس النص بعد إزالة المسافات الزائدة من بدايته، لاحظ كيف تم إستخدام الثابت wihte_spaces والذي يحتوي على كل أشكال المسافات، وفي الدالة نفسها نقوم بحذف كل هذه المسافات من البداية (index 0)
ويمكن إستخدام نفس الطريقة لعمل دالة rtrim:
std::string& rtrim(std::string& str, const char* ws = wihte_spaces) { str.erase(str.find_last_not_of(ws) + 1); return str; } تقوم هذه الدالة بحذف المسافات من نهاية النص، لاحظ كيفية إستخدام التابع find_last_not_of وإضافة 1 إلى القيمة المرجعة.
الآن يمكنك أن تقوم ببساطة عمل دالة تسمى trim تقوم بتنفيذ الدالتين السابقتين، على النحو التالي:
// ستقوم هذه الدالة بحذف المسافات من بداية ونهاية النص المدخل إليها std::string& trim(std::string& str, const char* ws = wihte_spaces) { return ltrim(rtrim(str, ws), ws); } ستقوم هذه الدالة بإستدعاء كلٍ من ltrim و rtrim ثم إرجاع النص بعد إزالة المسافات من بدايته ونهايته.
الآن يمكنك إستخدام الدالة الأخيرة كالتالي:
std::string s = " Hello world "; std::cout << trim(s); // Output: "Hello world" -
إجابة سامح أشرف سؤال في كيف أحول هذا الكود إلى GUI؟ كانت الإجابة المقبولة
تحويل برنامج بسيط مكون من 5 أسطر مثل هذا سوف يأخذ بعض المجهود الإضافي لإنشاء واجهة رسومية GUI له، حيث يجب أن يتم إستعمال أحد المكتبات الخاصة بالواجهات الرسومية مثل Tkinter (موجودة مسبقًا في Python لذلك لا تحتاج تثبيت أي شيء إضافي)، لذلك يمكن إما جعل البرنامج يتلقى مُدخل من المستخدم من سطر الأوامر مباشرة:
# Distance Traveled speed=int(input("Enter Speed: ")) # The distance in 5 hours print("The distance the car will travel in 5 hours is", speed*5, "miles") print("The distance the car will travel in 8 hours is", speed*8, "miles") print("The distance the car will travel in 12 hours is", speed*12, "miles") أو يمكن عمل واجهة كاملة من خلال مكتبة Tkinter وستكون الخطوات كالتالي:
في البداية نقوم بعمل نافذة فارغة:
import tkinter as tk from tkinter import * class App(tk.Frame): def __init__(self, master): super().__init__(master) root = tk.Tk() # تغير عنوان النافذة root.title("Calculate Distance") myapp = App(root) myapp.mainloop() بعد ذلك نقوم بإضافة صندوق للإدخال ونص "Enter Speed" وزر لحساب المسافة:
class App(tk.Frame): def __init__(self, master): super().__init__(master) # إضافة حاشية إلى النافذة self.pack(padx=20, pady=20) # كتابة نص بسيط self.label = tk.Label(self, text="Enter Speed:") self.label.pack(fill=BOTH, expand=True) # عمل صندوق لإدخال قيمة السرعة self.entrythingy = tk.Entry(self, width=50) self.entrythingy.pack(fill=BOTH, expand=True) # عمل نص لعرض النتيجة في النهاية self.result_label = tk.Label(self, text="") self.result_label.pack(fill=BOTH, expand=True) # عرض ثلاث أزرار لحساب المسافة self.calc_btn_5 = tk.Button( text="Time: 5", command=lambda:self.calcDistance(time=5)) # التابع calcDistance سنقوم بعملة في الخطوة التالية self.calc_btn_5.pack(side=LEFT, expand=True) self.calc_btn_8 = tk.Button( text="Time: 8", command=lambda:self.calcDistance(time=8)) # التابع calcDistance سنقوم بعملة في الخطوة التالية self.calc_btn_8.pack(side=LEFT, expand=True) self.calc_btn_12 = tk.Button( text="Time: 12", command=lambda:self.calcDistance(time=12)) # التابع calcDistance سنقوم بعملة في الخطوة التالية self.calc_btn_12.pack(side=LEFT, expand=True) الآن نقوم بعمل التابع calcDistance لحساب المسافة وإظهار النتيجة في النافذة:
class App(tk.Frame): def __init__(self, master): super().__init__(master) # ... def calcDistance(self, time=1): # جلب القيمة من صندوق الإدخال speed = self.entrythingy.get() if not speed: return # تحويل المدخل إلى رقم speed = int(speed) # تجهيز النتيجة النهائية result = f"The distance the car will travel in { time } hours is { speed * time } miles" # عرض النتيحة في النافذة self.result_label.config(text=result) سيكون شكل البرنامج في النهاية كالتالي:
هنا الكود بالكامل:
import tkinter as tk from tkinter import * class App(tk.Frame): def __init__(self, master): super().__init__(master) # إضافة حاشية إلى النافذة self.pack(padx=20, pady=20) # كتابة نص بسيط self.label = tk.Label(self, text="Enter Speed:") self.label.pack(fill=BOTH, expand=True) # عمل صندوق لإدخال قيمة السرعة self.entrythingy = tk.Entry(self, width=50) self.entrythingy.pack(fill=BOTH, expand=True) # عمل نص لعرض النتيجة في النهاية self.result_label = tk.Label(self, text="") self.result_label.pack(fill=BOTH, expand=True) # عرض ثلاث أزرار لحساب المسافة self.calc_btn_5 = tk.Button( text="Time: 5", command=lambda:self.calcDistance(time=5)) self.calc_btn_5.pack(side=LEFT, expand=True) self.calc_btn_8 = tk.Button( text="Time: 8", command=lambda:self.calcDistance(time=8)) self.calc_btn_8.pack(side=LEFT, expand=True) self.calc_btn_12 = tk.Button( text="Time: 12", command=lambda:self.calcDistance(time=12)) self.calc_btn_12.pack(side=LEFT, expand=True) def calcDistance(self, time=1): # جلب القيمة من صندوق الإدخال speed = self.entrythingy.get() if not speed: return # تحويل المدخل إلى رقم speed = int(speed) # تجهيز النتيجة النهائية result = f"The distance the car will travel in { time } hours is { speed * time } miles" # عرض النتيحة في النافذة self.result_label.config(text=result) root = tk.Tk() # تغير عنوان النافذة root.title("Calculate Distance") myapp = App(root) myapp.mainloop()
-
إجابة سامح أشرف سؤال في كيفية تمرير مدخلات إلى برنامج مكتوب بلغة ++C؟ كانت الإجابة المقبولة
لا تحتاج إلى أي مكتبات للقيام بهذا الأمر حيث يقوم المصرِّف تلقائيًا بتمرير عدد هذه القيم المدخلة arguments إلى البرنامج وقيمها أيضًا إلى الدالة main، لذلك تحتاج إلى أستقبال هذه القيم في شكل معاملات parameters
int main(int argc, char** argv) { std::cout << "The total arguments is " << argc << std::endl; std::cout << "The file name is " << argv[0] << std::endl; std::cout << "The first argument is " << argv[1] << std::endl; return 0; } عند تشغيل البرنامج السابق وتمرير كلمة Hsoub إليه فستجد أن البرنامج يقوم بطباعة التالي:
C:\> main.exe Hsoub The total arguments is 2 The file name is main.exe The first argument is Hsoub المعامل الأول الذي يتم تمريره إلى الدالة main هو عدد صحيح عبارة عن عدد المدخلات، بينما المعامل الثاني فيعبر عن كل القيم الممرة وتكون أول قيمة هي اسم البرنامج نفسه.
ملاحظة: المعامل الأول يكون دائمًا اسم البرنامج نفسه في سطر الأوامر (main.exe)، لذلك فإن سنجد أن عدد المدخلات في المثال السابق هو 2 وليس 1
يمكن أيضًا إستخدام الكود التالي وستحصل على نفس النتيجة:
int main(int argc, char** argv) { // ... } ملاحظة في حالة تمرير أكثر من معامل سيتم الفصل بينهم من خلال المسافة:
C:\> main.exe Hello World The total arguments is 3 The file name is main.exe The first argument is Hello في المثال السابق يتم تمرير كلمة Hello كمعامل ثاني وكلمة World كمعامل ثالث، ولكي يتم تتمرير الكلمتين كمعامل واحد فقط يجب أن يتم وضعهم داخل علامة تنصيص، على النحو التالي:
C:\> main.exe "Hello World" The total arguments is 3 The file name is main.exe The first argument is Hello World -
إجابة سامح أشرف سؤال في ما هي طرق تهيئة المتغيرات في لغة ++C؟ كانت الإجابة المقبولة
تحتوي لغة ++C على أربع طرق أساسية لتهيئة المتغيرات، وهي كالتالي:
الطريقة الأولى: تعريف المتغير بدون تهيئة قيمة أولية للمتغير
int a; نستخدم هذه الطريقة عندما نريد تخزين قيمة في وقت لاحق، كما في الكوطد التالي:
int age; std::cin >> age; // نخزن العمر بعد أن يقوم المستخدم بإدخال الرقم أو في حالة أردنا إستخدام متغير عام داخل أحد المجالات المحلية، وقد ذكرت هذا الأمر هنا:
الطريقة الثانية: هي تهيئة النسخ copy initialization، وهي الطريقة العادية المتبعة في أغلب لغات البرمجة:
int width = 5; تسمى هذه الطريقة بهذا الاسم، لأنها تقوم بنسخ القيمة الموجودة على اليمين إلى المتغير الموجود على اليسار.، ويفضل إتباع هذه الطريقة في الحالات العادية، ولكن هناك حالات أكثر تعقيدًا لن يمكننا أن نستخدم فيها هذه الطريقة.
الطريقة الثالثة: هي التهيئة المباشرة Direct initialization
int width( 5 ); الطريقة السابقة هي نفسها المستخدمه في طريقة تهئية النسخ، ولكن بالنسبة الحالات الأكثر تعقيدًا ، تميل التهيئة المباشرة إلى أن تكون أكثر كفاءة من تهيئة النسخ.
الطريقة الرابعة والأخيرة: هي التهيئة باستخدام الأقواس Brace initialization (وتسمى أيضًا بـ uniform initialization أو list initialization)
يمكن إستخدام هذه الطريقة بثلاث أشكال:
int width { 5 }; // يفضل إتباع هذه الطريقة int height = { 6 }; // نفس الطريقة السابقة int depth {}; // تسمى بـ value initialization هناك بعض الحالات التي لا يمكن فيها إستخدام تهئية النسخ أو التهيئة المباشرة، فعلى سبيل المثال في الكود التالي سوف يحدث خطأ:
int width { 4.5 }; // Error في الكود السابق نحاول تخزين القيمة 4.5 (وهي قيمة من نوع double) في المتغير width ولكن نوع المتغير هو رقم صحيح int وبالتالي يظهر خطأ بسبب إختلاف الأنواع، لن يظهر هذا الخطأ إن كانت القيمة صحيح (4.0 أو 4).
بينما إن تم إستخدام طريقة تهيئة النسخ أو التهيئة المباشرة سوف يتم تجاهل القيمة العشرية بالكامل وسكون الرقم هو 4 فقط، مما قد يؤدي إلى أخطأ وقت التشغيل يصعب إكتشافها وإصلاحها. لذلك يفضل التهيئة باستخدام الأقواس كلما أمكن ذلك.
-
إجابة سامح أشرف سؤال في ما الذي يجب أن تعيده الدالة main في لغة ++C؟ كانت الإجابة المقبولة
القيمة المرجعة من الدالة main تشير إلى حالة البرنامج أثناء خروجه، وتعني القيمة 0 أن البرنامج قد إنتهى بشكل سليم وبدون مشاكل، بينما أي قيمة أخرى تعني أن هناك مشكلة قد حدثت ولكن لا يوجد قيم متفق عليها تشير إلى حالة واحدة من المشاكل بين أنظمة التشغيل، ومع ذلك توفر الحزمة iostream القيمتين EXIT_SUCCESS و EXIT_FAILURE والتي يمكن إستخدامها للتعبر عن حالة البرنامج أثناء خروجه وستعمل هذه القيم بدون مشكلة بغض النظر عن نظام التشغيل المستخدم:
#include <iostream> int main() { return EXIT_SUCCESS; } أيضًا إن لم يتم إرجاع أي قيمة من الدالة main فسوف يتم إرجاع القيمة 0 بشكل إفتراضي بدون تدخل من المبرمج.
أما بالنسبة إلى نوع الدالة فيجب أن يكون النوع int في ++C وذلك حسب معاير اللغة نفسها، وبالرغم من أن النوع void يمكن أن يستخدم ولن تظهر أي أخطاء فلا يجب أن يتم إستخدام هذا النوع كنوع للدالة main .
أما بالنسبة للتأثير على أداء البرنامج فلا يوجد فرق بين أي طريقة من الطريق السابقة من ناحية الأداء، حيث يتم تنفيذ الدالة main مرة واحدة في الغالب ويتم إغلاقها بمجرد إنتهاء البرنامج.
-
إجابة سامح أشرف سؤال في هل توجد دورة للغة python في أكاديمية حسوب؟ كانت الإجابة المقبولة
كل دورات حسوب ليس دورات متخصصة في لغة برمجة أو تقنية معينة، بل تهتم بالمجال بشكل عام، فعلى سبيل المثال ستجد أن هناك دورة لتطوير واجهات المستخدم يتم فيها شرح أساسيات HTML و CSS و JavaScript و jQuery و Bootstrap وغيرها الكثير من التقنيات والمكتبات والحزم، وبالتالي يمكن للطالب أن يقوم أي واجهة مستخدم بعد الإنتهاء من الدورة بشكل كامل وإتمام كل المشاريع التي تحتويها. أو كمثال آخر هو دورة تطوير الواجهات الخلفية بإستخدام لغة PHP، سنجد أن الدورة تركز على شرح أساسيات التعامل مع الواجهات الخلفية وإتمام المشاريع العملية التي يحتاجها سوق العمل، بالطبع بعد شرح كل أساسيات لغة PHP من البداية، ويقوم الطالب بعمل العديد من المشاريع بإستعمال أشهر إطارات العمل Laravel، وكنتيجة لهذا الأمر سيكون لدى الطالب إماكانية عمل كل مشاريع الواجهات الخلفية Backend بإستخدام لغة PHP
لذلك لا تحتوي الأكاديمية على دورة مخصصة تشرح فيها لغة Python وحدها، ولكن تحتوي الأكاديمية على دورة علوم الحاسوب على شرح لكل أساسيات لغة Python ومفاهيم البرمجة الكائنية Object-oriented programming، وكيفية تنفيذ أشهر الخوارزميات بإستخدام هذه اللغة، بالإضافة لشرح هياكل البيانات Data Structure بإستخدام Python أيضًا، وكذلك أنماط التصميم Design Patterns الإنشائية والهيكلية والسلوكية، وكل هذا يكون بعد تعليم الطالب الأساسيات البرمجية وكيفية التعامل مع أنظمة التشغيل وفهمه لأنظمة قواعد البيانات المختلفة.
يمكنك معرفة المزيد عن هذه الدورة من خلال الصفحة الخاصة بها من هنا (دورة علوم الحاسوب)
-
إجابة سامح أشرف سؤال في لماذا لا يمكن إنشاء متغيرات في جملة switch في لغة ++C؟ كانت الإجابة المقبولة
جمل case عبارة عن وسوم حالة case label وهذا يعني أن المصرِّف سوف يفهم هذا الجزء على أنه قفز jump (إنتقال مباشر) إلى وسم حالة case label الذي يساوي القيمة val مباشرة، وسبب المشكلة هنا متعلق بالأقواس المعقوفة Curly Brackets، حيث أن الأقواس الخاصة بجملة Switch نفسها تحدد المجال scope بأنه كل شيء داخل جملة Switch، وهذا يعني أن عملية الإنتقال ستتجاهل تعريف متغير x في داخل أي وسم حالة case label لكي لا يحدث خطأ redefined variable، حيث يتم إعتبار أن كل الحالات cases تتبع نفس مجال switch مما يؤدي إلى إعادة تعريف المتغير أكثر من مرة (حسب الحالات cases).
لحل هذه المشكلة يمكن ببساطة إضافة أقواس لكل حالة case، على النحو التالي:
int main() { int val = 1; switch (val) { case 1: { std::cout << "done"; int x = 123; break; } case 2: { int x = 456; break; } } return 0; } أو يمكن أيضًا تعريف المتغير نفسه خارج جملة Switch وتغير قيمته في كل حالة case:
int main() { int val = 1; int x; switch (val) { case 1: std::cout << "done"; x= 123; break; case 2: x = 456; break; } std::cout << x; // 123 return 0; } يفضل أن تستعمل كلا الطريقتين السابقتين (إستعمال الأقواس وتعريف المتغير خارج switch)، ليكون الكود في النهاية على هذا الشكل:
int main() { int val = 1; int x; switch (val) { case 1: { std::cout << "done"; x = 123; break; } case 2: { x = 456; break; } } std::cout << x; // 123 return 0; } يوجد حل آخر أقل شيوعًا وهو تعريف المتغير داخل جملة Switch نفسها، ولكن هذا الأمر سوف يؤدي إلى أن يكون المتغير تابع إلى مجال scope جملة switch وبالتالي لا يمكنك أن تستعمل هذا المتغير خارج Switch:
int main() { int val = 1; switch (val) { int x; // تعريف المتغير داخل switch نفسها case 1: { std::cout << "done"; x = 123; break; } case 2: { x = 456; break; } } std::cout << x; // Error: 'x': undeclared identifier return 0; } يمكنك أن تستعمل الكود السابق في حالة أردت أن تستعمل المتغير x داخل Switch فقط، وليس خارجها.
يمكنك أن تقرأ أكثر حول وسوم الحالة case labels من خلال هذه المقالة:
أو من خلال هذه المقالة التي تشرح جملة Switch بالتفصيل:
-
إجابة سامح أشرف سؤال في ما الفرق بين المصرِّف gcc و ++g في تصريف ملفات ++C؟ كانت الإجابة المقبولة
كلامهما عبارة عن مصرِّف من مجموعة مصرفات جنو GNU Compliers Collection أو إختصارًا GCC (بأحرف كبيرة)، وهو عبارة عن كل اللغات المختلفة التي يدعما مصرف جنو GNU Compiler.
التسمية
سُمي المصرِّف gcc كإختصار لـ GNU: C Compiler ، بينما تم تسميه المصرِّف ++g إختصار لـ GNU C++ Compiler
الإختلافات
توجد مجموعة من الإختلافات الرئيسية بينهما:
المصرِّف gcc سوف يقوم بتصريف الملفات c.* و cpp.* بناءً على اللغة المستخدمة، أي أن الملفات التي تنتهي بالصيغة c. سوف يتم ترجمتها على أساس لغة C ونفس الأمر بالنسبة للملفات التي لها الصيغة cpp.* سيتم تصريفها على أساس لغة ++C، على الجانب الآخر لن يقوم المصرف ++C بهذا الأمر وسيقوم بالتعامل مع كلا الصيغتين على أنهما ملفات ++C بشكل إفتراضي. عند إستخدام المصرِّف ++g لربط ملفات الكائنات Object files سوف يقوم تلقائيًا بربط المكتبات القياسية المستخدمة std C++ libraries، بينما المصرِّف gcc يحتاج إلى إستخدام المعامل التالي: gcc -xc++ -lstdc++ -shared-libgcc المصرِّف gcc سوف يستخدم شيفرات جامعة (macros) أقل عند تصريف ملفات c.* عند إستخدام المصرف gcc لتصريف ملفات ++C أو عند إستخدام المصرِّف ++g (لتصريف الملفات c.* أو cpp.*) فسيتم إستخدام بعض الـ Macros الإضافية، كالتالي: #define __GXX_WEAK__ 1 #define __cplusplus 1 #define __DEPRECATED 1 #define __GNUG__ 4 #define __EXCEPTIONS 1 #define __private_extern__ extern كخلاصة لما سبق، يمكن إستخدام كلا المصرفين لتصريف ملفات ++C بدون مشكلة، ويفضل أن تستخدم ++g عند التعامل معه ++C بشكل عام، لأنه يسهل على المبرمج عملية ربط ملفات الكائنات Object Files.
-
إجابة سامح أشرف سؤال في ما الفرق بين func(void) و ()func في لغة ++C؟ كانت الإجابة المقبولة
هذه العادة البرمجية جاءت من لغة C ، حيث أنه في لغة C الدالة التي لديها قوسين فارغين كالدالة التالية:
int func(); تقبل أي عدد من المدخلات من أي نوع، حيث لا يقوم المصرف بالتحقق في هذه الحالة من عدد المدخلات إلى الدالة، ولجعل الدالة لا تقبل أي مدخلات على الإطلاق يجب أن تكتب النوع void كمعامل لهذه الدالة، ويؤدي إستخدام الدالة بأقواس فارغة إلى سلوك غير معروف undefined behavior, لذلك يجب أن يتم تمرير معامل واحد على الأقل، وفي الوقت الحالي يفضل أن يتم إستخدام طريقة ... لعمل دالة تقبل أي عدد من المدخلات:
int func(int args, ...); أما في لغة ++C فإن الأقواس الفارغة ( ) تعني أن الدالة لا تقبل أي مدخلات، ومع ذلك فإن void كمعامل للدالة في لغة ++C يُعد صحيحًا أيضًا. وبناءً على ما سبق فلا يوجد فرق في للأداء بين الطريقتين.
-
إجابة سامح أشرف سؤال في ما هو المعامل <-- في لغة ++C؟ كانت الإجابة المقبولة
في الواقع هذا ليس معامل واحد بل معاملين معًا، الأول هو معامل الإنقاص -- والثاني هو معامل المقارنة < ، وبذلك يتم مقارنة قيمة x مع الصفر ثم يتم إنقاص واحد من هذه القيمة، ليكون الكود أوضح يمكنك أن تستخدم الأقواس، كالتالي:
#include <iostream> int main() { int x = 11; while ((x--) > 0) { std::cout << x << std::endl; } return 0; } لاحظ يبدو الكود أكثر وضوحًا الآن، كما يمكن إستعمال نفس الطريقة في حلقة for:
for(int x = 11; x --> 0;) { std::cout << x << std::endl; }
-
إجابة سامح أشرف سؤال في لماذا لا يجب أن أستعمل using namespace std في برامج ++C؟ كانت الإجابة المقبولة
السبب ليس تأثير هذه الطريقة في إستخدام المجال std على أداء البرنامج، في الواقع هذا الكود لن يؤثر على أداء البرنامج بل على طريقة تصريفه compiling حيث قد تظهر العديد من الأخطاء في حالة وجود دوال مشابة للموجودة في المجال std، فعلى سبيل المثال إن كان لدينا الكود التالي:
using namespace foo; // يحتوي على الدالة xyz using namespace bar; // يحتوي ايضًا على الدالة xyz الكود السابق سوف يؤدي إلى خطأ أثناء عملية التصريف بسبب تعريف الدالة xyz مسبقًا، وبالتالي لا تستفيد من فضاءات الأسماء namespaces حيث أن سبب إستخدامها هو حل هذه المشكلة من الأساس.
يمكنك الإطلاع على هذه المقالة لمزيد من المعلومات حول فضاءات الأسماء namespaces:
ملاحظة يمكنك أن تستعمل اللكود التالي في حالة أردت إستخدام cout بدون كتابة اسم المجال std:
#include <iostream> // إستخدام cout فقط using std::cout; int main() { cout << "hello, wolrd\n"; return 0; } بهذه الطريقة يمكنك إستعمال كل الدوال من المكتبة القياسية iostream وفي نفس الوقت تستطيع إستعمال cout بشكل مباشر.