Dodawanie zmiennych
Zapisze Pan/Pani wartości i odczyta je później.
Dodawanie zmiennych to bezpłatna lekcja C Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C Academy zawiera 4 lekcji w sumie.
Poza arytmetykę
Aby obsługiwać x = 5, a później x + 1, interpreter potrzebuje identyfikatorów oraz miejsca do przechowywania ich wartości. Tym miejscem jest środowisko.
Rozszerzymy lekser, parser i ewaluator tak, aby obsługiwały nazwy i przypisania.
Lekserowanie identyfikatorów
Identyfikator zaczyna się od litery lub znaku podkreślenia, a następnie może zawierać litery, cyfry lub znaki podkreślenia. Kopiujemy nazwę do tokenu.
Bufor o stałym rozmiarze upraszcza przykład; rzeczywiste leksery internują nazwy.
#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;
}Węzeł zmiennej
AST zyskuje dwa rodzaje węzłów: odwołanie do zmiennej i przypisanie. Każdy przechowuje nazwę zmiennej.
Przypisanie przechowuje również wyrażenie, którego wartość zapisuje.
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;Środowisko
Środowisko mapuje nazwy na wartości. W przypadku małego interpretera wystarczy prosta tablica par nazwa/wartość.
Większe języki używają tablic haszujących do wyszukiwania w czasie O(1) oraz zagnieżdżonych zakresów.
#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 */Ustawianie zmiennej
Przypisanie aktualizuje istniejący slot albo dopisuje nowy. Wyszukiwanie liniowe pozwala zachować krótki kod.
Zwracanie zapisanej wartości pozwala używać przypisań jako wyrażeń, na przykład 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;
}Odczytywanie zmiennej
Wyszukiwanie przegląda nazwy i zwraca wartość znalezionej zmiennej. Odwołanie do niezdefiniowanej zmiennej jest błędem wykonania.
Wykrycie tego błędu w tym miejscu pozwala wyświetlić jasny komunikat zamiast zwracać przypadkową wartość.
#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);
}Parsowanie przypisania
Przypisanie ma najniższy priorytet i jest prawostronnie łączne. Parsujemy czynnik; jeśli jest nim samodzielny identyfikator, po którym następuje =, tworzymy węzeł assign.
W przeciwnym razie identyfikator jest po prostu odczytem zmiennej.
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();
}Obliczanie zmiennych
Ewaluator zyskuje dwa przypadki. Węzeł zmiennej wyszukuje jej nazwę w środowisku, a węzeł assign oblicza wyrażenie wartości i zapisuje je.
Oba przypadki ponownie wykorzystują funkcje pomocnicze env_get i 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;
}Działający krok REPL
Ten program zapisuje zmienną, a następnie odczytuje ją z powrotem w wyrażeniu — wszystko za pośrednictwem środowiska. Odwzorowuje jeden wiersz rzeczywistego interpretera.
Uruchom program, aby zobaczyć współpracę przypisywania i wyszukiwania.
#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;
}Zakresy i przesłanianie
Pojedyncze płaskie środowisko jest globalne. Rzeczywiste języki dodają zagnieżdżone zakresy, aby zmienne lokalne funkcji nie nadpisywały nazw z zewnętrznych zakresów.
Implementuje się to przez łańcuchowe łączenie środowisk: wyszukiwanie przechodzi od najbardziej wewnętrznego zakresu na zewnątrz.
typedef struct Env {
char names[MAX_VARS][32];
int values[MAX_VARS];
int count;
struct Env *parent; /* enclosing scope */
} Env;Co dalej
Mając zmienne, można dodawać instrukcje, instrukcje warunkowe i funkcje. Każdy z tych elementów to nowy rodzaj węzła oraz odpowiadający mu przypadek w eval.
Od tego miejsca ten sam potok lekser–parser–ewaluator można rozbudować do rzeczywistego małego języka.
Szybkie sprawdzenie
Zastanów się, za co odpowiada środowisko.
Podsumowanie
Dodali Państwo identyfikatory do leksera, węzły var/assign do AST, środowisko z operacjami set i get oraz przypadki eval dla odczytów i przypisań.
Interpreter pamięta teraz stan — to podstawa obsługi instrukcji i funkcji.
Często zadawane pytania
Czy lekcja „Dodawanie zmiennych” jest bezpłatna?
Tak — pełny tekst „Dodawanie zmiennych” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C Academy, przejdź na CoddyKit PRO. Kurs C Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Dodawanie zmiennych”?
Zapisze Pan/Pani wartości i odczyta je później. Ćwiczysz C Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C Academy?
Nie wymagamy żadnego doświadczenia. C Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Dodawanie zmiennych”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C Academy?
Tak. Każda lekcja C Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Tokenizacja danych wejściowych
- Parsowanie wyrażeń
- Ewaluacja drzewa
- Dodawanie zmiennych