0Pricing
C Academy · Lezione

Aggiungere variabili

Salvi e recuperi i valori.

Aggiungere variabili è una lezione C Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C Academy include 4 lezioni in totale.

Oltre l'aritmetica

Per supportare x = 5 e in seguito x + 1, l'interprete ha bisogno degli identificatori e di un luogo in cui memorizzarne i valori. Questo luogo è l'ambiente.

Estenderemo lexer, parser e valutatore per gestire nomi e assegnamenti.

Analizzare gli identificatori

Un identificatore inizia con una lettera o un trattino basso e prosegue con lettere, cifre o trattini bassi. Copiamo il nome nel token.

Un buffer di dimensione fissa mantiene semplice l'esempio; i lexer reali internano i nomi.

#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;
}

Un nodo variabile

L'AST acquisisce due tipi di nodo: un riferimento a una variabile e un assegnamento. Ognuno memorizza il nome della variabile.

L'assegnamento conserva anche l'espressione di cui memorizza il valore.

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;

L'ambiente

L'ambiente associa i nomi ai valori. Per un piccolo interprete è sufficiente un semplice array lineare di coppie nome/valore.

I linguaggi più grandi utilizzano tabelle hash per una ricerca in O(1) e gli scope annidati.

#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 */

Impostare una variabile

Un assegnamento aggiorna uno slot esistente oppure ne aggiunge uno nuovo. La ricerca lineare mantiene il codice breve.

Restituire il valore memorizzato consente di usare gli assegnamenti come espressioni, ad esempio 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;
}

Leggere una variabile

La ricerca scorre i nomi e ne restituisce il valore. Fare riferimento a una variabile non definita è un errore di runtime.

Rilevarlo qui consente di mostrare un messaggio chiaro invece di restituire un valore casuale.

#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);
}

Analizzare un assegnamento

L'assegnamento ha la precedenza più bassa ed è associativo a destra. Analizziamo un fattore; se è un identificatore isolato seguito da =, costruiamo un nodo assign.

Altrimenti l'identificatore rappresenta semplicemente una lettura della variabile.

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();
}

Valutare le variabili

Il valutatore acquisisce due casi. Un nodo variabile cerca il proprio nome nell'ambiente; un nodo assign valuta la propria espressione di valore e la memorizza.

Entrambi riutilizzano gli helper env_get e 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;
}

Un passaggio REPL funzionante

Questo programma memorizza una variabile, poi la legge all'interno di un'espressione, il tutto tramite l'ambiente. Modella una riga di un interprete reale.

Esegua il programma per vedere come assegnamento e ricerca collaborano.

#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;
}

Scope e shadowing

Un ambiente piatto unico è globale. I linguaggi reali aggiungono scope annidati, così le variabili locali di una funzione non sovrascrivono i nomi esterni.

Si implementa concatenando gli ambienti: la ricerca procede dallo scope più interno verso l'esterno.

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

Il prossimo passo

Con le variabili disponibili, è possibile aggiungere istruzioni, condizioni e funzioni. Ognuno di questi elementi è un nuovo tipo di nodo più un caso in eval.

Da qui, la stessa pipeline lexer-parser-valutatore può crescere fino a diventare un vero piccolo linguaggio.

Verifica rapida

Rifletta sulle responsabilità dell'ambiente.

Riepilogo

Ha aggiunto gli identificatori al lexer, i nodi var/assign all'AST, un ambiente con set e get e i casi di eval per le letture e gli assegnamenti.

L'interprete ora conserva lo stato: è la base per istruzioni e funzioni.

Domande Frequenti

La lezione «Aggiungere variabili» è gratuita?

Sì — il testo completo di «Aggiungere variabili» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C Academy, passa a CoddyKit PRO. Il corso C Academy include 4 lezioni in totale.

Cosa imparerò in «Aggiungere variabili»?

Salvi e recuperi i valori. Eserciti C Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C Academy?

Non è richiesta alcuna esperienza precedente. C Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Aggiungere variabili»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C Academy?

Sì. Ogni lezione C Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Tokenizzare l'input
  2. Analizzare le espressioni
  3. Valutare l'albero
  4. Aggiungere variabili
← Torna a C Academy