C Language (LV.1)

ภาษาซี ระดับพื้นฐาน

โครงสร้างของโปรแกรมภาษาซี

1. ส่วนหัวของโปรแกรม

Preprocessor Directive

ใช้สำหรับเรียกไฟล์ที่โปรแกรมต้องการ และกำหนดค่าต่างๆ จะต้องเริ่มต้นด้วยเครื่องหมายไดเรกทีฟ (#) และตามด้วยชื่อที่ต้องการกำหนดค่า

Directive

การใช้งาน

#include

การโหลด source code จากไฟล์

#define

ประกาศ macro

#undef

ยกเลิกการประกาศ macro

#ifdef

ตรวจสอบว่า macro มีการประกาศไว้หรือไม่

#ifndef

ตรวจสอบว่า macro ไม่ได้มีการประกาศไว้

#if

ตรวจสอบเงื่อนไขบางอย่างกับ macro หากเข้าเงื่อนไขจะทำ directive ที่ตามมา

#else

กำหนดสิ่งที่จะทำ หากไม่เข้าเงื่อนไขใน #if

#elif

ตรวจสอบเงื่อนไขบางอย่างกับ macro เมื่อไม่ตรงเงื่อนไขใน #if

#endif

จบการตรวจสอบเงื่อนไข macro

#line

กำหนดว่าเมื่อ compiler error จะแสดงข้อผิดพลาด เป็นเลขบรรทัด หรือชื่อไฟล์อย่างไร

#error

หยุดการ compile ปกติใช้ร่วมกับ directive ประเภทเงื่อนไข

ตัวอย่าง directive ที่ใช้บ่อยคือ #include #include <...> - compiler จะทำการค้นหา header file จาก directory ที่ใช้เก็บ header file โดยเฉพาะ #include "..." - compiler จะทำการค้นหา header file จาก directory เดียวกับ source code ก่อน หากไม่พบจึงหาใน directory ที่เก็บ header file เฉพาะ #define #define - กำหนดค่าคงที่เพื่อใช้ในโปรแกรม มีประโยชน์ในกรณีที่ค่าถูกใช้หลายที่ในโปรแกรม ทำให้สะดวกต่อการแก้ไข

Global Declaration

เป็นการประกาศชื่อตัวแปร หรือ function ภายในโปรแกรม โดยที่ทุกส่วนของโปรแกรมจะสามารถเรียกใช้ตัวแปร หรือ function นี้ได้

2. ส่วนของ function

ปกติจะเขียนด้วยชื่อบล็อกของชุดคำสั่ง ตามด้วยเครื่องหมาย (...) ภายใน (...) จะระบุตัวแปร เพื่อรับค่าที่ส่งเข้าบล็อกของ function ส่วน {...} เป็นตัวกำหนดขอบเขตของบล็อก

ภายในโปรแกรมจะมีกี่บล็อกของ function ก็ได้ แต่จะต้องมีบล็อก main

3. ส่วนรายละเอียดของโปรแกรม

เป็นส่วนของการเขียนคำสั่ง เพื่อให้โปรแกรมทำงานตามที่ได้ออกแบบไว้

4. ส่วน comment (จะมีหรือไม่ก็ได้)

เขียนเพื่ออธิบายการทำงานของโปรแกรม เพื่อให้ผู้พัฒนาร่วมกันเข้าใจ ระบบจะข้ามการแปลส่วนนี้ ปกติจะเขียนด้วยเครื่องหมาย ดังนี้

comment แบบบรรทัดเดียว ใช้ //... ซึ่งข้อความที่ตามหลังจะเป็น comment

comment แบบหลายบรรทัด ใช้ /*...*/

ตัวอย่างโครงสร้างโปรแกรม

Token & Whitespace

แต่ละกลุ่มอักษร เราเรียกว่า token และช่องว่างระหว่าง token คือ whitespace

Token & Whitespace

ตัวแปร (Variable)

ตัวแปรเป็นส่วนที่ใช้เก็บค่าของข้อมูล โดยสามารถเปลี่ยนแปลงค่าที่ถูกเก็บอยู่ได้ในระหว่างที่โปรแกรมทำงานอยู่ ซึ่งในการเก็บค่า จะต้องกำหนดชนิดของตัวแปรให้ตรงกับข้อมูลที่ต้องการจะเก็บ ชนิดของตัวแปรมีดังนี้

1. ตัวอักษร

  • char - มีขนาดหนึ่งตัวอักษร

  • string - กลุ่มอักษรที่เรียงต่อกัน

2. ตัวเลข

2.1 จำนวนเต็ม

  • int - จำนวนเต็ม

  • long - จำนวนเต็มที่มีขนาดใหญ่กว่า int

2.2 จำนวนทศนิยม

  • float - จำนวนทศนิยม

  • double - จำนวนทศนิยมที่มีขนาดใหญ่กว่า float

  • long double - จำนวนทศนิยมที่มีขนาดใหญ่กว่า double

ดั้งเดิมในสมัยที่ระบบคอมพิวเตอร์ CPU Architecture 16 bits ชนิดตัวแปรมีค่าดังนี้

Type

ขนาดความจุ (byte)

ค่าที่เป็นไปได้

char

1

-128 ถึง 127 (กรณีนำมาเก็บเลข)

int

2

-32768 ถึง 32767

long

4

-2,147,483,648 ถึง 2,147,483,647

float

4

เลขทศนิยม 6 ตำแหน่ง

double

8

เลขทศนิยม 15 ตำแหน่ง

long double

12

เลขทศนิยม 19 ตำแหน่ง

ขนาดความจุของชนิดข้อมูลมีการเปลี่ยนไป เมื่อ CPU Architecture มีการพัฒนา ดังนั้นค่าที่เป็นไปได้ของ Data Type แต่ละชนิดจึงมีค่ามากกว่าแต่ก่อน เพื่อป้องกันความสับสนในขนาดความจุของข้อมูล ภายหลังจึงมีการกำหนดชนิดตัวแปรแบบระบุ CPU Architecture ไว้ด้วย เช่น int16, int32, int64

ตัวอย่างการคำนวณหาค่าที่เป็นไปได้ของแต่ละ Data Type

sign int

sign bit 0 คือ เลขที่มีค่าเป็นบวก 1 คือ เลขที่มีค่าติดลบ value bit 215=327682^{15} = 32768 มีความเป็นไปได้ 32768 แบบ กรณีทั้ง sign bit และ value bit เป็น 0 ทั้งหมด จะมีค่าเป็น ศูนย์ ดังนั้นในค่าที่เป็นบวก จะมีได้ถึงเลข 32767 ทำให้ int สามารถแทนเลข ที่มีค่าระหว่าง -32768 จนถึง 32767 ได้

กรณีเลขชนิด Unsigned

ในตัวแปรชนิดที่เราไม่ต้องการค่าติดลบ เราสามารถประกาศชนิดข้อมูลแบบ unsigned ได้ เช่น

  • unsigned int

  • unsigned long

  • unsigned float

bit ที่เคยเป็นของ sign bit จะมาเป็นส่วนหนึ่งของ value bit แทน ทำให้ค่าความเป็นไปได้เพิ่มขึ้น ตัวอย่างเช่น int 216=655362^{16} = 65536 มีความเป็นไปได้ 65536 แบบ ทำให้สามารถแทนเลขได้ตั้งแต่ 0 ถึง 65535

การตั้งชื่อตัวแปร

  • ห้ามขึ้นต้นด้วยตัวเลข

  • ห้ามมี whitespace

  • ห้ามมีสัญลักษณ์พิเศษ ยกเว้น underscore (_)

  • ห้ามมี reserve word

Reserved word

reserved word คือคำสงวนที่ห้ามใช้เป็นชื่อตัวแปรในโปรแกรม เนื่องจากคำเหล่านี้มีความหมายเฉพาะภายในโปรแกรม ในแต่ละโปรแกรมจะมี reserve word ต่างกันออกไป

auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

ตัวดำเนินการ (Operator)

ตัวดำเนินการทางคณิตศาสตร์ (Mathematical Operator)

Operator

ความหมาย

ตัวอย่าง

ผลลัพธ์

+

บวก

4+6

10

-

ลบ

5-4

1

*

คูณ

7*1

7

/

หารเอาผล

4/2

2

%

หารเอาเศษ

5%2

1

++

เพิ่มค่าขึ้นหนึ่ง

4++

5

--

ลดค่าลงหนึ่ง

4--

3

% เรียกว่า มอดูลัส (modulus)

ข้อสังเกต การหารเอาเศษ 7%4 => 3 4%7 => 4 -7%4 => -3 7%-4 => 3 -7%-4 => -3 ดังนั้น ถ้าตัวถูกหารมีค่าเป็นลบ จะได้ผลลัพธ์เป็นค่าลบ

ลำดับในการดำเนินการทางคณิตศาสตร์

  1. ทำค่าในวงเล็บก่อน

  2. ทำคูณและหารก่อน โดยเรียงลำดับจากซ้ายไปขวา

  3. ทำบวกและลบ โดยเรียงลำดับจากซ้ายไปขวา

ตัวอย่างการคำนวณ

1000/100%(5+6)+13*7 =1000/100%11+13*7 =10%11+13*7 =10+13*7 =10+91 =101

ตัวดำเนินการเปรียบเทียบ (Comparison Operator)

Operator

ความหมาย

ตัวอย่าง

ผลลัพธ์

==

เท่ากับ

4==3

เท็จ

!=

ไม่เท่ากับ

4!=3

จริง

>

มากกว่า

4>3

จริง

>=

มากกว่าหรือเท่ากับ

3>=3

จริง

<

น้อยกว่า

4<3

เท็จ

<=

น้อยกว่าหรือเท่ากับ

4<=4

จริง

ตัวดำเนินการทางตรรกะ (Logical Operator)

Operator

ควาหมาย

&&

และ

||

หรือ

!

นิเสธ

ค่าที่หนึ่ง

Operator

ค่าที่สอง

ผลลัพธ์

T

&&

T

T

T

&&

F

F

F

&&

T

F

F

&&

F

F

T

||

T

T

T

||

F

T

F

||

T

T

F

||

F

F

ค่า

ผลลัพธ์

!T

F

!F

T

ตัวดำเนินการกำหนดค่า (Assignment Operator)

Operator

การใช้งาน

ผลของตัวแปร

ตัวอย่าง

ผลลัพธ์

=

ตัวแปร = ตัวเลข

ตัวเลข

a = 3

a = 3

+=

ตัวแปร += ตัวเลข

ตัวแปร + ตัวเลข

a += 3

a = a+3

-=

ตัวแปร -= ตัวเลข

ตัวแปร - ตัวเลข

a -= 3

a = a-3

*=

ตัวแปร *= ตัวเลข

ตัวแปร * ตัวเลข

a *= 3

a = a*3

/=

ตัวแปร /= ตัวเลข

ตัวแปร / ตัวเลข

a /= 3

a = a/3

%=

ตัวแปร %= ตัวเลข

ตัวแปร % ตัวเลข

a %= 3

a = a%3

การกำหนดค่า ทำการคำนวณค่าฝั่งขวาของเครื่องหมายเท่ากับ (=) และเก็บลงในตัวแปรฝั่งซ้ายของเครื่องหมายเท่ากับ (=)

int a = 3; a = a+1; จะเป็นการคำนวณ 3+1 ได้ผลลัพธ์เป็น 4 แล้วเก็บลงตัวแปร a ทำให้ หลังการดำเนินการ a มีค่าเป็น 4

การแสดงผล

ใช้ function printf ในการแสดงค่าออกหน้าจอ

ตัวอย่างการใช้งาน

ผลลัพธ์บนหน้าจอ

printf("Hello World")

Hello World

int x = 10;

printf("%d", x);

10

int x =10;

printf("A = %d", x);

A = 10

int x = 1, y = 2, z = 3;

printf("%d %d %d", x, y, z)

1 2 3

ตัวแทนชนิดข้อมูลที่ใช้บ่อย

เครื่องหมาย

ชนิดข้อมูล

%d

int

%u

unsigned int

%f

float

%c

char

%s

string

หากมีการใช้ตัวเลขร่วมกับชนิดตัวแทนข้อมูล จะมีความหมายเฉพาะ %<ตัวเลข><เครื่องหมาย> - จะบอกว่าต้องการพื้นที่ช่องอักษรตามจำนวนตัวเลข และใส่อักษรลงไปในพื้นที่นั้นโดยชิดขวา เช่น %3d ของ 10 จะได้ " 10" หากตัวเลขติดลบ จะหมายถึงการชิดซ้าย เช่น %-3d ของ 10 จะได้ "10 " กรณีตัวเลขขึ้นต้นด้วย 0 (ใช้ได้กับข้อมูลตัวเลขเท่านั้น เช่น int float) หมายถึงพื้นที่ว่างนั้นให้เติมด้วย 0 เช่น %02d ของ 6 จะได้ "06" กรณี มีจุดทศนิยม ก่อนหน้าตัวเลข หมายความว่า ตัวเลขนั้น แทนจำนวนหลักของทศนิยม เช่น %.2f ของ 3.14159265 จะได้ "3.14" %2.2f ของ 3.14159265 จะได้ "3.14" (เพราะเลขน้อยกว่าจำนวนอักษรที่ต้องพิมพ์ทั้งหมด) %5.2f ของ 3.14159265 จะได้ " 3.14" %06.2f ของ 3.14159265 จะได้ "003.14"

Escape sequence

เป็นรหัสพิเศษที่ช่วยในการจัดการแสดงผล ค่าที่ใช้บ่อยดังนี้

เครื่องหมาย

ผลลัพธ์

\n

ขึ้นบรรทัดใหม่

\r

เลื่อนเคอเซอร์ไปซ้ายสุด

\t

Tab ในแนวนอน

\'

พิมพ์ single quote (')

\"

พิมพ์ double quote (")

\\

พิมพ์ backslash (\)

\0

ค่าว่าง (Null character)

การประกาศค่าคงที่

ใช้เพื่อให้ไม่สามารถเปลี่ยนค่าของตัวแปรได้ เมื่อประกาศไปแล้ว ทำได้โดยใส่ const นำหน้าชื่อตัวแปร เมื่อประกาศตัวแปร เช่น

การรับค่าจากผู้ใช้

ใช้ฟังก์ชัน scanf วิธีการใช้ดังนี้

scanf("ตัวแทนชนิดข้อมูล", &ตัวแปร);

เช่น

ในการรับค่า string จะไม่ใช้ %s ในการรับค่า แต่จะใช้ %c แทน เช่น scanf("%c", &mystring);

รูปแบบคำสั่งกำหนดเงื่อนไข (Conditional Statements)

ใช้เพื่อเปลี่ยน Flow การทำงานของโปรแกรม มีรูปแบบดังนี้

IF statement

เป็นการกำหนดเงื่อนไข ซึ่งจะทำคำสั่งซึ่งอยู่ภายในขอบเขต {...} ก็ต่อเมื่อเงื่อนไขเป็นจริง

ตัวอย่างเช่น

หาก a > b เป็นจริง ก็จะทำคำสั่งภายในบล็อก

สามารถใช้ตัวเลขแทน True/False ใน condition ได้ โดยค่าใดก็ตามที่เป็น zero จะถือเป็น False ส่วน non-zero จะเป็น True เช่น if(0) => False if(1) => True if(-1) => True

IF-ELSE Statement

เป็นการกำหนดเงื่อนไขการทำงาน โดยหากเงื่อนไขเป็นจริง จะทำงานในส่วนขอบเขตของ if แต่หากไม่เป็นจริง จะทำงานภายใต้ขอบเขตของ else

ตัวอย่างเช่น

ถ้า a เป็น 5 จะมีเงื่อนไขเป็นจริง ทำให้ทำงาน statement a++ และได้ผลลัพธ์เป็น 6 แต่ถ้า a เป็น 2 จะมีเงื่อนไขเป็นเท็จ ทำให้ทำงาน statement a-- และได้ผลลัพธ์เป็น 1

IF-ELSE IF-ELSE Statement

เป็นการกำหนดเงื่อนไขที่ซับซ้อนขึ้น โดยหากเงื่อนไขใน if ไม่เป็นจริง จะมีการไปตรวจสอบเงื่อนไขในส่วน else if ต่อไปก่อนว่าเป็นจริงหรือไม่ แล้วจึงค่อยไปทำงานในส่วนของ else หากไม่มีเงื่อนไขใดเป็นจริงเลย

ตัวอย่างเช่น

Switch Statement

ในบางครั้ง condition อาจเป็นการตรวจสอบว่า variable มีค่าตรงกับค่าใด การเขียน IF-ELSE IF-ELSE Statement บางครั้งอาจดูซับซ้อนไป สามารถเขียนให้อ่านง่ายได้ด้วย switch statement แทน

ตัวอย่างเช่น

จะได้ผลลัพธ์ออกหน้าจอเป็น I got B

รูปแบบคำสั่งทำซ้ำ (Iteration Statements)

ใช้เพื่อเปลี่ยน Flow การทำงานของโปรแกรม มีรูปแบบดังนี้

WHILE Loop Statement

จะมีการตรวจสอบเงื่อนไขก่อนว่าเป็นจริงหรือไม่ หากเป็นจริงจึงจะทำคำสั่งที่อยู่ในขอบเขต {...} และจะมีการทำซ้ำไปเรื่อยๆ หากเงื่อนไขยังคงเป็นจริงอยู่

ตัวอย่างเช่น

DO-WHILE Loop Statement

คล้าย While Loop statement แต่จะมีการทำงานไปรอบหนึ่ง แล้วจึงค่อยตรวจสอบเงื่อนไข หากเงื่อนไขยังคงเป็นจริงอยู่ก็จะทำซ้ำไปเรื่อยๆ จนกว่าเงื่อนไขจะเป็นเท็จ

FOR Loop Statement

มักจะใช้ในรูปแบบการวนซ้ำที่มีมีการระบุจำนวนครั้งอย่างชัดเจน จะมีความคล้ายกับ While Loop statement คือตรวจสอบเงื่อนไขก่อนเริ่มทำงานในขอบเขต {...}

ตัวอย่างเมื่อเปรียบเทียบกับ While Loop statement

Array

เป็นโครงสร้างการเก็บข้อมูลจำนวนมากซึ่งมีชนิดเดียวกัน มีจำนวนข้อมูลที่แน่นอน และมีความสัมพันธ์ในเชิงลำดับต่อกัน โดยข้อมูลที่เก็บจะมีการจองพื้นที่หน่วยความจำซึ่งเรียงต่อกันอีกด้วย

เปรียบเทียบระหว่างการประกาศตัวแปรตามปกติ กับ array

simple variable VS array variable

การประกาศตัวแปรตามปกติจะมีขนาดสำหรับเก็บข้อมูลตามชนิดของตัวแปร เพียง 1 ค่า แต่การประกาศตัวแปรแบบ Array จะได้พื้นที่เก็บข้อมูลเรียงต่อกัน ตามจำนวนที่เรากำหนด

การประกาศตัวแปร

ตัวอย่างเช่น

การระบุตำแหน่งข้อมูลใน Array

ตำแหน่งข้อมูลใน Array เราเรียกว่า index โดย index จะเริ่มตั้งแต่ 0

Array and Indices

การเข้าถึงข้อมูลทำได้โดยระบุเลข index ภายใน [...] ซึ่งตามหลังชื่อตัวแปรของ Array เช่น

Array for string

ใน Array ตัวเลข การมีการกำหนดค่าเริ่มต้นให้เมื่อประกาศตัวแปร ในตำแหน่งที่ไม่ได้กำหนดค่ามา จะมีค่าเป็น 0 เช่น int n[5]={1,2,3} n[4] และ n[5] จะมีค่าเป็น 0

การเก็บ String ลงใน Array ประเภท character ทำได้ดังนี้

การใช้ %s แทน %c จะทำให้สามารถรับค่าทั้งข้อความได้เลย ไม่ต้องเก็บทีละตัวอักษร โปรแกรมจะทำการเก็บค่าแต่ละตัวอักษรในข้อความลงในตัวแปรตามลำดับตั้งแต่ index 0 ไปจนจบข้อความ

Array หลายมิติ

เราสามารถสร้าง Array หลายมิติได้ โดยกำหนดขนาดของมิติอื่นได้ด้วยการระบุ [...] ต่อท้ายเพิ่มไป เช่น

Array สามารถมีจำนวนมิติได้ไม่จำกัด ตำแหน่งของพื้นที่ในหน่วยความจำสำหรับ Array หลายมิติ เป็นดังนี้

Memory location for multi-dimension array

ตัวอย่างการเข้าถึงข้อมูลใน Array หลายมิติ

Pointers

เป็นตัวแปรซึ่งเก็บค่า memory address ของข้อมูล การประกาศ Pointer ทำได้ดังนี้

ตัวอย่างเช่น

เราสามารถหา address ของตัวแปรแต่ละตัวได้จากเครื่องหมาย ampersand (&) เช่น pa = &a; จะเป็นการหา address ของตัวแปร a และนำไปเก็บในตัวแปร pa

การเข้าถึงค่าหรือพื้นที่จัดเก็บข้อมูล ซึ่งอยู่ภายใต้ address แต่ละตัว สามารถทำได้โดย ระบุ * หน้า Pointer เช่น

จากตัวอย่าง จะเป็นการเข้าถึงค่าของข้อมูลโดยอ้างอิงจาก address ซึ่งเก็บอยู่ใน pa (ซึ่งเป็น address ของตัวแปร a) และนำค่าไปเก็บในตัวแปร b

ในทางกลับกัน เราอาจจะกำหนดค่า และนำไปใส่ในตำแหน่งพื้นที่ที่ Pointer อ้างอิงอยู่ก็ได้ ดังนี้

การเลื่อน Pointer

ในการเลื่อน Pointer ไปยังตำแหน่ง address ถัดไป หรือก่อนหน้า จะทำได้โดยการบวก/ลบค่าของ address ที่เก็บอยู่ เพื่อเป็นการอ้างอิงไปยัง address ข้างเคียง เพียงแต่ขนาดของข้อมูลแต่ชนิดนั้น มีผลต่อเลข address หากบวก/ลบ ผิดขนาด จะส่งผลให้โปรแกรมทำงานผิดพลาดได้ ทางแก้คือ ควรใช้ ++ หรือ -- แทน เนื่องจากโปรแกรมจะทราบว่า Pointer นั้นเป็น Pointer ของข้อมูลชนิดใด และจะทำการบวก/ลบค่า ตามขนาดข้อมูลให้ เช่น

ข้อแตกต่างระหว่าง Array กับ Pointer

Pointer

Array

เก็บ Address ของข้อมูล

เก็บข้อมูล

การเข้าถึงข้อมูลเป็นแบบทางอ้อม (ต้องผ่าน address ก่อน)

สามารถเข้าถึงข้อมูลได้ทางตรงโดยการระบุ index

มักจะใช้กับโครงสร้างข้อมูลที่มีการเปลี่ยนแปลงได้ในขณะ run time

มักจะใช้กับข้อมูลที่มีขนาดแน่นอนตลอดการทำงานของโปรแกรม

มักจะใช้คู่กับ function malloc() และ free()

มีการจัดสรรไว้ก่อนที่โปรแกรมจะเริ่มทำงาน

โดยปกติใช้อ้างอิงข้อมูลที่ไม่เจาะจงตำแหน่ง มีตำแหน่งตอน run-time

ตำแหน่ง address ของข้อมูลถูกกำหนดแน่นอนตอนที่ compile โปรแกรม

การใช้ function malloc() และ free()

malloc เป็น function ที่ใช้ในการจองพื้นที่หน่วยความจำขณะ run time และ free เป็น function ที่ใช้คืนพื้นที่หน่วยความจำที่ขอมา

function นี้อยู่ภายใต้ lib stdlib.h ดังนั้นต้อง include header file ก่อนใช้งานด้วย

การใช้ malloc ทำได้โดยการกำหนดขนาด byte ที่ต้องการจะจองเป็น argument ใน function malloc และจะได้ค่า address ของพื้นที่ที่จอง ตอบกลับมา เช่น

จากตัวอย่างจะเป็นการจองพื้นที่ขนาด 4 bytes ให้กับ Pointer pa จะมี int* เพื่อทำ implicit type conversion เพื่อให้รู้ว่าค่า ที่ return กลับมาจาก function malloc เป็น address ของ int pointer

ในคอมพิวเตอร์รุ่นใหม่ๆ ขนาดของตัวแปรอาจจะมากกว่าเดิม เช่น int เปลี่ยนจาก 2 bytes เป็น 4 bytes ดังนั้นเพื่อความถูกต้องในการกำหนดขนาดของข้อมูล เราจะใช้ function sizeof เพื่อหาขนาดของข้อมูลกัน เช่น pa = (int*) malloc(sizeof(int) * 5); ซึ่งจะเป็นการจองพื้นที่สำหรับ int 5 ตัว

การใช้ free ทำได้โดยระบุ address แรกของพื้นที่ซึ่งต้องการคืนพื้นที่มาเป็น argument ของ function เช่น

การใช้ function gets() และ puts()

function ทั้งสองอยู่ภายใต้ lib stdio.h ดังนั้นต้อง include header file ก่อนใช้งานด้วย

function gets() สามารถใช้รับค่า string จากผู้ใช้มาได้เหมือนกับ function scanf() มีตัวอย่างการใช้ดังนี้

function gets ปัจจุบันไม่แนะนำให้ใช้แล้ว เนื่องจากมีจุดอ่อนในการออกแบบซึ่งทำให้เกิด Buffer overflow ไปยังพื้นที่ข้อมูลอื่นๆได้ จึงไม่ปลอดภัย และถูกแนะนำให้เปลี่ยนไปใช้ function fgets() แทน ซึ่ง function fgets() มีการกำหนดจำนวนข้อมูลที่จะรับเข้ามา จึงมีความปลอดภัยมากกว่า

function puts() สามารถใช้แสดงค่า string ได้เหมือนกับ function printf()

Functions

เป็นส่วนของโปรแกรมที่แยกออกมา เพื่อกำหนดขั้นตอนการทำงานซึ่งมีความซ้ำซ้อนหรือคล้ายคลึงกัน มีการใช้ซ้ำบ่อยๆ การแยกส่วนของโปรแกรมออกมาเป็นฟังก์ชัน จะทำให้โปรแกรมเป็นส่วนๆมากขึ้น ลดความซ้ำซ้อน ลดความผิดพลาดจากการแก้ไขไม่ครบ และทำให้โปรแกรมอ่านง่ายขึ้น

การทำงานของ Function

ฟังก์ชันจะถูกใช้งานเมื่อมีการเรียกใช้ และทำงานตามคำสั่งที่อยู่ภายใต้ขอบเขตของฟังก์ชัน หลังจากทำงานเสร็จจะโดดกลับไปทำงานต่อจากส่วนที่เรียกใช้

Program and Function Flow

รูปแบบการประกาศ Function

ฟังก์ชันสามารถรับค่ามาได้หลายค่า และมาเก็บอยู่ในตัวแปร Parameter แต่การที่ฟังก์ชันจะส่งค่าตอบกลับไปหลังจากทำงานเสร็จนั้น จะตอบกลับได้เพียงค่าเดียว การตอบกลับจะใช้ส่วนที่เรียกว่า return เมื่อฟังก์ชันพบส่วน return จะโดดกลับไปที่ส่วนที่เรียกฟังก์ชันทันที ดังนั้นหากมี code ส่วนใดอยู่ถัดจากส่วน return นั้น code ส่วนนั้นจะไม่ถูกทำงาน

เราสามารถที่จะสร้างฟังก์ชันที่จะทำงานไปจนจบฟังก์ชันโดยไม่ตอบกลับค่าใดๆไปได้ ดังนั้นภายในฟังก์ชันจะไม่มีบรรทัด return และชนิดของค่าที่ return จะแทนด้วย void

ตัวอย่างการประกาศ Function

การจะใช้งานฟังก์ชันได้ จะต้องประกาศฟังก์ชันไว้ก่อน ในบางภาษาจึงต้องประกาศฟังก์ชันไว้ก่อนส่วนที่เป็นการทำงานหลัก เช่น ก่อนส่วน function main() ซึ่งภาษาซีก็มีลักษณะนั้นเช่นกัน ดังนั้นหากมีรายละเอียดคำสั่งภายในฟังก์ชันเยอะ ก็จะเป็นความลำบากในการหาส่วนการทำงานหลัก และไล่การทำงานของโปรแกรม ทางแก้คือ การแยกส่วนประกาศ interface เชื่อมต่อในการเรียกใช้งานฟังก์ชัน (function prototype) ออกจากส่วนที่เป็นรายละเอียดของฟังก์ชัน (function implementation) ได้ ตัวอย่างเช่น

ซึ่งการทำเช่นนี้จะทำให้เราสามารถเขียนส่วนที่เป็นรายละเอียดการทำงานของฟังก์ชันอยู่บรรทัดหลังจาก function main() ได้ และสะดวกต่อการอ่าน code โปรแกรม

รูปแบบการส่งค่า argument ให้กับ Function

Pass by value

เป็นการส่งค่าแบบปกติ เป็นการ copy ค่าจาก argument ที่ให้มาในการเรียกฟังก์ชัน ไปใส่ในตัวแปร parameter ของฟังก์ชัน ตัวอย่างเช่น

การเปลี่ยนแปลงค่าภายในฟังก์ชัน ไม่มีผลต่อตัวแปรของการทำงานหลัก เพราะเป็นคนละตัวแปรกัน เพียงแค่ชื่อตัวแปรอาจจะตรงกันและ copy ค่าไปให้ทำงานภายในฟังก์ชันเท่านั้น

Pass by pointer

เป็นการ copy ค่า address ของตัวแปรไปให้ ดังนั้นหากภายในฟังก์ชันมีการเรียกใช้ตำแหน่งข้อมูลจาก address นั้น จะเป็นการเรียกใช้โดยอ้อม และมีผลต่อค่าที่เก็บอยู่ในตำแหน่งข้อมูลนั้น ตัวอย่างเช่น

ประโยชน์อย่างหนึ่งที่ได้จากการใช้ Pass by pointer คือ การแก้ไขค่าโดยฟังก์ชัน เพราะฟังก์ชันสามารถ return ค่ากลับได้เพียงค่าเดียว การส่ง address ของข้อมูลไปให้อ้างอิง และแก้ไข จะทำให้ฟังก์ชันสามารถอัพเดตข้อมูลได้มากกว่าหนึ่งค่า แต่การใช้ Pass by pointer ก็มีจุดอ่อนด้านความปลอดภัย เพราะ การใช้ pointer ชี้ตำแหน่ง address และทำการแก้ไขข้อมูลอาจจะเป็นจุดอ่อนให้ Hacker สามารถอ้างอิงไปยัง address อื่นๆได้

ภายหลังปัญหานี้ได้ถูกแก้ในภาษาซีสมัยใหม่ เช่น C++ โดยมี Pass by reference มาทดแทน ซึ่งจะปลอดภัยกว่า เพราะไม่สามารถไปปรับเปลี่ยนค่า address ของข้อมูลโดยตรงได้

ตัวอย่างการใช้ Pass by pointer กับข้อมูลชนิด array สามารถรับมาและใช้ในรูปแบบ Array ได้ตามเดิม ตอนที่รับค่ามาใช้ มีเพียงมิติแรกเท่านั้นที่ไม่ต้องระบุขนาดของมิติ มิติอื่นๆต้องกำหนดขนาด เพื่อใช้ในการคำนวณขนาดมิติแรก

Strings

เป็น Array 1 มิติของ char ตัวอักษรภายใน string สามารถเข้าถึงได้เช่นเดียวกับส่วนหนึ่งของ array หรือใช้ pointer ระบุตำแหน่ง char NULL character ('\0'; ASCII 0) ถูกใช้เพื่อบอกจุดสิ้นสุดของ string

การประกาศ string คือการสร้าง Array ของ character ขนาดตามข้อมูลที่จะเก็บ + 1

การรับค่ามาเก็บสามารถใช้ function scanf() ร่วมกับตัวแทนชนิดข้อมูล string (%s)

เราสามารถประกาศข้อมูล string ได้โดยตรงเมื่อสร้าง Array

lib string.h เป็น library ที่ใช้จัดการงานเกี่ยวกับ string function ที่น่าสนใจดังนี้ strcpy - ใช้ copy ข้อมูลภายใน string จาก source ไป destination strlen - บอกจำนวนตัวอักษรภายใน string (ตั้งแต่ต้นไปจนถึง NULL character) strcmp - ใช้ในการเปรียบเทียบว่า string ทั้งสองเหมือนกันหรือไม่ การทำงานเบื้องหลังจากเป็นการนำ ASCII character มาลบกันทีละตัว หาก string ทั้งสองเหมือนกันจะได้ค่า return เป็น 0

lib ctype.h เป็น library ที่ใช้จัดการงานเกี่ยวกับ character function ที่น่าสนใจดังนี้ toupper - จะ return ค่าอักษรตัวพิมพ์ใหญ่ของตัวอักษรที่ pass เป็น argument ไป tolower - จะ return ค่าอักษรตัวพิมพ์เล็กของตัวอักษรที่ pass เป็น argument ไป ดังนั้นโดยปกติหากต้องการเปลี่ยนทั้ง string เป็นตัวพิมพ์ใหญ่หรือพิมพ์เล็กทั้งหมด จะใช้ function เหล่านี้คู่กับ loop

Structure & Union

Structure

Structure คือชนิดข้อมูลที่นิยามขึ้นภายในโปรแกรม จะประกอบด้วยชื่อชนิดข้อมูลที่จะสร้าง และโครงสร้างชนิดข้อมูลของสมาชิกภายใน

การประกาศ Structure

ตัวอย่างเช่น

จากตัวอย่าง จะเกิดชนิดข้อมูลชนิดใหม่ขึ้นชื่อ "struct student" ตัวอย่างการเรียกใช้ เช่น

การใช้แบบนี้จะไม่สะดวก โดยปกติจึงจะนำ typedef มาช่วยแก้ปัญหานี้ เช่น

นอกจากนี้ เรายังสามารถสร้าง Structure ซ้อนกันได้ ตัวอย่างเช่น

การเข้าถึงข้อมูลภายใน structure

กรณีตัวแปรธรรมดา เข้าถึงได้ด้วย dot operator (.) เช่น

กรณีตัวแปรแบบ Pointer

การเข้าถึงด้วย dot operator อาจสร้างความซับซ้อน ผิดพลาดได้ง่าย และไม่สะดวกต่อการใช้งาน จึงควรใช้ arrow operator (->) แทน เช่น

Union

จะแตกต่างจาก Structure ชนิดข้อมูลแบบ Structure จะมีขนาดเท่ากับผลรวมของขนาดสมาชิกทุกตัว และสมาชิกทุกตัวจะมี address ของตนเอง แต่ Union จะมีขนาดเท่ากับขนาดของสมาชิกที่ใหญ่ที่สุด และสมาชิกทุกตัวจะมี address เดียวกัน

Structure and Union

ปกติ union จะถูกใช้ทำหน้าที่เป็น buffer ของข้อมูล

การประกาศ Union

ตัวอย่างเช่น

Files

ในภาษาซี การจัดการเกี่ยวกับไฟล์ไม่ว่าจะเป็นการเขียน หรือการอ่านจากไฟล์ จะดำเนินการผ่านสิ่งที่เรียกว่า data stream stream ในภาษาซีมีสองแบบ คือ text (อักษร) กับ binary (ข้อมูลที่เป็น bit 0,1)

การระบุตำแหน่งของข้อมูลภายในไฟล์ จะดำเนินการผ่าน Pointer ของข้อมูลชนิด FILE

ในการสร้างไฟล์หรือจัดการกับไฟล์เดิมที่มีอยู่ จะต้องเริ่มจากการเปิดไฟล์ ด้วย function fopen() ซึ่งในการเปิดไฟล์จะต้องระบุโหมดการเข้าถึงไฟล์ (access mode) เพื่อให้โปรแกรมเข้าใจว่าเป็นการเปิดเพื่อ อ่าน หรือเขียน หรือทั้งสองอย่าง เมื่อใช้เสร็จแล้วก็ควรจะปิดไฟล์เพื่อคืน resource ให้กับระบบ

การเปิดไฟล์

มี Pattern ดังนี้

ตัวอย่างการใช้งาน

access mode

ความหมาย

r

เปิดไฟล์เพื่ออ่านอย่างเดียว โดยมี cursor อยู่ที่หัวไฟล์

w

สร้างไฟล์เพื่อเขียน หากไฟล์มีอยู่แล้วจะเกิดการเขียนทับ

a

เปิดเพื่อเพิ่มข้อมูลที่ ท้ายไฟล์ (end-of-file) หากไฟล์ไม่เคยมีอยู่จะสร้างและเขียนลงไป

r+

เปิดไฟล์เพื่ออ่านและเขียน โดยมี cursor อยู่ที่หัวไฟล์

w+

สร้างไฟล์เพื่ออ่านและเขียน โดยมี cursor อยู่ที่หัวไฟล์ หากไฟล์มีอยู่แล้วจะถูกเขียนทับ

a+

เปิดไฟล์เพื่ออ่านและเขียน โดยการอ่านจะมี cursor อยู่ที่หัวไฟล์ แต่การเขียนจะเป็นการเขียนข้อมูลต่อท้ายไฟล์ หากไฟล์ไม่เคยมีอยู่จะสร้างและเขียนลงไป

การเปิดไฟล์ด้วย function fopen() หากเปิดไฟล์ไม่สำเร็จ จะได้ค่าตอบกลับมาเป็น NULL

การปิดไฟล์

จะใช้ function fclose() โดยมี argument เป็น Pointer ของ FILE เช่น

ข้อมูลที่มีการสั่งเขียนและยังคงค้างอยู่ใน Buffer จะถูก flush (เขียน) ลงไฟล์ แล้วจึงปิดไฟล์ หน่วยความจำสำหรับ input buffer และ output buffer จะถูกคืนให้กับระบบ

การปิดไฟล์ด้วย function fclose() หากปิดไฟล์สำเร็จ จะได้ค่าตอบกลับเป็น 0 หากปิดไฟล์ไม่สำเร็จจะได้ค่าตอบกลับเป็น EOF

การอ่านและเขียนไฟล์มี 3 แบบ

  • ดำเนินการทีละตัวอักษร

  • ดำเนินการทีละ string

  • ดำเนินการทีละ Block ของข้อมูล

ฟังก์ชันอ่านทีละตัวอักษร

จะ return เป็นเลข ASCII ของอักษรที่ถูกอ่าน หากมี error จะ return เป็น EOF

ฟังก์ชันเขียนทีละตัวอักษร

จะ return เป็นเลข ASCII ของอักษรที่ถูกเขียน หากมี error จะ return เป็น EOF

ตัวอย่างการใช้งาน

ฟังก์ชันอ่านทีละ string

ฟังก์ชันจะอ่านข้อมูลจากไฟล์ จนถึง string_length - 1 หรือขึ้นบรรทัดใหม่ และจะเติม NULL character ให้ที่ท้ายข้อมูลที่อ่าน กรณีที่อ่านสำเร็จจะมี return string กรณีอ่านจนจบไฟล์ (End-of-File; EOF) จะ return ค่า NULL

ฟังก์ชันเขียนทีละ string

ฟังก์ชันจะอ่าน NULL character ไปด้วย แต่จะไม่เขียน NULL character ลงไฟล์ ถ้าเขียนสำเร็จจะ return non-negative value แต่ถ้า error จะ return EOF

ตัวอย่างการใช้งาน

ฟังก์ชันอ่านทีละ block

ฟังก์ชันจะอ่านข้อมูลจากไฟล์ โดยจำนวนที่อ่านขึ้นมาในแต่ละรอบมีขนาดเท่ากับที่ระบุใน size และจะอ่านทั้งหมดเท่ากับ N_block รอบ หากอ่านสำเร็จ จะ return จำนวนรอบที่อ่านสำเร็จ ซึ่งจะเท่ากับค่า size หาก error หรืออ่านไปจนสุดไฟล์ จะ return ค่าที่ไม่ตรงกับ size (อาจจะเป็น 0 ได้)

ฟังก์ชันเขียนทีละ block

ฟังก์ชันจะเขียนข้อมูลลงไฟล์ โดยจำนวนที่เขียนในแต่ละรอบมีขนาดเท่ากับที่ระบุใน size และจะเขียนทั้งหมดเท่ากับ N_block รอบ หากเขียนสำเร็จ จะ return จำนวนรอบที่เขียนสำเร็จ ซึ่งจะเท่ากับค่า size หากจำนวนที่ return ไม่ตรงกับที่ระบุใน size แสดงว่าอาจจะเกิด error

ตัวอย่างการใช้งาน

Last updated

Was this helpful?