CS50x : Week 1 - C
เขียนวันที่ : Apr 3, 2022
เนื้อหา Week 1 - C เริ่มเรียนการเขียนโปรแกรมจริงๆ ด้วยการเขียนภาษาซี (C Programming) สำหรับบทเรียนนี้ มือใหม่ หรือคนที่ไม่เคยหัดเขียนโปรแกรม อาจจะงงๆ กับการใช้งานโปรแกรมได้ และผมมีเพิ่มเนื้อหาบางส่วน ที่คิดว่า หลายๆ คนน่าจะติดปัญหา
จริงๆแล้วถ้าติดปัญหาส่วนไหน พยายามลองแก้ปัญหา ลองค้นหาเพิ่มเติมก่อนครับ ค่อยกลับมาดูใหม่ เช่น VS Code หากยังไม่มี ก็ลองไปดาวน์โหลด และติดตั้งดูก่อน ครับ หรือตอนอาจารย์สอนพวก Command Line เบื้องต้น ก็ลองฝึกทำตาม ฝึกหลายๆแบบดูครับ
และเนื้อหาเอาจริงๆ ก็คือเยอะมากๆ วันแรก ไม่เข้าใจ ไม่แปลกครับ พยายามทบทวน ค่อยๆ อ่าน ค่อยๆเรียนไป เนื้อหาพวกนี้ ถ้ารู้ไว้ เราก็สามารถต่อยอดในหลายๆ ภาษาได้ไม่ยากครับ (ใกล้เคียงกัน)
หลายๆครั้ง ถ้าเรา compile แล้วมี Error หรือ Text Editor / IDEs มีแจ้งเตือน หรือ warning พยายามลองอ่าน Error ที่มันขึ้นบอกก่อนนะครับ ถ้าเราอ่าน error เข้าใจ จะช่วยให้เราเข้าใจการทำงานของโปรแกรมได้ดีขึ้น
References
#include <stdio.h>
int main(void)
{
printf("hello, world\n");
}
C, IDE และ Compiler
- เป้าหมายการเรียนคือ ไม่ใช่การเรียนภาษาใดภาษาหนึ่ง แต่คือเรียนเขียนโปรแกรม (How to program) ซึ่งถ้าเรามีพื้นฐานแล้ว เราสามารถไปต่อยอดภาษา Programming อื่นๆได้ครับ
- Compiler เป็นตัวที่เอาไว้แปลงโค๊ดของเรา (Source Code) ให้เป็นภาษาเครื่อง (Machine Code) เพราะคอมรู้จักแค่ 0 กับ 1 (binary)
- Visual Studio Code เป็น Text Editor ที่เอาไว้เขียน source code
- สามารถใช้งาน VS Code เวอร์ชั่น Cloud Based ได้ครับ ใช้ Github ในการ login (อาจจะต้องลง Extension เสริม เช่น Codespace ต้องลองดูครับ ผมไม่ได้ลองส่วนนี้)
- ตัว VS Code จะมี Terminal อยู่ด้านล่าง หากไม่มีกด View -> Terminal เพื่อเปิด Terminal ขึ้นมา
- ใน Terminal เราจะสามารถใช้ Command Line Interface (CLI) ได้
make hello
เพื่อทำการ compile ไฟล์hello.c
ของเรา เป็น machine code (execution)./hello
- ให้คอมพิวเตอร์หาไฟล์ในโฟลเดอร์ปัจจุบัน (.) ที่ชื่อhello
และสั่งรันโปรแกรมhello
- เป็นไฟล์ที่ได้หลังจาก compiler แล้ว เป็นไฟล์ execution ที่เอาไว้รันstdio.h
คือ Header file เพื่อบอกให้ compiler ทำการ load library ในโปรแกรมเรา
คำสั่ง Linux เบื้องต้น
cd <directory>
- เอาไว้เปลี่ยน folder (directory) เช่นcd documents
ก็คือเหมือนกด file explorer โฟลเดอร์documents
cp
- คือการ copy filerm <file>
- เป็นคำสั่งที่เอาไว้ลบไฟล์rm
ย่อมาจาก remove จะได้จำง่ายๆls
- เป็นคำสั่งที่เอาไว้แสดงไฟล์ทั้งหมดใน folder ของเรา (ls คือ list directory contents)mkdir
- สร้างโฟลเดอร์ใหม่
อ่านเพิ่มเติมได้ Command Line พื้นฐานบน Ubuntu
วิธีการใช้ cs50.h
ร่วมกับ VS Code
สำหรับมือใหม่ VS Code ผมมีบทความเกี่ยวกับการใช้ VS Code บน Windows ครับ (ใช้ได้ทั้ง C และ C++) - เริ่มต้น C++ บน Windows
ในโค๊ดตัวอย่าง หรือในบทเรียน จะมีการใช้ library cs50.h
ถ้าใครใช้งานผ่าน CS50IDE หรือ Codespace ก็อาจจะไม่มีปัญหา
แต่ถ้าใครใช้ VS Code บนเครื่องตัวเอง มือใหม่อาจจะงงๆได้ครับ ก็เลยมาแนะนำ นิดนึงคือ
- ทำการดาวน์โหดลไฟล์ cs50.h และ cs50.c ทั้งสองไฟล์และก็อปไปวางไว้ที่เดียวกับไฟล์ที่เราเขียน เช่น
hello.c
ก็อยู่โฟลเดอร์เดียวกัน - จากนั้นที่ไฟล์
hello.c
ทำการเพิ่ม
#include "cs50.h"
ไฟล์ก็จะได้เป็นแบบนี้ (ลองใช้งาน get_string
)
#include <stdio.h>
#include "cs50.h"
int main(void)
{
string answer = get_string("What's your name?");
printf("hello, %s\n", answer);
}
สังเกตว่า
"cs50.h"
มี double quote นะครับ ต่างกับ<stdio.h>
เพราะ<stdio.h>
หมายถึง includes จาก folder ของ compiler ส่วน"cs50.h"
คือทำการ include ไฟล์ที่อยู่ในโฟลเดอร์
- ตอน compile ให้ใช้คำสั่ง option
cs50.c
ต่อท้ายด้วย
gcc hello.c -o hello cs50.C
- เมื่อ compile เรียบร้อย ก็ทำการกด รันโปรแกรม ตัวโปรแกรมจะรอรับ input จากเรา เมื่อเราพิมพ์คำลงไป กด Enter มันก็จะทำการ
hello <สิ่งที่เราพิมพ์>
ออกมา
./hello
IDE Online
สำหรับใครไม่ได้ติดตั้ง VS Code สามารถใช้ IDE Online ได้ เช่น
- https://ideone.com/
- https://www.programiz.com/c-programming/online-compiler/
- https://www.codechef.com/ide
Data Types
- เวลากำหนดตัวแปร เราต้องระบุ type ให้มันด้วย ซึ่ง type ในภาษาซี มีด้วยกันหลายแบบ
bool
- คือ boolean expression เป็นได้true
หรือfalse
char
- คือตัวอักษรตัวเดียว (ขนาด 1 byte)int
- คือตัวเลขปกติ (จำนวนเต็ม)long
- คือเหมือน int ที่เก็บค่าได้มากกว่า (จำนวน bit มากกว่า)float
- คือค่าพวกทศนิยมdouble
- เหมือน float แต่มีจุดทศนิยมได้มากกว่าstring
- กลุ่มคำ char หลายๆตัวรวมกัน- ส่วน operators ก็เหมือนคณิตศาสตร์ปกติเลยครับ มี
+ - * /
(คูณ ใช้*
) - และมี
%
(Modulo Operator) เอาไว้หาค่าเศษของการหาร เช่น5 mod 2
คือ 5 หาร 2 เหลือเศษ 1 ฉะนั้น5 % 2
เลยเท่ากับ1
printf
printf
- เป็น function ที่เอาไว้แสดง display ออกทางหน้าจอ เราสามารถกำหนดรูปแบบให้มันได้ (format code)%c
- สำหรับตัวแปรชนิดchar
%f
- สำหรับตัวแปรชนิดfloat
%i
- สำหรับตัวแปรชนิดint
%li
- สำหรับlong
(long integer)%s
- สำหรับstring
สังเกตวิธีการจำ format codes ง่ายๆ ก็คือ ตัวย่อของ data types ครับ
การประกาศตัวแปร ชื่อ counter
ให้มีค่าเท่ากับ 0
ทำได้ดังนี้
int counter = 0;
การเปลี่ยนค่า counter
มีค่าเท่ากับค่าตัวมันเอง +1
counter = counter + 1;
สามารถเขียนได้อีกรูปแบบ (syntactic sugar) มีค่าเท่ากันกับด้านบน
counter += 1;
เช่นกันโค๊ดด้านล่าง 2 บรรทัด เหมือนกัน
counter = counter + 5;
// เหมือนกัน
counter += 5;
และตัว counter++
หรือ counter--
ก็มีค่าเท่ากับ counter += 1
และ `counter -= 1 ครับ
Challenge: ลองไปหาข้อมูลดูว่า
counter++
และ++counter
ต่างกันยังไงนะ?
ตัวอย่างโปรแกรม Calculator
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int x = get_int("x: ");
int y = get_int("y: ");
printf("%i\n", x + y);
}
Condition เราสามารถเขียนแบบนี้ เพื่อเช็คเงื่อนไขว่า ถ้า x มากกว่า y ให้โปรแกรมทำงานในส่วน body {}
ด้านล่าง
if (x < y)
{
printf("x is less than y");
}
- If Else ชื่อเช็คเงื่อนไขเป็นจริงๆ ทำใน
if
ถ้าไม่จริง ทำในelse
if (x < y)
{
printf("x is less than y\n");
}
else
{
printf("x is not less than y\n");
}
- Else Ifเราสามารถเช็คหลายๆ เงื่อนไข ด้วย
else if
if (x < y)
{
printf("x is less than y\n");
}
else if (x > y)
{
printf("x is greater than y\n");
}
else if (x == y)
{
printf("x is equal to y\n");
}
ในภาษา C เวลาใช้เปรียบเทียบค่าจะใช้
==
เช่นif (x == y)
แต่ถ้าเป็น=
จะเป็นการประกาศและกำหนดค่าให้ตัวแปรconst
เป็นการบอกให้ compiler รู้ว่า ค่าของ variable (ตัวแปร) ของเราจะไม่เปลี่ยนค่าเราสามารถใช้ หรือ (or) (
||
), และ (and) (&&
) ใน condition ได้ แบบด้านล่าง
if (c == 'Y' || c == 'y')
ตัว bar
|
เป็นปุ่มที่อยู่บนปุ่ม Enter นะครับ
Loops, Function
- Loop คือการทำงานซ้ำๆ ของโปรแกรม
จากตัวอย่างโค๊ด เราให้มันแสดงคำว่า meow ออกมา 3 ครั้ง จะเป็นแบบนี้
#include <stdio.h>
int main(void)
{
printf("meow\n");
printf("meow\n");
printf("meow\n");
}
แล้วถ้าเกิดอยากให้มันแสดง 10 20 หรือ 100 ครั้ง ต้องมานั่งพิมพ์ซ้ำๆ 100 รอบหรือเปล่านะ?
while
เป็น loop ที่จะทำคำสั่งใน {}
จนกว่า condition เงื่อนไขจะเป็น false (เท็จ) โดยมีรูปแบบ แบบนี้
while (true)
{
printf("meow\n");
}
ด้านบน จะแสดง meow จนกว่า กว่า จะเป็น false แต่ในทีนี้มันเป็น true ตลอด ก็จะรันจนกว่าเราจะปิดโปรแกรม (แบบนี้เป็น infinity loop รันไม่มีวันจบ จนโปรแกรม crash สมัยผมเรียนปี 1 ก็ลืมบ่อยๆครับ 🤣)
ฉะนั้น การพิมพ์ meow 3 ครั้งด้วย while
จะเป็นแบบนี้
int counter = 0;
while (counter < 3)
{
printf("meow\n");
counter = counter + 1;
}
วิธีการคิดง่ายๆ
- เราเช็คก่อนว่า
counter
มากกว่า 3 มั้ย? คำตอบคือ มากกว่า เพราะ counter = 0 ตอนนี้ ฉะนั้นมันก็ทำงานใน{}
- ทำการ
printf()
บรรทัด 4 และ เพิ่มค่าcounter
+1 (ตอนนี้ counter = 1) จบรอบ ย้อนกลับไปเช็คwhile
อีกครั้ง - เช็ค while (counter < 3) หรือไม่? ใช่ มากกว่า เพราะ counter = 1 ตอนนี้
- ก็วน loop ทำงานใน function body
{}
เพิ่มค่า counter วนลูบ while จนกว่า เงื่อนไขจะเป็น false ก็จบการทำงาน
สามารถใช้ i++
แทน i = i + 1
ได้เหมือนกัน
int i = 0;
while (i < 3)
{
printf("meow\n");
i++;
}
- หลายๆครั้งมักใช้ตัวแปรชื่อ
i
สำหรับการ loop เป็น conventional variable name นะไม่ได้บังคับ
for loop
เป็น loop ที่เหมือนกับ while แต่ต่างกันที่ การกำหนดค่าเริ่มต้น, เงื่อนไข และ ค่าที่อัพเดท ทำใน for เลย ต่างจาก while ค่าเริ่มต้น กำหนดข้างนอก loop และเงื่อนไข อยู่ตรง while และ ค่าที่อัพเดท อยู่ใน body ของ loop
รูปแบบของ for loop เป็นแบบนีิ
for (initialization; condition; update) {
// body of-loop
}
และถ้าเขียนด้วย for loop จาก while loop ด้านบน ก็จะเป็นบบนี้
for (int i = 0; i < 3; i++)
{
printf("meow\n");
}
- คือ กำหนด
i=0
เริ่มต้น - เช็คเงื่อนไข ว่า
i<3
หรือไม่? - และ เพิ่มค่า
i++
เมื่อจบ loop 1 รอบ (เหมือน while loop ที่เพิ่มi++
หรือi = i +1
) int i = 0
ที่ตั้งขึ้นมาใน for loop จะใช้ได้แค่ scope ของ loop นี้นะครับ ใช้ข้างนอกไม่ได้
โปรแกรม แสดงข้อความ meow 3 ครั้งด้วย for loop:
#include <stdio.h>
int main(void)
{
for (int i = 0; i < 3; i++)
{
printf("meow\n");
}
}
function
เป็นคำสั่ง หรือชุดคำสั่งเพื่อทำงานบางอย่าง ที่เรากำหนด ถ้าเราสังเกต int main(void) {}
ก็คือ function main นั่นเอง
function meow ที่เป็น function ที่เอาไว้ printf
meow อย่างเดียว โดยไม่ได้รับค่าใดๆ และไม่ return ค่าใดๆ กลับไป
void meow(void)
{
printf("meow\n");
}
int main(void)
{
for (int i = 0; i < 3; i++)
{
meow();
}
}
- เวลาเรียก function ก็ใช้
()
วงเล็บเปิด / ปิด เช่นmeow();
ภาษาซีจะทำงานจากบน ลง ล่าง ถ้าเกิดเราไปเรียก meow()
ก่อนที่ compiler มันจะรู้จัก มันก็จะ error แบบตัวอย่างโค๊ดด้านล่าง void meow(void)
ถูกประกาศไว้หลัง function main ทำให้มันยังไม่รู้จักขณะเรียก
#include <stdio.h>
int main(void)
{
for (int i = 0; i < 3; i++)
{
meow();
}
}
void meow(void)
{
printf("meow\n");
}
- เราแก้ไขด้วยการทำ prototype function ครับ คือ function ที่ไม่มี body ข้างใน รู้แค่ function ชื่ออะไร รับค่าอะไร ส่งค่าอะไรกลับไป แบบนี้
void meow(void);
เมื่อรวมกับที่ error แล้ว ก็จะได้เป็นแบบนี้ compile ผ่านแล้ว
#include <stdio.h>
void meow(void);
int main(void)
{
for (int i = 0; i < 3; i++)
{
meow();
}
}
void meow(void)
{
printf("meow\n");
}
function สามารถรับค่าได้ และตอนเรียก function ก็ส่งค่าไปให้ function แบบนี้
#include <stdio.h>
void meow(int n);
int main(void)
{
meow(3);
}
void meow(int n)
{
for (int i = 0; i < n; i++)
{
printf("meow\n");
}
}
- จะเห็นว่า
void meow(int n)
เรากำหนดว่า function meow จะรับค่า n มา (เท่าไหร่ก็ได้ แล้วแต่ผู้เรียก function จะส่งมา) - ใน meow function ก็ for loop ตามจำนวน
n
เพื่อโชว์ meow - และตอนเรียก
meow(3)
ก็แค่ส่งจำนวนที่เราต้องการแสดง ในทีนี้คือ 3 ก็พิมพ์ meow 3 รอบ
Mario
ใช้ for loop ในการ print ?
#include <stdio.h>
int main(void)
{
for (int i = 0; i < 4; i++)
{
printf("?");
}
printf("\n");
}
สามารถรับค่า user input แล้วก็แสดง ?
ตามจำนวนที่ user กรอกได้แบบนี้
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int n;
do
{
n = get_int("Width: ");
}
while (n < 1);
for (int i = 0; i < n; i++)
{
printf("?");
}
printf("\n");
}
do while
เป็น loop อีกชนิด ที่มันการันตี ว่าจะต้องทำงานอย่างน้อย 1 ครั้ง แล้วก็เช็คเงื่อนไขในwhile(condition)
ถ้าจริง ก็ทำ loop เดิม ถ้าไม่ก็จบน loop ในตัวอย่าง คือรับค่า input จาก user ถ้า user พิมพ์ค่ามากกว่า 1 ก็จบการทำงาน และค่าn
ก็คือค่าที่ user พิมพ์มมาfor loop
ตามจำนวนn
ครั้ง เพื่อแสดง?
แบบยากขึ้น คือมองเป็น 2 มิติ (มีแนวตั้ง แนวนอน) (row/column)
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int n;
do
{
n = get_int("Size: ");
}
while (n < 1);
// For each row
for (int i = 0; i < n; i++)
{
// For each column
for (int j = 0; j < n; j++)
{
// Print a brick
printf("#");
}
// Move to next row
printf("\n");
}
}
ผลลัพธ์ด้านบนได้แบบนี้
Size: 3
###
###
###
เรื่อง loop หากใครยังเพิ่งหัดเขียนโปรแกรม แนะนำลองพิมพ์ ลองหัด ลองประยุกต์ใช้งาน จนกว่าจะคล่องนะครับ เพราะมันมีประโยชน์มากๆ เพราะมันอาจจะดูซับซ้อนในตอนแรก แต่เมื่อเข้าใจการทำงานแล้ว ก็จะไม่ยากเลย วิธีที่ง่ายสุด คือ ลองคิดการทำงานของ loop แต่ละขั้นตอน ลงกระดาษ (หรือถ้าใครรู้จักการ debug และการใช้ breakpoint ของ VS Code หรือ IDE ตัวอื่นๆ ก็จะพอเห็นภาพครับ)
การหยุดการทำงานของ loop จะใช้ break
:
while (true)
{
n = get_int("Size: ");
if (n > 1)
{
break;
}
}
Imprecision, overflow
เรื่องของการคำนวณ พวก floating number (ค่าที่มีทศนิยม) มักจะไม่ค่อยถูก หรือคลาดเคลื่อนไปบ้าง
#include <cs50.h>
#include <stdio.h>
int main(void)
{
// Prompt user for x
float x = get_float("x: ");
// Prompt user for y
float y = get_float("y: ");
// Divide x by y
float z = x / y;
printf("%f\n", z);
}
ผลลัพธ์
$ ./calculator
x: 2
y: 3
0.666667
$ ./calculator
x: 1
y: 10
0.100000
$
- เราสามารถใช้
printf("%.50f\n", z);
หรือ%.5f
เพื่อกำหนดจุดทศนิยมได้ - ปัญหาคือ computer ไม่สามารถแสดงค่าจริงๆ ได้ ทำได้แค่ใกล้เคียงที่สุด เพราะด้วยข้อจำกัดของ 32 bit เอาแบบง่ายๆ 2 หาร 3 จริงๆ ได้ค่าเท่าไหร่ ถ้าเราคิดด้วยมือ มันก็คือ
0.6666.
ไปเรื่อยๆ ใช่มั้ยครับ แล้วแต่เราจะพอแค่ไหน เช่น0.66666667
- ในอนาคตปี 2038 เราอาจจะมีปัญหา ในการเก็บข้อมูลเวลาในระบบ computer เพราะเราใช้แบบ 32-bit
- แต่ปัจจุบัน hardware มีความสามารถมากขึ้น สามารถจัดการ bit ได้มากกว่าเดิม
ตัวอย่างสุดท้าย
#include <cs50.h>
#include <stdio.h>
int main(void)
{
float amount = get_float("Dollar Amount: ");
int pennies = amount * 100;
printf("Pennies: %i\n", pennies);
}
ผลลัพธ์
$ ./pennies
Dollar Amount: .99
Pennies: 99
$ ./pennies
Dollar Amount: 1.23
Pennies: 123
$ ./pennies
Dollar Amount: 4.20
Pennies: 419
เหมือน ค่าที่รับสุดท้าย 4.20
จะแสดงไม่ตรง ซึ่งจริงๆ มันอาจจะเก็บค่า 4.199999...
และเวลา คูณด้วย 100 มันเลยกลายเป็น 419
ใช้ round
มาช่วย
#include <cs50.h>
#include <math.h>
#include <stdio.h>
int main(void)
{
float amount = get_float("Dollar Amount: ");
int pennies = round(amount * 100);
printf("Pennies: %i\n", pennies);
}
Quiz ท้ายบทเรียน
จริงๆ ผู้เรียนสามารถทำ quiz ส่งได้ และก็มีรายละเอียดและแบบฝึกเพิ่มเติม ลองดูครับ
สรุป
Week 1 มีอะไรให้เรียนเยอะมาก และนอกจากจะนั่งดู video 2ชั่วโมงแล้วจบ ต้องบอกว่าไม่ใช่ครับ แต่ละขั้นตอน ต้องใช้เวลาศึกษาเพิ่มเติม เช่น loop ต่างๆ หรือ if/else ถ้าเป็นมือใหม่ ยังไงก็ต้องลองหัดเขียน ลองสร้างคำถาม แล้วทำตาม หรือหาโจทย์ฝึกทำเพิ่มเติมครับ
แหล่งศึกษาเพิ่มเติม (ภาษาอังกฤษ)
- Tutorial: Learn to debug C++ code using Visual Studio
- Programming C
- C programming Tutorial - Introduction to C Programming
- The C Beginner's Handbook
ขอให้สนุกกับการเรียนนะครับ
Happy Coding ❤️