0Pricing
C Academy · درس

إضافة المتغيرات

خزّن القيم واسترجعها

إضافة المتغيرات درس مجاني في C Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C Academy 4 دروس في المجموع.

ما بعد الحساب

لدعم x = 5 ثم x + 1 لاحقًا، يحتاج المفسر إلى معرّفات ومكان لتخزين قيمها. ويُسمى هذا المخزن البيئة.

سنوسع المحلل المعجمي والمحلل النحوي والمقيّم للتعامل مع الأسماء والإسناد.

تحليل المعرّفات معجميًا

يبدأ المعرّف بحرف أو شرطة سفلية، ثم يتابع بحروف أو أرقام أو شرطات سفلية. وننسخ الاسم إلى الرمز.

يبقي المخزن المؤقت ثابت الحجم المثال بسيطًا؛ أما المحللات المعجمية الحقيقية فتستخدم interning للأسماء.

#include <ctype.h>
#include <string.h>

typedef struct { TokKind kind; int value; char name[32]; } Token;

static Token lex_ident(void) {
  Token t; t.kind = TOK_IDENT; int i = 0;
  while (isalnum((unsigned char)peek()) || peek() == '_')
    if (i < 31) t.name[i++] = advance(); else advance();
  t.name[i] = '\0';
  return t;
}

عقدة متغير

تكتسب AST نوعين من العقد: مرجع متغير وعملية إسناد. ويخزن كل منهما اسم المتغير.

ويحتفظ الإسناد أيضًا بالتعبير الذي يخزن قيمته.

typedef struct Node {
  enum { N_NUM, N_BINOP, N_VAR, N_ASSIGN } kind;
  int value; char op; char name[32];
  struct Node *left, *right;  /* assign uses right as value expr */
} Node;

البيئة

تربط البيئة الأسماء بالقيم. وتكفي مصفوفة خطية بسيطة من أزواج الاسم والقيمة لمفسر صغير.

أما اللغات الأكبر فتستخدم جداول تجزئة للبحث بزمن O(1) ونطاقات متداخلة.

#define MAX_VARS 64

typedef struct {
  char names[MAX_VARS][32];
  int  values[MAX_VARS];
  int  count;
} Env;

static Env env;  /* global for this tiny example */

تعيين متغير

يؤدي الإسناد إما إلى تحديث خانة موجودة أو إلى إضافة خانة جديدة. ويحافظ البحث الخطي على قِصر الشيفرة.

ويتيح إرجاع القيمة المخزنة استخدام الإسنادات كتعبيرات، مثل y = (x = 3).

#include <string.h>

static int env_set(const char *name, int v) {
  for (int i = 0; i < env.count; i++)
    if (strcmp(env.names[i], name) == 0) {
      env.values[i] = v; return v;
    }
  strcpy(env.names[env.count], name);
  env.values[env.count] = v;
  env.count++;
  return v;
}

قراءة متغير

يبحث الاستعلام عن الاسم ويعيد قيمته. ويُعد الرجوع إلى متغير غير معرّف خطأً أثناء التشغيل.

ويؤدي اكتشاف ذلك هنا إلى عرض رسالة واضحة بدلًا من إعادة قيمة عشوائية.

#include <stdio.h>
#include <stdlib.h>

static int env_get(const char *name) {
  for (int i = 0; i < env.count; i++)
    if (strcmp(env.names[i], name) == 0)
      return env.values[i];
  fprintf(stderr, "undefined variable: %s\n", name);
  exit(1);
}

تحليل الإسناد

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

أما إذا لم يتحقق ذلك، فيكون المعرّف مجرد قراءة لمتغير.

static Node *parse_assign(void) {
  if (cur().kind == TOK_IDENT) {
    char saved[32]; strcpy(saved, cur().name);
    bump();
    if (cur().kind == TOK_ASSIGN) {
      bump();
      Node *val = parse_assign();
      return assign_node(saved, val);
    }
    return var_node(saved);  /* not an assignment */
  }
  return parse_expr();
}

تقييم المتغيرات

يضيف المقيّم حالتين. تبحث عقدة المتغير عن اسمه في البيئة، بينما تقيّم عقدة الإسناد تعبير القيمة وتخزنه.

وتعيد كلتاهما استخدام الدالتين المساعدتين env_get وenv_set.

int eval(Node *n) {
  switch (n->kind) {
    case N_NUM:    return n->value;
    case N_VAR:    return env_get(n->name);
    case N_ASSIGN: return env_set(n->name, eval(n->right));
    case N_BINOP: {
      int l = eval(n->left), r = eval(n->right);
      switch (n->op) {
        case '+': return l + r; case '-': return l - r;
        case '*': return l * r; case '/': return l / r;
      }
    }
  }
  return 0;
}

خطوة عاملة في REPL

يخزن هذا البرنامج متغيرًا، ثم يقرأه مجددًا ضمن تعبير، وكل ذلك عبر البيئة. وهو يحاكي سطرًا واحدًا من مفسر حقيقي.

شغّلوه لتروا كيف يتعاون الإسناد والبحث.

#include <stdio.h>
#include <string.h>

#define MAX_VARS 64
static char names[MAX_VARS][32];
static int  values[MAX_VARS];
static int  count;

static int env_set(const char *n,int v){
  for(int i=0;i<count;i++) if(!strcmp(names[i],n)){values[i]=v;return v;}
  strcpy(names[count],n); values[count]=v; count++; return v;
}
static int env_get(const char *n){
  for(int i=0;i<count;i++) if(!strcmp(names[i],n)) return values[i];
  return 0;
}

int main(void){
  env_set("x", 5);            /* x = 5 */
  int r = env_get("x") + 1;   /* x + 1 */
  printf("x = %d\n", env_get("x"));
  printf("x + 1 = %d\n", r);
  return 0;
}

النطاقات وحجب الأسماء

تكون البيئة المسطحة الواحدة عامة. وتضيف اللغات الحقيقية نطاقات متداخلة حتى لا تغيّر المتغيرات المحلية في الدالة أسماء النطاقات الخارجية.

يمكن تنفيذ ذلك بربط البيئات؛ إذ ينتقل البحث من النطاق الداخلي إلى الخارج.

typedef struct Env {
  char names[MAX_VARS][32];
  int  values[MAX_VARS];
  int  count;
  struct Env *parent;  /* enclosing scope */
} Env;

إلى أين بعد ذلك؟

بعد إضافة المتغيرات، يمكنكم إضافة العبارات والشروط والدوال. ويمثل كل منها نوعًا جديدًا من العقد مع حالة eval خاصة به.

ومن هنا يمكن لخط أنابيب المحلل المعجمي والمحلل النحوي والمقيّم نفسه أن يتوسع ليصبح لغة صغيرة حقيقية.

تحقق سريع

فكّروا في المسؤوليات التي تتولاها البيئة.

مراجعة

أضفتم المعرّفات إلى المحلل المعجمي، وعقدتي var/assign إلى AST، وبيئةً مع set وget، وحالات eval للقراءة والإسناد.

يتذكر المفسر الآن حالته، وهذا أساس العبارات والدوال.

الأسئلة الشائعة

هل درس «إضافة المتغيرات» مجاني؟

نعم — نص درس «إضافة المتغيرات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C Academy 4 دروس في المجموع.

ماذا ستتعلم في «إضافة المتغيرات»؟

خزّن القيم واسترجعها تتمرن على C Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ C Academy؟

لا تُشترط خبرة سابقة. C Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «إضافة المتغيرات»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس C Academy هذا؟

نعم. كل درس في C Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تحويل الإدخال إلى رموز
  2. تحليل التعبيرات
  3. تقييم الشجرة
  4. إضافة المتغيرات
← العودة إلى C Academy