الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

نقاش حول الدالة getch

بدأه khaled_prg في 5 مارس 2012 · 9 رد · 8,103 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم و رحمة الله و بركاته

بينما كنت أناقش أخي Omnivore أمس, لفت انتباهي استدعاءه لــ getch بصفتين مختلفتين !, و هذا ما جعلني أفتح موضوعا جديدا لكي نناقش فيه بعض المسائل المتعلقة بهذه الدالة.

نهتم في هذا الموضوع بنقطتين هما :

  • ما نوع المتغير الذي تعيده getch ؟ هل هو char أم int ؟
  • كيف يمكننا استدعاء الدالة getch و كأنها تعيد void ؟

بالنسبة للنقطة الأولى :

بداية, الأخ Omnivore قال بأن الدالة getch تُعيد int و أنا أخالفه في هذه الجزئية ! لأنني أعتقد أن هذه الدالة تُعيد char وهو المحرف الذي أدخله المستخدم. بدليل أنه إذا قمنا بإسناد القيمة التي تعيدها getch إلى متغير من نوع int ثم أظهرنا هذه القيمة فسنلاحظ أن القيمة المُسندة ما هي إلا كود المتغير المُدخل في جدول ASCII و هذا يدل على أن getch قرأت المُدخلات على أنها محرف و عند إسناد القيمة تم عمل Casting بشكل ضمني و بالتالي سيحمل متغير int الكود المـُقابل للحرف المـُدخل في الــ ASCII.

حتى لا يكون كلامي نظريا بحتا, سأعطي مثال :

#include<stdio.h>
#include<conio.h>
int main() {
    char CH = getch();
    printf("CH = %d",(int)CH);
    return 0;
}

عندما نُدخل الحرف A في الكود السابق ستظهر القيمة 65 و عندما نُدخل a ستظهر القيمة 97.

إذا الدالة getch تعاملت مع المـُدخلات على أنها حرف (caractère) و السبب في ظهور تلك الأعداد يعود إلى نوع المتغير CH.(تم تحويل الأحرف إلى ما يُقابلها في جدول الآسكي).

الكود السابق يُكافئ :

char CH = getch();
printf("CH = %d",(int)CH);

ملاحظة :

حتى لا نخرج عن سياق الموضوع .. لسنا هنا للحديث عن ترميز أنواع المتغيرات داخل الذاكرة و تسجيل القيم داخل مسجلات لبروسيسور !

الموضوع يناقش طرق استدعاء الدالة getch و الأمور الأخرى عبارة عن كماليات و لا تمثل لب الموضوع.

بالنسبة للنقطة الثانية :

إذا كانت لدينا دالة تعيد قيمة من نوع ما, ثم قمنا باستدعاء هذه الدالة و لكن لم نُخزن هذه القيمة فهل معنى هذا أن الدالة لا تعيد شيء ؟ طبعا لا !

مثلا , يمكننا استدعاء الدالة getch بالطريقتين :

char CH = getch();
getch();

إذا كنت تستغرب الإستدعاء الثاني بحجة أن الدالة getch تُعيد char فإليك الكود الآتي :

#include<stdio.h>
#include<time.h>
int random(){
    srand(time(NULL));
    return rand()%100;
}
int main() {
    random();
    printf("RANDOM = %d",random());
    return 0;
}

الدالة random تُعيد عدد عشوائي يقع بين 0 و 100, لكن ألا تلاحظ أنه تم استدعاء random (بدون تخزين القيمة المـُعادة في متغير جديد) داخل printf ؟ و الكود يعمل 100% !

قد تقول هذه حالة خاصة باستدعاء الدوال في printf لأن الكتابة العامة لـــ printf تفرض ذلك !, حسنا لا بأس .. يمكنك استدعاء random هكذا :

random();

نخلص إلى أن استدعاء الدالة (بدون تخزين القيمة المُعادة) لا يعبر عن شيء !.

ما رأيكم ؟؟

تم تعديل هذه المشاركة بواسطة khaled_prg في 5 مارس 2012 في 11:35

#2
khaled_prg كتب:

بداية, الأخ Omnivore قال بأن الدالة getch تُعيد int و أنا أخالفه في هذه الجزئية ! لأنني أعتقد أن هذه الدالة تُعيد char وهو المحرف الذي أدخله المستخدم. بدليل أنه إذا قمنا بإسناد القيمة التي تعيدها getch إلى متغير من نوع int ثم أظهرنا هذه القيمة فسنلاحظ أن القيمة المُسندة ما هي إلا كود المتغير المُدخل في جدول ASCII و هذا يدل على أن getch قرأت المُدخلات على أنها محرف و عند إسناد القيمة تم عمل Casting بشكل ضمني و بالتالي سيحمل متغير int الكود المـُقابل للحرف المـُدخل في الــ ASCII.

اليك الدليل على كلامي, هذا تعريفها من ملف conio.h , GCC (لاحظ نوع المتغير الذي تعيده ) :

_CRTIMP int __cdecl __MINGW_NOTHROW	getch (void);

ومن ملف conio.h لــVisual Studio :

_CRTIMP int __cdecl getch(void);

في كودك السابق عندما تضع نتيجة نداء الدالة getch في متغير من نوع حرفي فقد عملت truncation أو قطع للقيمة int وأخذت الـــLow Order Byte فقط وهو يمثل الحرف الذي ادخله المستخدم فعلا,

اقتباس
بالمناسبة, هل لدى أحدكم فكرة عن برمجة دالة مُكافئة لــ getch ؟

بالنسبة للبديل على نظم من نوع unix , Linux وجدت الكود التالي على الرابط التالي :

http://www.linuxforums.org/forum/programming-scripting/40146-getch-gcc.html

وهو كود يحاكي getch يمكن استعماله على Linux , اذن يمكن عمل استبدال واحدة باخرى عن طريق

#ifdef __linux__
#define getch mygetch
#endif

/*solution provided by kermi3 from this web posting C Board */

#include <termios.h>
#include <unistd.h>

int mygetch(void)
{
struct termios oldt,
newt;
int ch;
tcgetattr( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}

بالتوفيق للجميع

تم تعديل هذه المشاركة بواسطة OmniVore في 5 مارس 2012 في 12:58

وما كلُّ ذِي لُبٍّ بمِؤْتيكَ نُصحَه ****** وما كلُّ مؤْتٍ نُصحَه بلبيب

************************************************

ما دعوة أنفع يا صـــــاحبي .... من دعوة الغــــائب للغــــائب

ناشدتك الــــرحمن يا قــــارئاً .... أن تسأل الغفران للكاتب (ولوالديه)

#3
اقتباس

بالنسبة للبديل على نظم من نوع unix , Linux وجدت الكود التالي على الرابط التالي :

أخى OmniVore هل جرب الكود أعلاة . حقيقا انا من كثرة ما وجدت الكود هذا على المنتديات حفظتة يصدر مشاكل كثيرة وﻻ يعمل

والكود البديل لة الذى جربتة فى برنامج سي على بيئة NetBeans7 يعمل وبشكل جيد:

ومصدر الكود من هنا.

 /*
    getch() and getche() functionality for UNIX,
    based on termios (terminal handling functions)

    This code snippet was written by Wesley Stessens (wesley@ubuntu.com)
    It is released in the Public Domain.
*/

#include <termios.h>
#include <stdio.h>

static struct termios old, new;

/* Initialize new terminal i/o settings */
void initTermios(int echo) {
    tcgetattr(0, &old); /* grab old terminal i/o settings */
    new = old; /* make new settings same as old settings */
    new.c_lflag &= ~ICANON; /* disable buffered i/o */
    new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */
    tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */
}

/* Restore old terminal i/o settings */
void resetTermios(void) {
    tcsetattr(0, TCSANOW, &old);
}

/* Read 1 character - echo defines echo mode */
char getch_(int echo) {
    char ch;
    initTermios(echo);
    ch = getchar();
    resetTermios();
    return ch;
}

/* Read 1 character without echo */
char getch(void) {
    return getch_(0);
}

/* Read 1 character with echo */
char getche(void) {
    return getch_(1);
}

/* Let's test it out */
int main(void) {
    char c;
    printf("(getche example) please type a letter: ");
    c = getche();
    printf("\nYou typed: %c\n", c);
    printf("(getch example) please type a letter...");
    c = getch();
    printf("\nYou typed: %c\n", c);
    return 0;
}

لكن مشكلة الــ buffered لــ new line موجودة.

والله ولى التوفيق

تم تعديل هذه المشاركة بواسطة محمد ابوزايد في 5 مارس 2012 في 14:02

post-15367-027053900%201340345097.gifpost-15367-087183200%201340344597.gif
post-247365-0-10080400-1379403554_thumb.

#4
محمد ابوزايد كتب:

أخى OmniVore هل جرب الكود أعلاة . حقيقا انا من كثرة ما وجدت الكود هذا على المنتديات حفظتة يصدر مشاكل كثيرة وﻻ يعمل

انا لم اجرب الكود فقط نقلته , لانني حذفت جميع الـــvirtual machines من نوع linux التي كانت على الجهاز بسبب احتياجي لمساحة اكبر على القرص الصلب,

بالتوفيق للجميع

تم تعديل هذه المشاركة بواسطة OmniVore في 5 مارس 2012 في 14:05

وما كلُّ ذِي لُبٍّ بمِؤْتيكَ نُصحَه ****** وما كلُّ مؤْتٍ نُصحَه بلبيب

************************************************

ما دعوة أنفع يا صـــــاحبي .... من دعوة الغــــائب للغــــائب

ناشدتك الــــرحمن يا قــــارئاً .... أن تسأل الغفران للكاتب (ولوالديه)

#5

أشكركم إخوتي على إثراء الموضوع :)

لكن من قال بأنني أريد محاكاة عمل getch في نظام Linux فقط ؟؟

أنا أريد محاكاة عمل الدالة getch بدون الإعتماد على نظام تشغيل معين !, يعني يكون الكود صالح للويندوز و لينكس و الماك و ....

بالمناسبة, ما فائدة هذه المكبتة ؟

#include <termios.h>
#6
khaled_prg كتب:

لكن من قال بأنني أريدأشكركم إخوتي على إثراء الموضوع :) محاكاة عمل getch في نظام Linux فقط ؟؟

أنا أريد محاكاة عمل الدالة getch بدون الإعتماد على نظام تشغيل معين !, يعني يكون الكود صالح للويندوز و لينكس و الماك و ....

فكرة جيدة . هل هذة الدالة لم تفكر فيها ANSI C/C++ أكيد بحثو هذة المشكلة ووجدو مشاكل جوهرية تعوق ذلك ﻻ ادرى ولكن سأبحث فى ذلك

khaled_prg كتب:

بالمناسبة, ما فائدة هذه المكبتة ؟

#include <termios.h>

هى مكتبة تشبه console.h فى ويندوز . وتهدف اساسا للتحكم فى بيئة سطر اﻻوامر فى لينكس والتى تسمى terminal تستطيع مثلا التحكم هل تظهر الحروف التى تكتبها على الشاشة ام ﻻ. و تحويل newline الى CR وغيرها من المميزاات

للأطلاع على المكتبة بالتفصيل

مـــــــــن هنـــــــــــــــــــــــــا

بالنسبة لمستخدمى لينكس عن طريق اﻻمر:

$ man termios

تم تعديل هذه المشاركة بواسطة محمد ابوزايد في 5 مارس 2012 في 17:03

1

post-15367-027053900%201340345097.gifpost-15367-087183200%201340344597.gif
post-247365-0-10080400-1379403554_thumb.

#7
اقتباس
فكرة جيدة . هل هذة الدالة لم تفكر فيها ANSI C/C++ أكيد بحثو هذة المشكلة ووجدو مشاكل جوهرية تعوق ذلك ﻻ ادرى ولكن سأبحث فى ذلك

كلامك جميل أخي محمد :)

أنا أيضا سأبحث في هذه القضية إن شاء الله.

اقتباس
هى مكتبة تشبه console.h فى ويندوز . وتهدف اساسا للتحكم فى بيئة سطر اﻻوامر فى لينكس والتى تسمى terminal تستطيع مثلا التحكم هل تظهر الحروف التى تكتبها على الشاشة ام ﻻ. و تحويل newline الى CR وغيرها من المميزاات

اتضحت الفكرة, بارك الله فيك.

1
#8

نسخه تعمل مع اى مترجم

int getch()
{
    return fgetc(stdin);
}

راجع هذا الرابط لمعرفة لماذا تعيد الداله getch قيمة من نوع int و ليس char.

و الله ولي التوفيق

تم تعديل هذه المشاركة بواسطة محمد علاء الدين في 6 مارس 2012 في 01:30

1

مدونتي: C++ Tips and Tricks

#9

جميل جدا, يمكننا القول أن هذه الدالة تُحاكي getchar في طريقة الإدخال .. لأنها تأخذ الحرف الأول فقط من المُدخلات كما أنها تحاكي getch أيضا في عدم ظهور المدخلات عند إجراء عملية الإدخال. و بالتالي فالدالة الجديدة لا تحاكي getch بصفة دقيقة.

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

اقتباس
راجع هذا الرابط لمعرفة لماذا تعيد الداله getch قيمة من نوع int و ليس char.

الرابط يحتوي على شرح الدالة fgetc و ليست getch ! :)

#10

السلام عليكم

فى جميع الحالات تعيد الدالة عدد صحيح يدل على الحرف المدخل عن طريق جدول ASCII

ويتضح هذا من تعريف مدى المتغيرات:

مدى المتغير من نوع char من 128- إلى 127 إذاً فهذا لا يصلح للتعبير عن الرموز فى جدول ASCII , ويشغل حجم 1byte

مدى المتغير من نوع unsigned char هو من 0 إلى 255 , ويشغل حجم 1byte

ومدى المتغير من نوع 2,147,438,648 - إلى 2,147,438,647 , ويشغل حجم 4bytes

وهذا يدل على أن القيم المخزنة بتلك المتغيرات هى أعداد صحيحة ولكن تختلف فى المدى والحجم الذى تشغله.

لاتحسبن العلم ينفع وحده مالم يتوج ربه بخلاق

لا إله إلا أنت سبحانك إني كنت من الظالمين

مواضيع مشابهة