Skip to content

Commit 0b1aed1

Browse files
committed
feat(c-lessons): fix lesson bugs, add C-specific Flow view rendering
- Fix c-1-6: Korean text in English lesson (최종 → Final) - Fix c-3-5: unify pointer address/type, add per-iteration steps - Fix c-5-6: retain stack vars after free() with dangling marker - Improve c-6-4: add structMembers arrays for struct chip rendering - Improve c-4-2: add frame markers to all steps for consistency - CTransformer: position-based frame tracking, pass structMembers/ charElements/dangling to FlowVariable metadata - VariableBox: render dangling badge, struct member chips, char cells
1 parent c7ddde2 commit 0b1aed1

7 files changed

Lines changed: 1097 additions & 17 deletions

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
{
2+
"lessonId": "c-1-6",
3+
"title": "Loops (while)",
4+
"concept": "The while loop repeats as long as the condition is true. It's ideal when the number of iterations isn't predetermined.",
5+
"content": {
6+
"code": "#include <stdio.h>\n\nint main() {\n int n = 1;\n\n while (n <= 4) {\n printf(\"n = %d\\n\", n);\n n = n * 2;\n }\n\n printf(\"Final n = %d\\n\", n);\n\n return 0;\n}\n",
7+
"steps": [
8+
{
9+
"title": "Starting the main Function",
10+
"explanation": "The main stack frame is created when the program starts.\n\nThis lesson focuses on understanding how a while loop progresses and terminates through condition checks and value updates.",
11+
"visualizationType": "cMemory",
12+
"code": "int main() {",
13+
"stack": [
14+
{
15+
"frame": "main",
16+
"name": "main",
17+
"value": "main"
18+
}
19+
],
20+
"heap": [],
21+
"stdout": ""
22+
},
23+
{
24+
"title": "Setting the Initial Value",
25+
"explanation": "`int n = 1;` prepares the variable for the while loop outside the loop body.\n\nSince while requires you to manually write initialization and updates, it's important to clearly set the starting value.",
26+
"code": "int n = 1;",
27+
"stack": [
28+
{
29+
"frame": "main",
30+
"name": "main",
31+
"value": "main"
32+
},
33+
{
34+
"name": "n",
35+
"type": "int",
36+
"value": "1",
37+
"highlight": true,
38+
"address": "0x1000"
39+
}
40+
]
41+
},
42+
{
43+
"title": "while Condition Check (n = 1)",
44+
"explanation": "First checks whether `n <= 4` is true, then executes the body if it is.\n\nSince while is a condition-driven loop, forgetting to update the condition variable easily leads to an infinite loop.",
45+
"code": "while (n <= 4) {"
46+
},
47+
{
48+
"title": "Iteration 1: Print Current Value",
49+
"explanation": "Prints the current value of `n` to track the loop's progress.\n\nIn while loops, these intermediate outputs are useful for debugging the flow.",
50+
"code": "printf(\"n = %d\\n\", n);",
51+
"stdout": "n = 1"
52+
},
53+
{
54+
"title": "Iteration 1: Double n",
55+
"explanation": "`n = n * 2;` changes n from 1 to 2. In a while loop, this kind of update code is essential.\n\nWithout the update, the condition stays true forever and creates an infinite loop.",
56+
"code": "n = n * 2;",
57+
"stack": [
58+
{
59+
"frame": "main",
60+
"name": "main",
61+
"value": "main"
62+
},
63+
{
64+
"name": "n",
65+
"type": "int",
66+
"value": "2",
67+
"highlight": true,
68+
"address": "0x1000"
69+
}
70+
]
71+
},
72+
{
73+
"title": "Iteration 2: Condition Check (n = 2)",
74+
"explanation": "After the update, `n=2` so the condition is still true and the next iteration proceeds.\n\nThe flow repeats: `condition check -> execute body -> update value`.",
75+
"code": "n = n * 2;",
76+
"stack": [
77+
{
78+
"frame": "main",
79+
"name": "main",
80+
"value": "main"
81+
},
82+
{
83+
"name": "n",
84+
"type": "int",
85+
"value": "4",
86+
"highlight": true,
87+
"address": "0x1000"
88+
}
89+
],
90+
"stdout": "n = 1\nn = 2"
91+
},
92+
{
93+
"title": "Iteration 3: Condition Check (n = 4)",
94+
"explanation": "At `n=4`, `n <= 4` is still true so the body executes one more time.\n\nAfter this update, n becomes 8, which will terminate the loop at the next check.",
95+
"code": "n = n * 2;",
96+
"stack": [
97+
{
98+
"frame": "main",
99+
"name": "main",
100+
"value": "main"
101+
},
102+
{
103+
"name": "n",
104+
"type": "int",
105+
"value": "8",
106+
"highlight": true,
107+
"address": "0x1000"
108+
}
109+
],
110+
"stdout": "n = 1\nn = 2\nn = 4"
111+
},
112+
{
113+
"title": "Loop Terminates (n = 8)",
114+
"explanation": "When `n=8`, `8 <= 4` is false so the while loop exits.\n\nNote that upon loop exit, the variable's value may not be the \"last executed value\" but rather the \"value that broke the condition\".",
115+
"code": "while (n <= 4) {"
116+
},
117+
{
118+
"title": "Printing Final n",
119+
"explanation": "Prints the final value of `n` after the loop ends.\n\nThis confirms that the exit value isn't the last printed value, but rather the value after the condition was broken.",
120+
"code": "printf(\"Final n = %d\\n\", n);",
121+
"stdout": "n = 1\nn = 2\nn = 4\nFinal n = 8"
122+
},
123+
{
124+
"title": "Program Exit",
125+
"explanation": "`return 0;` terminates the program normally.\n\nThe key takeaway from this example is that in a while loop, you must manage the condition and update yourself.",
126+
"code": "return 0;",
127+
"stack": []
128+
}
129+
],
130+
"deltaFormat": true
131+
},
132+
"quiz": {
133+
"question": "How does while (1) { ... } behave?",
134+
"options": [
135+
"Executes once",
136+
"Does not execute",
137+
"Infinite loop",
138+
"Compilation error"
139+
],
140+
"correctIndex": 2,
141+
"explanation": "In C, 1 is true and 0 is false.\n\n`while (1)` means the condition is always true, so it loops infinitely.\nYou need a `break` statement to exit."
142+
},
143+
"misconceptions": [
144+
{
145+
"wrong": "A while loop executes at least once even if the condition is false",
146+
"correct": "If the condition is false from the start, while never executes.",
147+
"why": "The loop that guarantees at least one execution is do-while.\nwhile checks the condition before entering the body."
148+
}
149+
],
150+
"keyTakeaway": "The while loop is for 'when you don't know when it will end' — it keeps repeating as long as the condition is true!"
151+
}
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
{
2+
"lessonId": "c-3-5",
3+
"title": "Traversing with Pointers",
4+
"concept": "Instead of an index (i), you can move a pointer (p) to traverse an array",
5+
"content": {
6+
"code": "#include <stdio.h>\n\nint main() {\n int arr[] = {10, 20, 30, -1};\n int *p = arr;\n\n while (*p != -1) {\n printf(\"%d \", *p);\n p++;\n }\n\n return 0;\n}",
7+
"steps": [
8+
{
9+
"title": "Include header",
10+
"explanation": "`#include <stdio.h>` provides access to output functions.\n\nThis lesson covers the pattern of traversing an array using pointer movement (`p++`) instead of an index.",
11+
"visualizationType": "cMemory",
12+
"code": "#include <stdio.h>",
13+
"stack": [],
14+
"heap": [],
15+
"stdout": ""
16+
},
17+
{
18+
"title": "Start of main function",
19+
"explanation": "After creating the main frame, the array and pointer are prepared.\n\nNext, `p` will be moved one slot at a time to read values.",
20+
"visualizationType": "cMemory",
21+
"code": "int main() {",
22+
"stack": [
23+
{
24+
"name": "main",
25+
"type": "frame",
26+
"func": "main",
27+
"value": "frame"
28+
}
29+
]
30+
},
31+
{
32+
"title": "Array declaration",
33+
"explanation": "The `-1` at the end of `arr` is a sentinel value that signals the end of traversal.\n\nUsing a sentinel lets you determine the end without needing a separate length variable.",
34+
"visualizationType": "cMemory",
35+
"code": "int arr[] = {10, 20, 30, -1};",
36+
"stack": [
37+
{
38+
"name": "arr",
39+
"value": "{10, 20, 30, -1}",
40+
"type": "int[4]",
41+
"address": "0x1000"
42+
}
43+
]
44+
},
45+
{
46+
"title": "Pointer initialization",
47+
"explanation": "`p = arr` makes `p` point to the first element.\n\nNow in the loop, we can read `*p` and move to the next element with `p++`.",
48+
"visualizationType": "cMemory",
49+
"code": "int *p = arr;",
50+
"stack": [
51+
{
52+
"name": "arr",
53+
"value": "{10, 20, 30, -1}",
54+
"type": "int[4]",
55+
"address": "0x1000"
56+
},
57+
{
58+
"name": "p",
59+
"value": "0x1000",
60+
"type": "int*",
61+
"address": "0x1010",
62+
"points_to": "arr"
63+
}
64+
]
65+
},
66+
{
67+
"title": "Condition check: *p is 10 (not -1)",
68+
"explanation": "`*p` is 10, which is not `-1`, so the loop body executes.\n\nThe pointer starts at `arr[0]` (address `0x1000`).",
69+
"visualizationType": "cMemory",
70+
"code": "while (*p != -1) {",
71+
"stack": [
72+
{
73+
"name": "arr",
74+
"value": "{10, 20, 30, -1}",
75+
"type": "int[4]",
76+
"address": "0x1000"
77+
},
78+
{
79+
"name": "p",
80+
"value": "0x1000",
81+
"type": "int*",
82+
"address": "0x1010",
83+
"highlight": true,
84+
"points_to": "arr"
85+
}
86+
]
87+
},
88+
{
89+
"title": "Iteration 1: Print *p (10)",
90+
"explanation": "`*p` dereferences the pointer to read the value at `0x1000`, which is `10`.\n\nThe pointer reads the value without modifying the array.",
91+
"visualizationType": "cMemory",
92+
"code": "printf(\"%d \", *p);",
93+
"stdout": "10 "
94+
},
95+
{
96+
"title": "Iteration 1: p++ moves to next element",
97+
"explanation": "`p++` advances the pointer by `sizeof(int)` (4 bytes): `0x1000` -> `0x1004`.\n\nNow `p` points to `arr[1]` which holds `20`.",
98+
"visualizationType": "cMemory",
99+
"code": "p++;",
100+
"stack": [
101+
{
102+
"name": "arr",
103+
"value": "{10, 20, 30, -1}",
104+
"type": "int[4]",
105+
"address": "0x1000"
106+
},
107+
{
108+
"name": "p",
109+
"value": "0x1004",
110+
"type": "int*",
111+
"address": "0x1010",
112+
"highlight": true
113+
}
114+
]
115+
},
116+
{
117+
"title": "Condition check: *p is 20 (not -1)",
118+
"explanation": "`*p` is now `20`, so the loop continues.\n\nEach iteration: check condition -> print -> advance pointer.",
119+
"visualizationType": "cMemory",
120+
"code": "while (*p != -1) {",
121+
"occurrence": 2
122+
},
123+
{
124+
"title": "Iteration 2: Print *p (20)",
125+
"explanation": "Prints the value at `0x1004`, which is `20`.",
126+
"visualizationType": "cMemory",
127+
"code": "printf(\"%d \", *p);",
128+
"occurrence": 2,
129+
"stdout": "10 20 "
130+
},
131+
{
132+
"title": "Iteration 2: p++ moves to next element",
133+
"explanation": "`p++` advances from `0x1004` to `0x1008`.\n\nNow `p` points to `arr[2]` which holds `30`.",
134+
"visualizationType": "cMemory",
135+
"code": "p++;",
136+
"occurrence": 2,
137+
"stack": [
138+
{
139+
"name": "arr",
140+
"value": "{10, 20, 30, -1}",
141+
"type": "int[4]",
142+
"address": "0x1000"
143+
},
144+
{
145+
"name": "p",
146+
"value": "0x1008",
147+
"type": "int*",
148+
"address": "0x1010",
149+
"highlight": true
150+
}
151+
]
152+
},
153+
{
154+
"title": "Condition check: *p is 30 (not -1)",
155+
"explanation": "`*p` is `30`, so the loop continues for one more iteration.",
156+
"visualizationType": "cMemory",
157+
"code": "while (*p != -1) {",
158+
"occurrence": 3
159+
},
160+
{
161+
"title": "Iteration 3: Print *p (30)",
162+
"explanation": "Prints the value at `0x1008`, which is `30`. This is the last valid element.",
163+
"visualizationType": "cMemory",
164+
"code": "printf(\"%d \", *p);",
165+
"occurrence": 3,
166+
"stdout": "10 20 30 "
167+
},
168+
{
169+
"title": "Iteration 3: p++ moves past last element",
170+
"explanation": "`p++` advances from `0x1008` to `0x100c`.\n\nNow `p` points to `arr[3]` which holds the sentinel value `-1`.",
171+
"visualizationType": "cMemory",
172+
"code": "p++;",
173+
"occurrence": 3,
174+
"stack": [
175+
{
176+
"name": "arr",
177+
"value": "{10, 20, 30, -1}",
178+
"type": "int[4]",
179+
"address": "0x1000"
180+
},
181+
{
182+
"name": "p",
183+
"value": "0x100c",
184+
"type": "int*",
185+
"address": "0x1010",
186+
"highlight": true
187+
}
188+
]
189+
},
190+
{
191+
"title": "Loop exits: *p is -1 (sentinel found)",
192+
"explanation": "`*p` is now `-1`, so `*p != -1` is false and the loop terminates.\n\nThe sentinel value successfully stopped the traversal at the right point.",
193+
"visualizationType": "cMemory",
194+
"code": "while (*p != -1) {",
195+
"occurrence": 4
196+
},
197+
{
198+
"title": "Program exit",
199+
"explanation": "`return 0;` terminates the program.\n\nThe key pattern: read (`*p`) + move (`p++`) + sentinel termination condition.",
200+
"visualizationType": "cMemory",
201+
"code": "return 0;",
202+
"stack": []
203+
}
204+
],
205+
"deltaFormat": true
206+
},
207+
"quiz": {
208+
"question": "What should you watch out for when traversing an array with a pointer p?",
209+
"options": [
210+
"It's slower than using an index",
211+
"There's a risk of going out of bounds",
212+
"p++ only moves by 1 byte",
213+
"It cannot be used"
214+
],
215+
"correctIndex": 1,
216+
"explanation": "Pointers have no brakes.\nIf you get the termination condition wrong (reaching `length` or `NULL` character), it keeps reading past the end of the array, causing memory errors."
217+
},
218+
"misconceptions": [
219+
{
220+
"wrong": "Modern compilers make pointer traversal (p++) much faster than indexing (arr[i])",
221+
"correct": "That used to be true, but nowadays compiler optimizations make them about the same.",
222+
"why": "For readability, using indexing (`arr[i]`) is often the better choice.\nThere's no need to force pointer usage."
223+
}
224+
],
225+
"keyTakeaway": "Traversing an array with `p++` is a classic C pattern. But beware of the cliff (out-of-bounds access)."
226+
}

0 commit comments

Comments
 (0)