ภาพจ๊อบรวมๆ 3

ภาพจ๊อบรวมๆ4

ภาพจ๊อบรวมๆ2

วันจันทร์ที่ 31 มกราคม พ.ศ. 2554

Solar cells (โซล่าร์เซลล์)

Solar cells (โซล่าร์เซลล์) PDF พิมพ์
 
โซล่าร์เซลล์ คืออะไร ?
solar_cell

      โซล่าร์เซลล์ คือ อุปกรณ์ที่ใช้ในการเปลี่ยนพลังงานจากแสงอาทิตย์ให้เป็นพลังงานไฟฟ้า โดยไม่มีการเคลื่อนไหวของชิ้นส่วนประกอบใด ๆ    ด้วยกรรมวิธีทางวิทยาศาสตร์ เมื่อแสงอาทิตย์สัมผัสกับซิลิคอนและสารเคมีบางชนิดก็จะเกิดกระแสไฟฟ้าขึ้น                                                                                   
       ในปัจจุบันนี้ บริษัทผู้ผลิตได้ประดิษฐ์แผงที่รวมชิ้นส่วนของโซล่าร์เซลล์ขึ้น โดยแผงไฟจะสามารถผลิตกระแสไฟฟ้าจากแสงอาทิตย์ 12 - 14 โวลต์ ซึ่งสามารถนำกระแสไฟฟ้าไปใช้ได้โดยตรงทันที หรือจะอัดไว้ในหม้อแบตเตอรี่ธรรมดาขนาดดังกล่าว ซึ่งใช้กันทั่วไปในรถยนตร์ หรือรถมอเตอร์ไซค์ทั่วไป และนำกระแสไฟฟ้านั้นไว้ใช้ในเวลากลางคืน
       เมื่อมีความต้องการใช้กระแสไฟฟ้ามากขึ้น ก็สามารถเพิ่มอัดไฟฟ้าได้โดยการเพิ่มแผงไฟขึ้น การเพิ่มแผงไฟนี้ทำได้ง่ายมาก เพียงแต่นำแผงไฟมาวางเรียงกัน และต่อสายไฟขนานกันไปเท่านั้น ก็จะได้กระแสไฟฟ้าเพิ่มขึ้น
      วัตถุที่ใช้หนาประมาณครึ่งเซนติเมตร น้ำหนัก .65 กิโลกรัม ต่อ 1 แผ่น ใช้ได้ในที่ร้อนที่สุด เช่น กลางทะเลทรายและที่หนาวที่สุด เช่น ขั้วโลกเหนือและขั้วโลกใต้
การติดตั้งที่ง่าย โดยวางลงบนแผงไม้หรือโครงเหล็ก และหันให้รับแสงอาทิตย์ให้ได้มากที่สุดและนานที่สุดด้วย

ไอซี 555

ไอซี 555

 
 
ไอซี 555 เบอร์ NE555 จาก Signetics ตัวถังสี่เหลี่ยม (DIP)
บล็อกไดอะแกรม และตำแหน่งขา
ไอซี 555 (อังกฤษ: IC 555) เป็นวงจรรวม หรือวงจรเบ็ดเสร็จ ที่เรียกกันทั่วไปว่า ชิป ที่รู้จักกันดีในบรรดานักอิเล็กทรอนิกส์ ไอซีตัวนี้ได้รับการออกแบบ และประดิษฐ์โดยนักออกแบบชิปที่มีชื่อเสียง ชื่อนั่นคือนายฮันส์ อาร์ คาเมนซินด์ (Hans R. Camenzind) โดยเริ่มออกแบบเมื่อ พ.ศ. 2513 และแนะนำผลิตภัณฑ์ในปีถัดมา โดยบริษัทซิกเนติกส์ คอร์ปอเรชัน (Signetics Corporation) มีหมายเลขรุ่น SE555/NE555 และเรียกชื่อว่า "The IC Time Machine" มีการใช้อย่างกว้างขวาง ทั้งนี้เพราะสามารถใช้งานง่าย ราคาถูก มีเสถียรภาพที่ดี ในปัจจุบันนี้ บริษัทซัมซุงของเกาหลี สามารถผลิตได้ปีละกว่า 1 พันล้านตัว (ข้อมูล พ.ศ. 2546)
ไอซีไทเมอร์ 555 นับเป็นวงจรรวมที่สามารถใช้งานได้หลากหลายและเป็นที่นิยมมากที่สุดตัวหนึ่งเท่าที่เคยผลิตมา ภายในตัวประกอบด้วยทรานซิสเตอร์ 23 ตัว, ไดโอด 2 ตัว และรีซิสเตอร์อีก 16 ตัว เรียงกันบนชิปซิลิกอนแผ่นเดียว โดยติดตั้งในตัวถัง 8 ขา แบบมินิ DIP (dual-in-line package) นอกจากนี้ยังมีการผลิตไอซี 556 ซึ่งเป็น DIP แบบ 14 ขา โดยอาศัยการรวมไอซี 555 จำนวน 2 ตัวบนชิปตัวเดียว ขณะที่ 558 เป็นไอซีอีกตัวหนึ่งที่พัฒนาขึ้นจาก 555 เป็น DIP แบบ 16 ขา (quad) โดยรวมเอา 555 จำนวน 4 ตัว (โดยมีการปรับแต่งเล็กน้อย) มาไว้บนชิปตัวเดียว (DIS และ THR มีการเชื่อมต่อกันภายใน ส่วน TR นั้นมีค่าความไวที่ขอบแทนที่จะเป็นความไวทั้งระดับ) นอกจากนี้ยังมีรุ่นกำลังต่ำพิเศษ (ultra-low power) ของไอซี 555 นั่นคือ เบอร์ 7555 สำหรับไอซี 7555 นี้จะมีการเดินสายที่แตกต่างไปเล็กน้อย ทั้งยังมีการใช้กำลังไฟที่น้อยกว่า และอุปกรณ์ภายนอกน้อยกว่าด้วย
ไอซี 555 มีโหมดการทำงาน 3 โหมด ดังนี้
  • โมโนสเตเบิล (Monostable) ในโหมดนี้ การทำงานของ 555 จะเป็นแบบซิงเกิ้ลช็อต หรือวันช็อต (one-shot) โดยการสร้างสัญญาณครั้งเดียว ประยุกต์การใช้งานสำหรับการนับเวลา การตรวจสอบพัลส์ สวิตช์สัมผัส ฯลฯ
  • อะสเตเบิล (Astable) ในโหมดนี้ การทำงานจะเป็นออสซิลเลเตอร์ การใช้งาน ได้แก่ ทำไฟกระพริบ, กำเนิดพัลส์, กำเนิดเสียง, เตือนภัย ฯลฯ
  • ไบสเตเบิล (Bistable) ในโหมดนี้ ไอซี 555 สามารถทำงานเป็นฟลิปฟล็อป (flip-flop) ถ้าไม่ต่อขา DIS และไม่ใช้คาปาซิเตอร์ ใช้เป็นสวิตช์ bouncefree latched switches เป็นต้น

การใช้งาน

ขาของไอซีแต่ละขา มีหน้าที่ดังต่อไปนี้
ขาชื่อหน้าที่
1GNDกราวด์ หรือ คอมมอนส์
2TRพัลส์สั้นกระตุ้นทริกเกอร์เพื่อเริ่มนับเวลา
3Qช่วงการนับเวลา เอาต์พุตจะอยู่ที่ +VCC
4Rช่วงเวลานับ อาจหยุดโดยการใช้พัลส์รีเซ็ต
5CVแรงดันควบคุมยอมให้เข้าถึงตัวหารแรงดันภายใน (2/3 VCC)
6THRเทรสโฮลด์ที่จุดช่วงเวลานับ
7DISเชื่อมต่อกับคาปาซิเตอร์ตัวหนึ่ง ซึ่งเวลาคายประจุของมันจะมีผลต่อช่วงเวลาการนับ
8V+, VCCแรงดันจ่ายไฟบวก ซึ่งต้องอยู่ในช่วง +5 ถึง + 15 V
เมื่อใช้คาปาซิเตอร์ และรีซิสเตอร์มาต่อร่วม จะสามารถปรับช่วงการตั้งเวลา (นั่นคือ ช่วงเวลาที่เอาต์พุตมีค่าต่ำ) ตามความต้องการใช้งานได้
สำหรับการเชื่อมต่อเป็นดังนี้
Example 555 schematic
แผนผังการเชื่อมต่อไอซี 555
ช่วงเวลา t คำนวณได้จาก
t = 1.1RC
ซึ่งเป็นเวลาที่ใช้เพื่อประจุตัวเก็บประจุให้ได้ 63% ของแรงดันที่จ่าย (ค่าจริง : (1-1/e) V) ดูเพิ่มเติมที่ วงจรอาร์ซี (RC circuit) สำหรับคำอธิบายของปรากฏการณ์ดังกล่าว

 ข้อมูลจำเพาะ

ข้อมูลจำเพาะต่อไปนี้เป็นของรุ่น NE555 สำหรับไอซีไทเมอร์ 555 รุ่นอื่นๆ อาจมีข้อมูลจำเพาะที่แตกต่างออกไป ขึ้นกับเกรดการใช้งาน เช่น เกรดกองทัพ หรือทางการแพทย์ เป็นต้น)
แรงดันจ่าย (VCC)4.5 to 15 V
กระแสจ่ายต่ำสุด (VCC = +5 V)3 to 6 mA
กระแสจ่ายสูงสุด (VCC = +15 V)10 to 15 mA
กระแสขาออก (สูงสุด)200 mA
กำลังไฟฟ้า600 mW
อุณหภูมิการทำงาน0 to 70° C

 ผู้ผลิตรายต่างๆ

มีผู้ผลิตไอซี 555 หลายรายด้วยกัน รวมทั้งรุ่นซีมอส (CMOS) โดยแต่ละบริษัทจะกำหนดเบอร์ต่างๆ กันดังนี้
ผู้ผลิตเบอร์
ECG PhilipsECG955M
ExarXR-555
FairchildNE555/KA555
HarrisHA555
IntersilSE555/NE555
Lithic SystemsLC555
MaximICM7555
MotorolaMC1455/MC1555
NationalLM1455/LM555C
NTE SylvaniaNTE955M
RaytheonRM555/RC555
RCACA555/CA555C
SanyoLC7555
Texas InstrumentsSN52555/SN72555

ออสซิลโลสโคป

ออสซิลโลสโคป 

ออสซิลโลสโคปหรือเรียกสั้นๆ ว่า "สโคป" (Scope) มีชื่อเต็มมาจาก แคโทดเรย
ออสซิลโลสโคป (Cathode ray oscilloscope ; CRO ) หมายถึงออสซิลโลสโคปใช ้หลอดรังสีแคโทด สโคปเป็นเครื่องมือวัดทาง อิเล็กทรอนิกส์ที่สำคัญอีกชนิดหนี่งที่ใช้ในการวัดแสดงรูปคลื่นสัญญาณต่างๆ ออกมาเป็นภาพ ปรากฎบนจอหลอดภาพให้เห็นได้ เช่น การวัดสัญญาณกระแสไฟฟ้าหรือแรงดันไฟฟ้า(ที่เป็นไฟ AC หรือ DC) การวัดความถี่ของ สัญญาณ การวัดเฟสของสัญญาณ และรวมถึงการวัดสัญญาณพัลส์การอ่านค่าแอมพลิจูดของสัญญาณจะเป็น พีค-ทู-พีค หรือค่าพีคและค่าเวลาเป็นวินาที


หลักการทำงานของออสซิลโลสโคป
        ออสซิสโลสโคปจะใช้หลักการบังคับการบ่ายเบนของลำอิเล็กตรอนภายในหลอดภาพรังสีแคโทด (Cathode ray tube ; CRT)
ด้วยระบบการบ่ายเบนทางไฟฟ้าสถิต (Electrostatic deflection)

หน้าที่หลักของออสซิลโลสโคป คือ

1. รับสัญญาณ
2. แสดงภาพของสัญญาณที่รับ
3. วิเคราะห์สัญญาณ


ประโยชน์ของการนำออสซิสโลสโคปไปใช้งาน
1. ใช้วัดแรงดันไฟฟ้าตรง (DC) วัดแรงดันไฟฟ้าสลับ (AC) และกระแสไฟฟ้าของสัญญาณ
2. ใช้วัดค่าเวลา คาบเวลา และความถี่ของสัญญาณ
3. ใช้วัดผลต่างทางเฟสของสัญญาณ และเปรียบเทียบสัญญาณ 2 สัญญาณ
4. ใช้วัดตรวจสอบวงจรอิเล็กทรอนิกส์เกี่ยวกับความถี่และรูปคลื่นสัญญาณที่ถูกต้อง เช่น การปรับจูนเครื่องรับ-ส่งวิทยุ เครื่องรับโทรทัศน์ วิดีโอ เครื่องเสียง เป็นต้น
5. ใช้ตรวจเช็คคุณสมบัติของอุปกรณ์อิเล็กทรอนิกส์ว่าดีหรือเสียได้โดยดูจากภาพที่ปรากฎบนจอ
6. นำไปใช้ประกอบร่วมกับอุปกรณ์อื่นเพื่อให้สามาถใช้งานด้านอื่นได้กว้างขวาง
บล็อดไดอะแกรมของออสซิลโลสโคป

แผนภาพบล็อกของออสซิลโลสโคป (Oscilloscope block diagram) ทั่วไป (แบบ 1 ลำแสง) มีส่วนประกอบดังนี้
ส่วนประกอบแผนภาพของบล็อกของออสซิสโลสโคป คือ
1. Cathode ray tube, or CRT
2. Vecital amplifier
3. Delay line
4. Time base generator
5. Horizontal amplifier
6. Trigger circuit
7. Power supply

อธิบายแผนภาพบล็อก
1. หลอดรังสีแคโทดหรือเรียกสั้นๆ ว่า "CRT" ถือว่าเป็นหัวใจของออสซิลโลสโคป ด้านในหลอดภาพฉาบด้วยสารเรืองแสง เมื่ออิเล็กตรอนวิ่งกระทบจุดใดจุดนั้นจะเรืองแสงขึ้นจึงเกิดเป็นภาพให้เห็น
2. ภาคขยายสัญญาณทางแนวตั้ง (Vertical amplifier) ทำหน้าที่ขยายสัญญาณทางแนวตั้งให้มีความแรงขึ้นที่ส่งมาจากภาคลดทอนแรงดัน (Volt/DIV) ทางแนวตั้ง
3. ภาคหน่วงสัญญาณ (Delay line) จะทำหน้าที่หน่วงสัญญาณอินพุตทางแนวตั้ง (Vertical input) ก่อนที่จะป้อนเข้าแผ่นเพลตบ่ายเบนทางแนวตั้ง (Vertical deflection plate) ใช้เวลาการหน่วงประมาณ 0.25 ms
4. เครื่องกำเนิดฐานเวลา (Time base generator) หรือตัวกำเนิดการกวาด (Sweep generator) ทำหน้าที่ควบคุมรูปคลื่นฟันเลื่อย (Sawtooth waveform) ให้มีความถูกต้อง (โดยปรับปุ่ม Time/DIV) ก่อนป้อนให้กับภาคขยายสัญญาณทางแนวนอน (Horizontal amplifier)
5. ภาคขยายสัญญาณทางแนวนอนทำหน้าที่ขยายสัญญาณรูปฟันเลื่อยให้มีความแรงพอก่อนป้อนเข้าแผ่นเพลตบ่ายเบนทางแนวนอน ทำให้เกิดสนามไฟฟ้าสถิตควบคุมลำอิเล็กตรอนให้บ่ายเบนซ้ายขวาทางแนวนอนได้
6. ภาควงจรชุดชนวน (Trigger circuit) หรือวงจรซิงค์ (Synce circuit) ทำหน้าที่ควบคุมให้สัญญาณแนวนอนทำงานพร้อมกับสัญญาณแนวตั้ง ทำให้ภาพบนจอหยุดนิ่ง โดยรับสัญญาณมาจากภาคขยายแนวตั้งมาทำเป็นสัญญาณพัลส์ไปควบคุมรูปคลื่นฟันเลื่อยของภาคกำเนิดฐานเวลาให้มีความถี่ที่ถูกต้อง
7. ภาคจ่ายไฟ (Power supply) แรงดันไฟต่ำ (Low voltage; LV) จะจ่ายไฟให้กับทุกวงจร ยกเว้น แรงดันไฟสูง (High voltage ; HV) จ่ายให้กับหลอด CRT

หลักการเกิดภาพบนจอออสซิสโลสโคป-

การเกิดรูปสัญญาณที่จอออสซิลโลสโคป (หลอด CRT) อาศัยหลักการทำงานของ 2 ภาคใหญ่ๆ คือ
1. การบ่ายเบนสัญญาณทางแนวตั้ง (Vertical deflection)
2. การบ่ายเบนสัญญาณทางแนวนอน (Horizontal deflection)
การป้อนสัญญาณเข้าที่ชุดแผ่นเพลตบ่ายเบนทั้งแนวตั้งและแนวนอนโดยตรงจะต้องมีค่าแรงดันไฟฟ้าที่สูงมาก เพื่อให้ลำอิเล็กตรอนเกิดบ่ายเบนไปถึงหน้าจอที่ฉาบด้วยสารเรืองแสง ดังนั้นถ้ากรณีที่สัญญาณเข้าเป็นแรงดันไฟฟ้าค่าต่ำๆ ก่อนเข้าแผ่นเพลตบ่ายเบนทั้ง 2 ชุด โดยการขยายสัญญาณดังกล่าวเสียก่อน เรียกว่าวงจรขยายสัญญาณทางแนวตั้งและวงจรขยายสัญญาณทางแนวนอนจะเห็นการแสกน (Scan) ของรูปคลื่นไซน์ที่ป้อนเข้าทางแนวตั้งและรูปคลื่นฟันเลื่อยเข้าทางแนวนอน ภาพที่จะปรากฎจะเป็นการเริ่มต้นสแกนของจุดลำแสงอิเล็กตรอนที่จอหลอดภาพวิ่งจากซ้ายไปขวา เริ่มจากตำแหน่งศูนย์เหมือนกันจนกระทั่งถึงจุดสูงสุด (เลข 8) ของสัญญาณคลื่นไซน์และฟันเลื่อย จากนั้นจุดลำแสงอิเล็กตรอนบนจอหลอด CRT จะวิ่งกลับจากตำแหน่งสูงสุด (ขวาสุด) มายังซ้ายสุดด้วยความเร็วที่สูงมากเราจึงเห็นรูปสัญญาณคลื่นไซน์ปรากฎหน้าจอ


หลอดรังสีแคโทด
หลอดรังสีแคโทด (Cathode ray tube ; CRT) หรือ CRT เป็นอุปกรณ์ที่ใช้แสดงค่ารูปร่างของสัญญาณที่วัดได้บนหน้าจอ หลอด CRT มีโครงสร้างภายใน (ดังรูป)





ส่วนประกอบภายในหลอด CRT แบ่งได้เป็น 4 ส่วน คือ
1. ชุดปืนอิเล็กตรอน (Electron gun assembly)
2. ชุดแผ่นเพลตบ่ายเบน (Deflection plate assembly)
3. จอภาพเรืองแสง (Fluorescent screen)
4. ตัวหลอดแก้วและขั้วหลอด (Glass envelope and base of the tube)
1. ชุดปืนอิเล็กตรอน ในส่วนนี้จะทำหน้าที่คล้ายกับหลอดไทรโอด (Triode) คือ แคโทดจะเป็นตัวยิงอิเล็กตรอนไปยังจอภาพ และกริดเป็นตัวควบคุมการจ่ายอิเล็กตรอน และมีแอโนดเป็นตัวรับอิเล็กตรอนจะสามารถวิ่งผ่านรูเล็กๆ นี้ได้ มีลักษณะเป็นลำอิเล็กตรอน ขนาดของลำอิเล็กตรอนสามารถปรับหรือควบคุมได้โดยใช้ปุ่มปรับความเข้ม การปรับปรุงดังกล่าวเป็นการปรับแรงดันไฟลบที่กริด ถ้ากริดเป็นไฟลบมากลำอิเล็กตรอนก็จะลดลงทำให้ความเข้มที่จอภาพลดลงและถ้ากริดเป็นไฟลบน้อย ลำอิเล็กตรอนจะมาก ความเข้มที่จอภาพก็มากตาม
เมื่ออิเล็กตรอนผ่านกริดควบคุมจะถูกเร่งความเร็วโดยขั้วแอโนดเร่งความเร็ว (Accelerating anode) 2 แผ่นที่มีศักย์ไฟฟ้าบวกค่าสูงป้อนให้ ระหว่างแอโนดทั้งสองจะมีแอโนดโฟกัส (Focusing anode) คั่นอยู่ซี่งจะทำหน้าที่ช่วยปรับให้อิเล็กตรอนเป็นลำขนาดเล็กและคมชัดขึ้น ลำอิเล็กตรอนที่ถูกปรับโฟกัสและถูกเร่งความเร็วแล้วจะเคลื่อนที่ผ่านเพลตบ่ายเบนแนวตั้งและแนวนอนพุ่งไปกระทบจอเรืองแสงต่อไป
2. ชุดแผ่นเพลตบ่ายเบน (Deflection plate assembly) ประกอบด้วยแผ่นเพลตโลหะ 2 แผ่น วางคู่ขนานกันทั้งทางแนวตั้งและแนวนอน โดยมีแรงดันไฟฟ้าป้อนให้กับแผ่นโลหะทั้งสอง เป็นผลทำให้เกิดสนามไฟฟ้าที่มีค่าสม่ำเสมอเกิดขึ้นระหว่างแผ่นโลหะนี้ เมื่อลำอิเล็กตรอนที่ถูกเร่งความเร็วผ่านเข้ามายังแผ่นบ่ายเบนเป็นผลทำให้ได้รับอิทธิพลจากสนามไฟฟ้าภายในดังกล่าวจะเกิดแรงที่กระทำต่ออิเล็กตรอน
3. จอภาพเรืองแสง (Fluorescent screen) คือจอด้านหน้าสุดของหลอด CRT ที่ฉาบด้วยสารเรืองแสงจำพวกฟอสเฟอร์ (Phospher) ซึ่งเป็นสารทำให้เกิดแสงสว่างบนจอภาพ โดยจะเปลี่ยนพลังงานไฟฟ้าให้เป็นพลังงานแสงเพมื่อถูกวิ่งชนด้วยลำอิเล็กตรอนที่มีความแรงและกำลังสูง
คุณสมบัติของสารในการปล่อยแสง เมื่อได้รับการกระตุ้น (ชน) โดยลำอิเล็กตรอนเรียกว่า "ฟลูออเรสเซนต์" ความสามารถของสารในการปล่อยแสงออกมาอย่างต่อเนื่อง แม้ว่าจะหมดลำอิเล็กตรอนแล้วก็ตามเราเรียกว่า "ฟอสเฟอเรสเซนซ์" (Phospherescence) และช่วงระยะเวลาที่เกิดการเรืองแสงต่อเนื่องเรียกว่า "ความคงสว่าง" (Persistence) ของสารเรืองแสง

4. ตัวหลอดแก้วและขั้วหลอด (Glass envelope and bade of the tube)

- ประเภทของหลอด CRT
ถ้าแบ่งตามจำนวนของลำแสงอิเล็กตรอนภายในหลอดจะแบ่งออกเป็น
1.1 ลำแสงเดียว (Single-beam)
2.2 ลำแสงคู่ (Dual-beam)
3.3 หลายลำแสง (Multi-beam)


สายโพรบกับออสซิลโลสโคป
โพรบ (Probe)
การวัดสัญญาณอินพุตของสโคปนั้นจำเป็นต้องผ่านทางสายเคเบิลแกนร่วม (Coaxial cable) ซึ่งสายเคเบิลนี้เราเรียกว่า "สายโพรบ"
โพรบเป็นอุปกรณ์ที่ใช้สำหรับตรวจวัดสัญญาณ ถือว่าเป็นอุปกรณ์ส่วนหน้าในการวัดสัญญาณที่ต้องการตรวจสอบ โดยไม่ให้เกิดการรบกวนของสัญญาณขณะใช้งาน แล้วส่งผ่านสัญญาณให้อินพุตทางแนวตั้ง
ส่วนประกอบของโพรบมี 2 อย่างคือ สายนำสัญญาณและกราวนด์ ทั้ง 2 อย่างนี้ประกอบกันโดยมีการแบ่งเป็นชั้นๆ กราวนด์จะถูกถักเป็นเปียพันรอบสายสัญญาณที่มีฉนวนหุ้มทั้งหมดอีกครั้ง สัญญาณจะผ่านตัวนำที่อยู่ตรงกลางและกราวนด์ที่อยู่รอบนอกจะเป็นตัวป้องกันสัญญาณที่ไม่ต้องการผ่านเข้าไปได้
โพรบมีอยู่หลายแบบขึ้นอยู่กับการนำไปใช้งาน เช่น โพรบแบบพาสซีฟ (Passive probe) โพรบแบบแอกทีฟ (Active prove) โพรบวัดกระแสไฟฟ้า (Current-probe) และโพรบวัดแรงดันไฟสูง (High voltage probe) เป็นต้น
โพรบที่นิยมใช้มากที่สุดจะเป็นโพรบแบบพาสซีฟ โดยเป็นแบบไม่มีการลดทอนสัญญาณ (Probe x 1) หรือ 1 : 1 และแบบลดทอนสัญญาณลง 10 เท่า (Probe x 10) หรือ10 : 1
สายโพรบนี้จะมีค่าความจุไฟฟ้า (Capacitance ; Ccc) ต่อคร่อมอยู่เพื่อป้องกันสัญญาณความถี่สูงที่จะเข้าไปปรากฎบนจอภาพ ส่วนด้านอินพุตของสโคปจะมีค่าความต้านทาน (Ri) ต่อขนานอยู่กับค่าความจุไฟฟ้า (Ci)
บล็อคไดอแกรมของสายโพรบ

ไมโครโปรเซสเซอร์

สถาปัตยกรรมไมโคร

 
สถาปัตยกรรมไมโครของ 80286
สถาปัตยกรรมไมโคร (อังกฤษ: microarchitecture) เป็นลักษณะของ เครื่องคอมพิวเตอร์ส่วนใหญ่ที่มีการใช้งานมาตั้งแต่ยุคเริ่มต้น จนถึงยุคปัจจุบัน ได้รับการออกแบบโครงสร้างและการทำงานโดยจอนวอนนิวแมน(John Von Neumann ) ซึ่งเป็นผู้นำในการออกแบบเครื่องคอมพิวเตอร์ โดยเครื่องคอมพิวเตอร์ ที่เขาได้ออกแบบ มีส่วนประกอบที่สำคัญ 3 อย่างด้วยกันคือ หน่วยประมวลผลกลาง (Central Processing Unit), หน่วยความจำ (Main Memory) และ หน่วยเชื่อมต่ออุปกรณ์ภายนอก (Input/Output) สำหรับไมโครโปรเซสเซอร์ ในตระกูล x86 ออกแบบโดยใช้หลักการเดียวกันกับที่ วอนนิวแมนได้กำหนดไว้ กล่าวคือ การประมวลผลทั้งหมด ที่หน่วยประมวลผลกลาง สำหรับข้อมูล ไม่ว่าจะเป็นข้อมูลที่ใช้สำหรับประมวลผล หรือข้อมูลที่เป็นคำสั่งก็ตาม จะถูกเก็บไว้ในหน่วยความจำจนกว่าจะมีการเรียกใช้งานจากหน่วยประมวลผลกลาง สำหรับส่วนต่อเชื่อมกับอุปกรณ์ภายนอกนั้น การทำงานของหน่วยประมวลผลกลางก็จะคล้ายกับการทำงานกับหน่วยความจำ ทั้งนี้เพราะทั้งหน่วยความจำและอุปกรณ์ภายนอก เมื่อมีการทำงานกับหน่วยประมวลผลกลางก็มีลักษณะของการเรียกใช้ข้อมูลและนำข้อมูลไปเก็บ ต่างกันเพียงสถานที่ และ วิธีการเข้าถึงเท่านั้น บัสของระบบ หน่วยงานต่าง ๆ ภายในเครื่องคอมพิวเตอร์นั้น ต่อเชื่อเข้าด้วยกันดัวยระบบบัส สำหรับโปรเซสเซอร์ในตระกูล 80x86 มีด้วยกัน 3 กลุ่มอันได้แก่ ดาตาบัส แอดเดรสบัส และคอมโทรลบัส ดังรายละเอียดต่อไปนี้

 ดาต้าบัส

เป็นสายนำสัญญาณข้อมูลของระบบ โดยมีทั้งขนาด 8 บิต , 16 บิต , 32 บิต และ 64 บิต ทั้งนี้ขึ้นอยู่กับรุ่นของไมโครโปรเซสเซอร์เอง ในกรณีนี้จำนวนสายสัญญาณของระบบบัส ไม่ได้เป็นข้อกำหนดขนาดความสามารถของตัวโปรเซสเซอร์ เช่น ในกรณีของ 80x88 มีขนาดของบัวเป็น 8 บิต ก็ไม่ได้หมายความว่า สามารถประมวลผลขนาด 8 บิต เป็นแต่เพียงว่าใน 1 จังหวะสัญญาณนาฬิกาจะ รับ-ส่ง ข้อมูลขนาด 8 บิต ดังนั้นเพื่อให้การรับส่งข้อมูลครบทั้ง 16 บิตก็ต้องใช้สัญญาณนาฬิกาจำนวน 2 ครั้งนั่นเอง ขนาดของดาต้าบัสแสดงในตาราง

 แอดเดรสบัส

เป็นระบบบัสที่ใช้เพื่อส่งข้อมูลเพื่อกำหนดตำแหน่งของหน่วยความจำ และอุปกรณ์ภายนอก โดยที่แอดเดรสบัส กำหนดที่อยู่ และ ดาต้าบัสเป็นเส้นทางนำเข้าและส่งออกข้อมูลประมวลผล โดยที่จำนวนของ สายสัญญาณของแอดเดรสบัส จะเป็นตัวบอกถึงความสามารถในการระบุตำแหน่งที่ไมโครโปรเซสเซอร์สามารถกำหนดได้ดังในตาราง
โปรเซสเซอร์ จำนวนของบัส หน่วยความจำสูงสุด หน่วย
8088 20 1.048,576 1 MB
80188 20 1,048,576 1 MB
8086 20 1,048,576 1 MB
80186 20 1,048,576 1 MB
80286 24 16,777,216 16 MB
80386SX 24 16,777,216 16 MB
80386DX 32 4,294,976,296 4 GB
80486 32 4,294,976,296 4 GB
80586 32 4,294,976,296 4 GB

บัสควบคุมการทำงาน

เป็นระบบสายสัญญาณที่ควบคุมการทำงานทั้งหมดของระบบ โดยมีจำนวนสายที่ไม่เท่ากันในแต่ละ โปรเซสเซอร์ ซึ่งขึ้นอยู่กับการออกแบบของโปรเซสเซอร์เหล่านั้น และหน้าที่ของสายสัญญาณแต่ละเส่นก็จะแตกต่างกันออกไป เช่น ในการอ่าน และ บันทึกข้อมูลไปยังหน่วยงานต่าง ๆ ก็จะมีข้อมูล รับ-ส่ง กันไปมา แล้วจะทราบได้อย่างไรว่าขณะนี้กำลังอ่านข้อมูลจากหน่วยความจำ หรือว่ากำลังนำเอาข้อมูลไปเขียนเก็บไว้ในหน่วยความจำ ดังนั้นจึงต้องมีสายสัญญาณที่กำหนดว่า กำลัง อ่าน (read) หรือ เขียน (write) ข้อมูล เป็นต้น สำหรับเครื่องไมโครคอมพิวเตอร์โดยทั่ว ๆ ไปที่ใช้งานในปัจจุบัน ไม่ว่าจะเป็นเครื่อง IBM PC และ Compatible จะใช้ไมโครโปรเซสเซอร์ตระกูล 80x86 ของบริษัท Intel ซึ่ง เริ่มจาก 8086 , 80286 , 80386 , 80486 และเป็น Pentium ในปัจจุบัน ดังนั้นจึงเป็นการเหมาะสมอย่างมากที่จะใช้เพื่อการศึกษาการเขียนโปรแกรมภาษา Assembly โดยอ้างอิงกับไมโครโปรเซสเซอร์ของ Intel เนื่องจากมีใช้อย่างแพร่หลาย ถึงแม้ว่าจะมีไมโครโปรเซสเซอร์ของผู้ผลิตรายอื่น ๆ บ้าง เช่น ของบริษัท AMD แต่เราก็ยังสามารถใช้คำสั่งชุดเดียวกับที่ใช้กับ ไมโครโปรเซสเซอร์ของบริษัท INTEL ได้ สำหรับการศึกษาในรหัสวิชานี้จะศึกษาเฉพาะการทำงานในแบบ real mode เท่านั้น คือพิจารณาในส่วนของโปรเซสเซอร์ 8086 หรือ 8088 โดยจะศึกษาโครงสร้างรายละเอียดของ รีจิสเตอร์ ซึ่งอยู่ภายในไมโครโปรเซสเซอร์ ซึ่งมีหน้าที่ในการจัดเก็บข้อมูล โดยการจัดแบ่งกลุ่มของ รีจิสเตอร์ นั้นไปตามหน้าที่ของการทำงานของ รีจิสเตอร์ แต่ละตัวดังรายละเอียดต่อไปนี้ 3.2. Data รีจิสเตอร์ เป็น รีจิสเตอร์ ที่ใช้เก็บข้อมูลทั่วไป ถึงแม้ว่าในการทำงานของระบบคอมพิวเตอร์ จะสามารถใช้หน่วยความจำเป็นที่เก็บข้อมูลได้ แต่ว่าการประมวลผลนั้นจะเกิดขึ้นภายในตัวไมโครโปรเวสเซอร์ ดังนั้นจึงต้องใช้ รีจิสเตอร์ ที่อยู่ภายในตัวไมโครโปรเซสเซอร์เป็นที่เก็บข้อมูลและทำให้การประมวลผลมีความเร็วสูงขึ้น สำหรับ รีจิสเตอร์ ที่ใช้ประมวลผลทั่วไป เป็นขนาด 16 บิต มีจำนวน 4 ตัว คือ AX , BX , CX และ DX ทั้งนี้ รีจิสเตอร์ ทั้ง 4 ตัวนี้ สามารถที่จะใช้งานครั้งละ 8 บิตได้โดยแบ่งใช้งานทีละครึ่ง โดยมีชื่อเรียกใช้งานเป็น AH และ AL และสำหรับ รีจิสเตอร์ ที่เหลืออีก 3 ตัว จะมีวิธีการเรียกชื่อในทำนองเดียวกัน โดย รีจิสเตอร์ AH เป็นการใช้ รีจิสเตอร์ บิตที่ 8-15 และ AL เป็นการใช้ รีจิสเตอร์ บิตที่ 0-7 ดังแสดงในรูปที่ 3.1 สำหรับ รีจิสเตอร์ ทั้ง 4 ตัวนี้ เป็นแบบใช้งานทั่วไป แต่บางกรณีอาจนำไปใช้เป็นกรณีเฉพาะด้วยเช่น
15 8 7 0 AX AH AL BX BH BL CX CH CL DX DH DL รูปที่ 3.1 แสดงรายละเอียดของรีจิสเตอร์แต่ละตัว 3.2.1. รีจิสเตอร์ AX ( Accumulator รีจิสเตอร์) ใช้เป็น รีจิสเตอร์ ที่ใช้ในการประมวลผลคำสั่งทางคณิตศาสตร์ เช่น บวก ลบ คูณ หาร และ การกระทำทางลอจิค นอกจากนี้ยังใช้ในการโอนย้ายข้อมูลด้วยเนื่องจากใช้คำสั่งที่สั้น และยังใช้งานกับกลุ่มคำสั่ง อินพุทและเอาต์พุต 3.2.2. รีจิสเตอร์ BX ( Base รีจิสเตอร์ ) ใช้เพื่อเป็น รีจิสเตอร์ เก็บข้อมูลชนิดที่อยู่ (Address รีจิสเตอร์) เช่นกลุ่มคำสั่งในการค้นหาข้อมูลในตาราง 3.2.3. รีจิสเตอร์ CX ( Counter รีจิสเตอร์ ) .ใช้เพื่อเป็นตัวนับจำนวนรอบของการทำงานของโปรแกรมที่มีลักษณะเป็นวงรอบ 3.2.4. รีจิสเตอร์ DX (Data รีจิสเตอร์ ) ใช้กับกลุ่มคำสั่งคูณและหาร และกับคำสั่งอินพุทและเอาต์พุต สำหรับหน้าที่ต่าง ๆ ที่กล่าวมาแล้วสำหรับรีจิสเตอร์แต่ละตัวนั้น ยังทำหน้าที่หลัก ๆ ที่เหมือนกันอย่างหนึ่งคืก ใช้เพื่อการเก็บข้อมูลชั่วคราวด้วย 3.2.5. เซ็กเมนต์ รีจิสเตอร์ ในการดำเนินการที่ติดต่อกับหน่วยความจำนั้น สำหรับไมโครโปรเซสเซอร์ในตระกูล 8088 นั้นมีจำนวน address bus ขนาด 20 บิต นั่นหมายความว่า สามารถเข้าถึงหน่วยความจำได้ขนาด 1 MB แต่เนื่องจาก รีจิสเตอร์ ภายในทั่งหมดนั้นมีขนาดเพียง 16 บิต ทำให้ไม่สามารถ address ข้อมูลในหน่วยความจำได้โดยตรง จึงต้องมีการจัดเนื้อที่ของหน่วยความจำออกเป็นส่วน ๆ เรียกว่า เซ็กเมนต์ โดยจะใช้ รีจิสเตอร์ จำนวน 2 ตัวในการอ้างถึงหน่วยความจำในแต่ละตำแหน่ง โดย รีจิสเตอร์ ตัวหนึ่งทำหน้าที่เป็นตัวกำหนด เซ็กเมนต์ และ รีจิสเตอร์ อีกตัวหนึ่งทำหน้าที่กำหนดค่าของ offset ทำให้การอ้างถึงข้อมูลจึงมีลักษณะเป็น logical address เช่น 1000:2000 ค่าตัวเลข 1000 หมายถึงส่วนของ เซ็กเมนต์ และค่าตัวเลข 2000 หมายถึงส่วนของ offset แต่หากพิจารณาดู จะพบว่า จำนวนบิตของข้อมูลเกิน 20 บิต ดังนั้นในการอ้าง address ที่ถูกต้องจึงได้จากการคำนวณค่าระหว่าง เซ็กเมนต์ และ offset ดังนี้
10000
                                                            +
                                                   2000
                                                 12000            มีขนาด 20 บิต ตรงกับ Physical address
ไมโครโปรเซสเซอร์ 8088 มี เซ็กเมนต์ รีจิสเตอร์ จำนวน 4 ตัว คือ Code เซ็กเมนต์ (CS) , Data เซ็กเมนต์ (DS) , Stack เซ็กเมนต์ (SS) และ Extra เซ็กเมนต์ (ES) ซึ่ง รีจิสเตอร์ ทั้ง 4 ตัวจะมีหน้าที่การทำงานที่แตกต่างกันออกไป และรับผิดชอบในการดูแลข้อมูลแต่ละกลุ่ม รีจิสเตอร์ทั้ง 4 ตัวนี้มีขนาดเป็น 16 บิต และขนาดของหน่วยความจำที่ไมโครโปรเซสเซอร์สามารถใช้งานได้ใน real mode มีขนาด 1 MB ทำให้มีจำนวน เซ็กเมนต์ ทั้งสิ้น 64 K เซ็กเมนต์ และในแต่ละ เซ็กเมนต์ จะสามารถ address ข้อมูลได้ 64 KB ในการ address หน่วยความจำของแต่ละ เซ็กเมนต์ อาจ กำหนด ให้ เซ็กเมนต์ รีจิสเตอร์ ทั้งหมด ชี้หน่วยความจำทีเดียวกันทั้งหมด หรือ แยกเป็นแต่ละส่วนก็สามรถทำได้ทั้ง 2 กรณี ขึ้นอยู่กับวัตถุประสงค์ของการใช้งานและ ลักษณะในการเขียนโปรแกรม แต่การใช้ เซ็กเมนต์ รีจิสเตอร์ แต่ละตัวก็ต้องพิจารณาให้เหมาะสมกับหน้าที่ และ รีจิสเตอร์ ที่มาใช้งานร่วมกัน (ส่วนของ offset ) เซ็กเมนต์ รีจิสเตอร์ ทั้ง 4 ตัวนี้มีขนาด 16 บิตและไม่สามารถแบ่งใช้งานได้เหมือนกับ รีจิสเตอร์ในกลุ่มของ data รีจิสเตอร์ โดยแต่ละตัวทำหน้าที่แตกต่างกันออกไปดังนี้คือ 3.2.5.1. รีจิสเตอร์ CS เป็น เซ็กเมนต์ รีจิสเตอร์ ที่ใช้เป็นตัวกำหนดที่อยู่ของหน่วยความจำในส่วนของคำสั่ง (Instruction) โดยจะมีการทำงานร่วมกับรีจิสเตอร์ IP 3.2.5.2. รีจิสเตอร์ SS เป็น เซ็กเมนต์ รีจิสเตอร์ ที่กำหนดที่อยู่ของ stack ซึ่งเป็นบริเวรหน่วยความจำที่กำหนดไว้เพื่อใช้เก็บข้อมูล โดยมีลักษณะการทำงานแบบ stack ใช้งานร่วมกับรีจิสเตอร์ SP 3.2.5.3. รีจิสเตอร์ DS และ ES ใช้กำหนดที่อยู่ของ ข้อมูลเมื่อทำการประมวลผลกับชุดคำสั่งกลุ่มข้อมูล โดยจะใช้งานร่วมกับ รีจิสเตอร์ BP และ DX 3.2.6. Pointer และ Index รีจิสเตอร์ เป็น รีจิสเตอร์ ที่ใช้ในการชี้ตำแหน่งข้อมูลในหน่วยความจำ โดยจะเป็นส่วนของ offset ประกอบด้วย รีจิสเตอร์ จำนวน 4 ตัวคือ 3.2.6.1. Stack pointer ( SP ) เป็น รีจิสเตอร์ ที่เก็บข้อมูลตำแหน่งหน่วยความจำในส่วนของ offset เพื่อใช้ชี้ตำแหน่งของ stack โดยใช้งานร่วมกับ Stack เซ็กเมนต์ (SS) 3.2.6.2. Base Pointer (BP) ใช้สำหรับ อ้างตำแหน่งข้อมูลในหน่วยความจำของ stack แตกต่างกับ SP ตรงที่สามารถเข้าถึงข้อมูลใน เซ็กเมนต์ อื่น ๆ ได้ 3.2.6.3. Source Index (SI) ใช้ในการอ้างตำแหน่งข้อมูล ในหน่วยความจำ โดยใช้งานร่วมกับ DS ส่วนใหญ่จะนำไปใช้งานกับกลุ่มคำสั่งประเภทกลุ่มข้อมูล 3.2.6.4. Destination Index (DI) มีการใช้งานเช่นเดียวกับ SI แต่ใช้งานร่วมกับ รีจิสเตอร์ ES 3.2.7. Instruction Pointer (IP) IP เป็น รีจิสเตอร์ ที่ใช้ในการชี้ตำแหน่งของข้อมูลในส่วนของคำสั่ง โดยใช้งานร่วมกับ CS ในการทำงานของไมโครโปรเซสเซอร์ ค่าของ IP จะเพิ่มขึ้นโดยอัตโนมัติ และไม่สามารถเปลี่ยนแปลงค่าโดยการโปรแกรมได้ โดยมันจะชี้ตำแหน่งของคำสั่งถัดไปในหน่วยความจำเสมอ 3.2.8. Flags รีจิสเตอร์ เป็น รีจิสเตอร์ ที่ใช้แสดงผลลัพธ์ของการประมวลของไมโครโปรเซสเซอร์ โดยค่าข้อมูลของ รีจิสเตอร์ ในแต่ละบิตนั้นถูกกำหนดความหมายไว้แตกต่างกันออกไป ซึ่งเรียกว่า flag โดยแบ่ง flag ออกเป็น 2 ประเภทด้วยกันคือ status flag และ control flag โดยที่ status flag ใช้เพื่อแสดงผลของการประมวลผลข้อมูล เช่น บวกข้อมูลแล้วมีตัวทดเกิดขึ้นหรือไม่ (carry flag) หรือ การลบข้อมูลมีผลลัพธ์เป็น 0 (zero flag) แต่ในส่วนของ control flag จะเป็นการควบคุมการทำงานของไมโครโปรเซสเซอร์ เช่น จะยอมให้มีการตอบรับการร้องขอ interrupt หรือไม่ เป็นต้น ซึ่งในรายละเอียดการใช้งาน flag ทั้ง 2 ประเภทนี้จะได้กล่าวถึงต่อไปในภายหลัง
 
หน้าที่ 1 - พื้นฐานโปรแกรมภาษา C
ดร. จันทร์จิรา สินทนะโยธิน, วิศรุต พลสิทธิ
สำนักงานพัฒนาวิทยาศาสตร์และเทคโนโลยีแห่งชาติ (สวทช)


ฝึกฝนและพัฒนาการเขียนโปรแกรมภาษา C
Introduction to C Programming


การพัฒนาโปรแกรมคอมพิวเตอร์ บางคนก็ว่ายาก บางคนก็ว่าเป็นเรื่องสนุก หลายคนบอกว่า ขอเป็นแค่ผู้ใช้สนุกที่สุด แต่จะมีซักกี่คนที่จะมีใจรักที่จะก้าวไปบนถนนแห่งการพัฒนาฝีมือและฝึกฝนการเขียนโปรแกรมด้วยตัวเอง เพื่อให้มีผู้ที่สนใจนำไปใช้งาน และเพิ่มประสิทธิ์ภาพในการทำงาน และ ความสะดวกสบายๆ ต่างๆมากขึ้น ว่าไปแล้วนักโปรแกรมเมอร์เหล่านี้ ก็ไม่แตกต่างจากผู้ที่ปิดทองหลังพระมากนัก เพราะหลายๆ โปรแกรมที่มีให้ใช้งานกันในปัจจุบัน จะมีใครทราบบ้างไหมว่า ผู้เขียนโปรแกรมเหล่านั้นมีใครกันบ้าง ดังนั้น ผู้ที่คิดจะก้าวมาเป็นนักพัฒนาโปรแกรมมืออาชีพ คงต้องอาศัยใจรักที่จะอยากจะพัฒนา และฝึกฝนฝืมือในการเป็นโปรแกมเมอร์มืออาชีพมาเป็นอันดับหนึ่ง สำหรับบทความนี้จะเริ่มต้นด้วยการสอนให้เข้าใจในหลักการพื้นฐานของการการพัฒนาโปรแกรมในภาษา C ความรู้และความเข้าใจที่จำเป็นต่อการเป็นโปรแกรมเมอร์มืออาชีพในอนาคต เราลองเริ่มมาเรียนรู้กันอย่างคร่าวๆ กันเลยล่ะกัน โดยผู้เขียนจะอธิบายเป็นตอนๆ ทั้งหมด 8 ตอนด้วยกันได้แก่

1. พื้นฐานโปรแกรมภาษา C (Introduction to C Programming)
2. การเขียนโปรแกรมทางเลือก (Selection Structures)
3. การเขียนโปรแกรมแบบ วนซ้ำ (Repetition & Loop)
4. ฟังก์ชัน และการเขียนโปรแกรมแยกเป็นโมดูล (Functions & Modular Programming)
5. ตารางอาเรย์ (Arrays)
6. ตัวแปรพอยเตอร์ (Pointers)
7. ตัวแปรสตริง (String)
8. โครงสร้างสตักเจอร์ (Structure)


1. พื้นฐานโปรแกรมภาษา C (Introduction to C Programming)

ก่อนอื่นของแนะนำพื้นฐานเกี่ยวกับคอมพิวเตอร์กันซักนิด ก่อนที่จะเริ่มเรียนรู้ภาษา C กัน หน่วยสำคัญที่สุดของคอมพิวเตอร์ก็คือ หน่วยประมวลผลหรือที่เรียกกันว่า CPU โดยปกติ CPU จะมีภาษาของตัวเองที่เรียกว่า ภาษาเครื่อง (Machine Language) ซึ่งจะเป็นภาษาที่ประกอบไปด้วยเลขฐานสองมากมาย ดังนั้นการที่จะเขียนโปรแกรมควบคุมการทำงานของคอมพิวเตอร์ โดยใช้ภาษาเครื่องโดยตรงนั้นจึงทำได้ยาก จึงได้มีการพัฒนาตัวแปรภาษาเครื่องที่เรียกว่า โปรแกรมภาษาระดับสูงขึ้นมา หรือที่เรียกว่า High Level Languages โดยภาษาในระดับสูงเหล่านี้ จะมีลักษณะรูปแบบการเขียน (Syntax) ที่ทำให้เข้าใจได้ง่ายต่อการสื่อสารกับผู้พัฒนา และถูกออกแบบมาให้ง่ายต่อการใช้งาน และจะเปลี่ยนคำสั่งจากผู้ใช้งาน ไปเป็นเป็นภาษาเครื่อง เพื่อที่จะควบคุมการทำงานของคอมพิวเตอร์ต่อไป ตัวอย่างของโปรแกรมภาษาระดับสูง ได้แก่ COBOL ใช้กันมากสำหรับโปรแกรมทางด้านธุรกิจ, Fortran ใช้กันมากสำหรับการพัฒนาโปรแกรมด้านวิทยาศาสตร์และวิศวกรรมศาสตร์ เพราะง่ายต่อการคำนวณ, Pascal มีใช้กันทั่วไป แต่เน้นสำหรับการพัฒนาเครื่องมือสำหรับการเรียนการสอน, C & C++ ใช้ทั่วไป ปัจจุบันมีผู้เลือกที่จะใช้กันอย่างแพร่หลาย, PROLOG เน้นหนักไปทางด้านงานประเภท AI และ JAVA ใช้ได้ทั่วไป ปัจจุบันเริ่มมีผู้หันมาสนใจกันมากและเพิ่มขึ้นอย่างรวดเร็ว
คราวนี้เราลองมาเตรียมตัวกันซักนิก ก่อนที่จะลงมือพัฒนาโปรแกรมคอมพิวเตอร์ ขั้นแรก เราต้องศึกษารูปแบบความต้องการของโปรแกรมที่จะพัฒนา จากนั้นก็วิเคราะห์ถึงปัญหาตลอดจนวิธีการแก้ปัญหา จากนั้นจึงนำเอาความคิดในการแก้ปัญหาอย่างเป็นขั้นตอน ไปเขียนในรูปแบบของโปรแกรมภาษาในระดับสูง ซึ่งจะอยู่ในรูปแบบของ Source Program หรือ Source Code จากนั้นเราก็จะใช้ Complier ของภาษาที่เราเลือก มาทำการ Compile Source code หรือกล่าวง่ายๆ คือแปลง Source code ของเราให้เป็นภาษาเครื่องนั่นเอง ซึ่งในขั้นตอนนี้ ผลที่ได้ เราจะเรียกว่า Object code จากนั้น Complier ก็จะทำการ Link หรือเชื่อม Object code เข้ากับฟังก์ชันการทำงานใน Libraries ต่างๆ ที่จำเป็นต่อการใช้งาน แล้วนำไปไว้ในหน่วยความจำ แล้วเราก็จะสามารถ Run เพื่อดูผลของการทำงานโปรแกรมได้ หากโปรแกรมมีข้อผิดพลาด เราก็จะทำการแก้ หรือที่เรียกกันในภาษาคอมพิวเตอร์ว่า การ Debug นั่นเอง
ภาษา C เป็นโปรแกรมภาษาระดับสูง ถูกพัฒนาขึ้นในปี 1972 ที่ AT&T Bell Lab เราสามารถใช้ภาษา C มาเขียนเป็นคำสั่งต่างๆ ที่คอมพิวเตอร์สามารถเข้าใจได้ และกลุ่มของคำสั่งเหล่านี้ เราก็เรียกกันว่า อัลกอริธึม ได้มีผู้ให้คำจำกัดความของคำว่า อัลกอริธึม ว่าเป็น “A precise description of a step-by-step process that is guaranteed to terminate after a finite number of steps with a correct answer for every particular instance of an algorithmic problem that may occur.” สำหรับ Compiler ภาษา C ที่มีในปัจจุบัน มี 2 ค่ายใหญ่ๆ ที่มีผู้คนสนใจใช้กันมากได้แก่ Microsoft และ Borland การใช้งาน Compiler ทั้งสองตัวนี้ สามารถเรียนรู้ได้ไม่ยากนัก เราจึงจะมาเริ่มต้นที่การเขียนโปรแกรมในภาษา C กันเลย เราลองมาเริ่มจากตัวอย่างการเขียน ภาษา C แบบ ง่ายๆ กันก่อนกับโปรแกรม Hello World

#include
main()
{
printf("Hello World !! ");
}

บรรทัดแรก #include เป็นการบอกว่าให้ทำการรวม Header file ที่ชื่อว่า stdio.h (.h = header) ซึ่งเป็น header ที่เกี่ยวข้องกับการรับและให้ข้อมูล (Standard Input Output) นอกจาก stdio.h แล้ว ก็ยังมี Header อื่นๆ ที่ผู้พัฒนาสามารถที่จะเรียกใช้งาน Function ที่จำเป็นจาก Header นั้นๆ ได้ อาทิเช่น

5519


รู้จัก Header File กันไปล่ะ คราวนี้ เราลองมาดูบรรทัดถัดไปกัน ก็คือ ฟังก์ชัน main() จะเป็นจุดเริ่มต้นของโปรแกรม และโปรแกรมทุกโปรแกรมในภาษา C จะต้องมี Function main() นี้ โดยส่วนมาก เราจะใช้ Function main() ในการกำหนดค่าเริ่มต้นต่างๆ ของโปรแกรม จากนั้นจึงเข้าสู่ Function ต่างๆ ที่ผู้พัฒนา ได้กำหนดขึ้นไว้

บรรทัดถัดมาจะเป็นเครื่องหมาย { ซึ่งเป็นเครื่องหมายบ่งบอกขอบเขตของ Function โดยขอบเขตของฟังก์ชัน จะเปิดและปิดโดยใช้เครื่องหมายเปิด { และเครื่องหมายปิด } ตามลำดับ ภายใน Function main() จะมีคำสั่ง (Statement) printf("Hello World !! "); ซึ่ง printf เป็น Function ในภาษา C ทำหน้าที่ให้โปรแกรม ทำการแสดงผลออกทางหน้าจอว่า Hello World !! และทุกครั้ง ผู้พัฒนาจะต้องทำการจบคำสั่งหรือ Statement ด้วยเครื่องหมาย semi-colon ;

ดังนั้นรูปแบบของการเขียนโปรแกรม จึงเขียนออกมาในรูปแบบดังนี้

// ข้อความที่อยู่ข้างหลังเครื่องหมาย // จะเป็นคำอธิบายโปรแกรม
#include
void main()
{
constant declarations; // การกำหนดค่าคงที่ต่างๆ
variable declarations; // การกำหนดตัวแปรต่างๆ
executable statements; // คำสั่งการทำงานของโปรแกรม
}

การอ่านข้อมูลและการแสดงผล (Input & Output)

รูปแบบการใช้งานฟังก์ชัน printf จะทำการพิมพ์ในรูปแบบที่ เริ่มต้นด้วย Format ที่ต้องการจะพิมพ์ และตามด้วยตัวแปรที่ต้องการพิมพ์ ดังนี้

printf( const char *format [, argument]... );

สำหรับการนำข้อมูลเข้าก็เช่นกัน จะใช้ฟังก์ชัน scanf ซึ่งจะเป็นฟังก์ชันสำหรับอ่านข้อมูลจากคีย์บอร์ด และจะนำข้อมูลที่ User ทำการพิมพ์ไปเก็บไว้ใน argument โดยแต่ละ argument จะต้องเป็นตัวแปรที่เรียกว่า pointer (รายละเอียดจะได้กล่าวต่อไป) และมีชนิดที่ตัวแปรที่สัมพันธ์กับที่ได้กำหนดไว้ใน Format รูปแบบการใช้งานของฟังก์ชัน scanf สามารถเขียนได้ดังนี้

scanf( const char *format [,argument]... );

หน้าที่ 2 - ตัวแปร (Variables)
ตัวแปร (Variables)

ตัวแปรจะเป็นชื่อที่ใช้ในการบอกจำนวนหรือปริมาณ ซึ่งสามารถที่จะทำการเปลี่ยนแปลงจำนวนได้ด้วยโปรแกรมคอมพิวเตอร์ การตั้งชื่อตัวแปร จะต้องตั้งชื่อให้แตกต่างไปจากชื่อของตัวแปรอื่นๆ ยกตัวอย่างชื่อของตัวแปร ได้แก่ x, y, peter, num_of_points และ streetnum เป็นต้น โดยปกติการเขียนโปรแกรมที่ดี ควรจะตั้งชื่อตัวแปรให้สอดคล้องกับการทำงานหรือหน้าที่ของตัวแปรนั้นๆ เพราะเมื่อถึงเวลาต้องมาทำการปรับปรุงแก้ไขโปรแกรม จะสามารถทำได้โดยไม่ยากนัก

ในภาษา C หรือ C++ ได้มีกฏในการตั้งชื่อตัวแปรที่สามารถใช้งานได้ดังนี้
- ชื่อตัวแปรจะต้องขึ้นต้นด้วยตัวอักษร
- ชื่อตัวแปรจะประกอบไปด้วย ตัวอักษร ตัวแลข และ _ ได้เท่านั้น
- ชื่อตัวแปรจะต้องไม่ใช่ชื่อ reserved word (ชื่อที่มีการจองไว้แล้ว)

ตัวอย่างของชื่อตัวแปรที่สามารถนำมาใช้ตั้งชื่อได้ ได้แก่
length, days_in_year, DataSet1, Profit95, Pressure, first_one

และตัวอย่างของชื่อ ที่ไม่สามารถนำมาใช้เป็นชื่อตัวแปรได้ ยกตัวอย่างเช่น
day-in-year, 1data, int, first.val เป็นต้น

reserved word (ชื่อที่มีการจองไว้แล้ว)

Reserved words หรือตัวแปรที่ได้จองไว้แล้วนั้น จะประกอบไปด้วยตัวอักษรตัวเล็กทั้งหมด และจะมีความสำคัญสำหรับภาษา C++ และจะไม่นำมาใช้ด้วยวัตถุประสงค์อื่นๆ ตัวอย่างของ Reserved words ได้แก่ and, bool, break, case, catch, char, class, continue, default, delete, do, double, if , else, enum, export, extern เป็นต้น

นอกจากนี้ในภาษา C หรือ C++ ชื่อตัวแปร ที่ประกอบไปด้วยอักษรเล็ก หรือใหญ่ ก็มีความแตกต่างกัน หรือที่เรียกว่า Case sensitive ยกตัวอย่างเช่น

‘X’ และ ‘x’ เป็นตัวแปรต่างกัน
‘peter’ และ ‘Peter’ เป็นตัวแปรต่างกัน
‘bookno1’ และ ‘bookNo1’ เป็นตัวแปรต่างกัน
‘XTREME’ และ ‘xtreme’ เป็นตัวแปรต่างกัน
‘X1’ และ ‘x1’ เป็นตัวแปรต่างกัน
‘int’ และ ‘Int’ เป็นตัวแปรต่างกัน

การกำหนดชนิดของตัวแปร (Declaration of Variables)

ในภาษา C หรือ C++ (และโปรแกรมในภาษาอื่นๆ) ตัวแปรทุกตัวที่จะมีการเรียกใช้ในโปรแกรมจำเป็นต้องมีการกำหนดชนิดของตัวแปรนั้นๆ ก่อนที่จะทำการเรียกใช้ตัวแปร

การกำหนดชนิดของตัวแปรมีวัตถุประสงค์หลัก 2 ประการได้แก่
- เป็นการบอกชนิด และตั้งชื่อตัวแปรที่จะเรียกใช้ ชนิดของตัวแปรจะทำให้คอมไพเลอร์สามารถแปลคำสั่งได้อย่างถูกต้อง (ยกตัวอย่างเช่น ใน CPU คำสั่งที่ใช้ในการบวกตัวเลขจำนวนเต็ม 2 จำนวน ย่อมแตกต่างจากคำสั่งที่จะบวกจำนวนจริง 2 จำนวนเข้าด้วยกัน)
- ชนิดของตัวแปร ยังเป็นบ่งบอกคอมไพเลอร์ให้ทราบว่าจะต้องจัดเตรียมเนื้อที่ให้กับตัวแปรตัวนั้นมากน้อยเท่าใด และจะจัดวางตัวแปรนั้นไว้แอดเดรส (Address) ไหนที่สามารถเรียกมาใช้ใน code ได้

สำหรับในบทความนี้จะพิจารณาชนิดตัวแปร 4 ชนิดที่ใช้กันมากได้แก่ int, float, bool และ char

int ชนิดตัวแปรที่สามารถแทนค่าจำนวนเต็มได้ทั้งบวกและลบ โดยปกติสำหรับคอมพิวเตอร์ทั่วไป คอมไพเลอร์ จะจองเนื้อที่ 2 ไบต์ สำหรับตัวแปรชนิด int จึงทำให้ค่าของตัวแปรมีค่าตั้งแต่ -32768 ถึง +32768
ตัวอย่างของค่า int ได้แก่ 123 -56 0 5645 เป็นต้น

floatชนิดของตัวแปรที่เป็นตัวแทนของจำนวนจริง หรือตัวเลขที่มีค่าทศนิยม ความละเอียดของตัวเลขหลังจุดทศนิยมขึ้นอยู่กับระบบคอมพิวเตอร์ โดยปกติแล้ว ตัวแปรชนิด float จะใช้เนื้อที่ 4 ไบต์ นั่นคือจะให้ความละเอียดของตัวเลขหลังจุดทศนิยม 6 ตำแหน่ง และมีค่าอยู่ระหว่าง -1038 ถึง +1038
ตัวอย่างของค่า float ได้แก่ 16.315 -0.67 31.567

bool ชนิดของตัวแปรที่สามารถเก็บค่าลอจิก จริง (True) หรือ เท็จ (False) ตัวแปรชนิดนี้ เป็นที่รู้จักกันอีกชื่อคือ ตัวแปรบูลีน (Boolean)
ตัวอย่างของตัวแปรชนิด bool ได้แก่ 1 0 true false (เมื่อ 1 = true และ 0 = false)

char เป็นชนิดตัวแปรที่เป็นตัวแทนของ ตัวอักษรเพียงตัวเดียว อาจเป็นตัวอักษร ตัวเลข หรือตัวอักขระพิเศษ โดยปกติตัวแปรชนิดนี้จะใช้เนื้อที่เพียง 1 ไบต์ ซึ่งจะให้ตัวอักษรในรูปแบบที่แตกต่างกันได้ถึง 256 ค่า การเขียนรูปแบบของ char หลายๆ ตัว โดยปกติ จะอ้างอิงกับ American Standard Code for Information Interchange (ASCII)
ตัวอย่างของตัวแปรชนิด char ได้แก่ '+' 'A' 'a' '*' '7'

การกำหนดชนิดของตัวแปร สามารถเขียนได้อยู่ในรูป type identifier-list;
เมื่อ type บ่งบอกชนิดของตัวแปร ส่วน identifier-list เป็นการกำหนดชื่อของตัวแปร ซึ่งอาจจะมีมากกว่า 1 ตัวแปร และจะแยกตัวแปรแต่ละตัวออกจากกันด้วยเครื่องหมาย comma (,)
ตัวอย่าง รูปแบบของการกำหนดชนิดของตัวแปร ได้แก่
int i, j, count;
 float sum, product;
 char ch;
 bool passed_exam;


มาถึงตอนนี้ เราก็จะสามารถปรับปรุงการเขียนโปรแกรมแบบง่ายๆ ได้ดังนี้

#include 
main()
{
   int its_price;
   printf("How much is that ? ");
   scanf("%d", &its_price);
   printf("oh! %d ?, hmmm...., too expensivenn",its_price);
}

จาก code ข้างบน ผู้อ่านจะเห็น %d เมื่อมีการเรียกใช้ฟังก์ชัน scanf และ printf ทั้งนี้ %d จะเป็น format ที่ใช้บ่งบอกชนิดของตำแหน่ง (Place Holders) ที่จะมีการส่งข้อมูล โดยในที่นี้ %d หมายถึงตำแหน่งของจำนวนเต็ม หรือ int นั่นเอง ตัวอย่างของ Place Holders อื่นๆ สามารถแสดงได้ดังตาราง

คราวนี้ลองมาดูตัวอย่างของการใช้ Place Holders

printf("C=%f, F=%f",cel,fah);
printf("He wants to score %d goals today",9);


เมื่อ % เป็นการบ่งบอกตำแหน่งเริ่มต้นของ Place Holder จากนั้น
ตัวอักษร f ตัวแรก จะบ่งบอกถึง ตัวแปรcel ว่ามีค่าเป็นจำนวนจริง (Float)
ส่วน f ตัวทีสอง จะบ่งบอกคอมไพเลอร์ว่า ตัวแปร fah ก็มีค่าเป็นจำนวนจริงเช่นกัน

นอกจากนี้ Place holder %d และ %f ยังสามารถใช้กับการกำหนดตำแหน่งตัวเลขตามต้องการได้ ยกตัวอย่างเช่น สมมุติให้ x=235; และ y=6.54321;

5520


การให้กำหนดค่าตัวแปร (Variable Assignment)

เราสามารถกำหนดค่าให้กับตัวแปรได้ ด้วยเครื่องหมาย = ยกตัวอย่างเช่น

int name; // กำหนดตัวแปร name ที่เก็บค่าจำนวนเต็ม
name = 23; // กำหนดให้ตัวแปร name มีค่าเป็น 23

ในขณะเดียวกัน เราสามารถใช้เครื่องหมาย = ระหว่างตัวแปรกับตัวแปร หรือตัวแปรกับจำนวนใดๆ ได้ อาทิเช่น

change = x1 - x2;
mean = (x1 + x2)/2;
x = x + 1;


ตอนนี้เราลองมาเขียนโปรแกรมอย่างง่าย เพื่อทำการแก้ปัญหาทางคณิตศาสตร์ ด้วยการแปลงค่า อุณหภูมิ ในหน่วยของ ฟาเรนไฮต์ เป็น เซลเซียส เมื่ออุณหภูมิในหน่วยฟาเรนไฮต์ มีค่า = 85 และเป็นที่ทราบกันดีว่า ความสัมพันธ์ระหว่าง องศาฟาเรนไฮต์ และ เซลเซียส สามารถเขียนได้อยู่ในรูปของสมการ

5521


การเขียนโปรแกรม เพื่อแก้ปัญหา การแปลงค่า 85 ฟาเรนไฮต์ให้เป็นเซลเซียส สามารถเขียนได้ดังนี้

#include 
void main()
{
     float F;
 float C;

 F = 85;
 C = 5*(F-32)/9;
 printf("the result is %f",C);
}


อีกตัวอย่าง ของโปรแกรม การบวกค่าจำนวนเต็ม 2 จำนวนเข้าด้วยกัน แล้วแสดงผลลัพธ์ออกทางหน้าจอ การเขียนโปรแกรมเพื่อแก้ปัญหานี้ สามารถเขียนได้ดังนี้

#include 
void main()
{
    int N1, N2, Sum;
 
 printf("please input an integer number : ");
 scanf("%d",&N1);
 printf("please input another integer number : ");
 scanf("%d",&N2);

 Sum = N1 + N2;
 printf("so, %d + %d = %d",N1,N2,Sum);
}


จากตัวอย่างการเขียนโปรแกรมข้างต้น จะเห็นว่ามีการคำนวณทางคณิตศาสตร์เข้ามาเกี่ยวข้อง คราวนี้เราลองมาดู การคำนวณในภาษา C กันว่าจะเขียนกันได้อย่างไรบ้าง

5522



หน้าที่ 3 - การเปรียบเทียบ แบบมีทางเลือก (Selection Structures)
2.การเขียนโปรแกรมแบบมีทางเลือก (Selection Structures)

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

เงื่อนไข (Condition)
- เป็นตัวกำหนดเงื่อนไขที่ผู้พัฒนาโปรแกรมได้สร้างขึ้นมา
- ผลลัพธ์ที่ได้จากเงื่อนไข จะมีค่า จริงหรือ เท็จ

โครงสร้างของเงื่อนไข (Condition Control Structures)

ประโยคเงื่อนไขสามารถที่จะเขียนให้อยู่ในรูปภาษา C จะเขียนได้ดังนี้

if condition then A else B

ซึ่งหมายความว่า ถ้าเงื่อนไข (condition) มีค่าเป็นจริง ก็จะดำเนินการทำคำสั่ง A มิเช่นนั้นก็จะทำคำสั่ง B
ตัวอย่างของการเขียนโครงสร้างทางเลือกในภาษา C สามารถเขียนได้ดังนี้

if (x < y)
 a = x * 2;
else
 a = x + y;


ความหมายของ code ดังกล่าว หมายความว่า ถ้า ค่า x มีค่าน้อยกว่า y แล้ว a = x*2
แต่ถ้า x มีค่ามากกว่าหรือเท่ากับ y แล้ว a = x+y นั่นเอง

รูปแบบของเงื่อนไข ส่วนใหญ่จะอยู่ในรูป “ตัวแปร โอเปอเรเตอร์ ตัวแปร” โอเปอเรเตอร์ที่กล่าวถึงนี้จะมีอยู่ 2 แบบ ด้วยกันคือ โอเปอเรเตอร์สัมพันธ์ (Relational Operator) และ โอเปอเรเตอร์ลอจิก (Logical Operator)

โอเปอเรเตอร์สัมพันธ์ที่ใช้ในภาษา C มีดังต่อไปนี้

5523



if ( condition1 ) 
   statement1 ; 
else 
      if ( condition2 ) 
           statement2 ; 
           . . . 
        else if ( condition-n ) 
                  statement-n ; 
                else 
                  statement-e ;


ยกตัวอย่างของโปรแกรม Nested if สามารถเขียนได้ดังนี้

if (x < 0.25)
  count1++;
else if (x < 0.5)
           count2++;
        else if (x < 0.75)
                  count3++;
                else
                    count4++;


นอกจากรูปแบบของ if-else แล้ว เรายังสามารถใช้เครื่องหมาย ? มาประยุกต์ในการเขียน code เพื่อให้ได้ความหมายเดียวกันกับ if-else ดังแสดงให้เห็นดังนี้

if (x < y)
 a = x * 2;
else
 a = x + y;


สามารถเขียนได้ในอีกรูปแบบหนึ่งคือ a = x < y ? x*2: x+y ; // ซึ่งจะให้ความหมายเดียวกันกับ code ข้างบนนั่นเอง

ในบางครั้งที่เราต้องเขียนโปรแกรมแบบมีทางเลือก โดยบางครั้งเราต้องการให้มีทางเลือกมากว่า 2 ทาง Nested if เป็นวิธีหนึ่งที่สามารถใช้แก้ปัญหาได้ แต่เพื่อให้ง่ายขึ้น ในภาษา C เราจึงสามารถใช้คำสั่ง switch ได้ โดยรูปแบบการเขียน คำสั่ง switch สามารถเขียนให้อยู่ในรูป

switch (selector) 
      {
      case label1: statement1;
          break;
      case label2: statement2;
          break;
          ...
      case labeln: statementn;
          break;
      default: statementd; // optional
          break;
      }


โดยที่ selector จะต้องเป็นจำนวนเต็ม ตัวอักษร หรือผลลัพธ์ของการกระทำที่ให้เลขจำนวนเต็มหรือตัวอักษร ตัวอย่างของการเขียน code โดยมีการเรียกใช้คำสั่ง switch สามารถเขียนได้ดังนี้

switch (i)
{
case 1 : grade = 'A';
break;
case 2 : grade = 'B';
break;
case 3 : grade = 'c';
break;
default : printf("%c not in range", i);
break;
}

โดยหลักการของ switch คือ compiler จะทำการเปรียบเทียบค่าของ selector เทียบกับ label ถ้าไม่ตรงกับ label ใดๆ ก็จะเข้าไปทำในคำสั่งของ default นอกจากนี้ การใส่คำสั่ง break หรือไม่มีคำสั่ง break ก็จะให้ผลลัพธ์ที่แตกต่างกัน ดังแสดงให้เห็นดังตัวอย่างต่อไปนี้ (ทดลอง Run แล้วจะเห็นความแตกต่าง)

5524



5525


การเปรียบเทียบตัวอักษร

คุณผู้อ่านทราบกันหรือไม่ว่า ตัวอักษรสามารถเปรียบเทียบค่ากันได้ เบื้องต้นเราต้องทราบก่อนว่า ตัวอักษรมีค่าอย่างไรกันบ้าง

 ตัวอักษรตัวเล็ก ‘a’ มีค่า 97 ไปจนถึง ‘z’ มีค่า 122
 ตัวอักษรตัวใหญ่ ‘A’ มีค่า 65 ไปจนถึง ‘Z’ มีค่า 90

ตัวอย่างของการเปรียบเทียบตัวอักษร สามารถแสดงให้เห็นได้ดังนี้

 ‘9’ >= ‘0’ มีค่าเป็นจริง (1)
 ‘a’ < ‘e’ มีค่าเป็นจริง (1)
 ‘B’ <= ‘A’ มีค่าเป็นเท็จ (0)
 ‘a’ <= ‘A’ ขึ้นอยู่กับระบบ แต่ส่วนใหญ่ เป็นเท็จ
 ‘a’ <= ‘c’ && ‘c’ <= ‘z’ มีค่าเป็นจริง

คราวนี้เราลองมาดูโจทย์กัน สมมุติว่า เราจะเขียนโปรแกรม เพื่ออ่านค่าตัวอักษร 4 ตัว แล้ว เราต้องการที่จะเปลี่ยนตัวอักษรแต่ละตัว ให้เป็นตัวอักษรใหญ่ หรือเล็กที่ตรงข้ามกับค่าที่รับเข้าไป ยกตัวอย่างเช่น PooH ให้เปลี่ยนเป็น pOOh เราจะเขียนโปรแกรมอย่างไร

คำตอบ วิธีหนึ่งที่สามารถทำได้ คือใช้ Nested-if ดังนี้

#include
void main()
{
char A, B, C, D;
printf("Input 4 charactersn");
scanf("%c %c %c %c", &A, &B, &C, &D);

if((A <= 90)&&(A >= 65)) A = (A - 65) + 97;
else if ((A >= 97)&&(A <= 122)) A = (A-97) + 65;
else A = A;
if((B <= 90)&&(B >= 65)) B = (B - 65) + 97;
else if ((B >= 97)&&(B <= 122)) B = (B-97) + 65;
else B = B;
if((C <= 90)&&(C >= 65)) C = (C - 65) + 97;
else if ((C >= 97)&&(C <= 122)) C = (C-97) + 65;
else C = C;
if((D <= 90)&&(D >= 65)) D = (D - 65) + 97;
else if ((D >= 97)&&(D <= 122)) D = (D-97) + 65;
else D = D;
printf("The answer is %c %c %c %cn", A, B, C, D);

หน้าที่ 4 - การเขียนโปรแกรมแบบ วนซ้ำ (Loop)
3.การเขียนโปรแกรมแบบ วนซ้ำ (Repetition & Loop)

กระบวนการหนึ่งที่สำคัญในการออกแบบอัลกอริทึม ก็คือความสามารถในการวนลูปของการทำงานของกลุ่มคำสั่งตามที่นักพัฒนาต้องการ ดังนั้นสำหรับตอนนี้ ก็จะนำเสนอการพัฒนาโปรแกรมเพื่อให้บางส่วนของคำสั่งสามารถมีการวนซ้ำได้หลายครั้ง สำหรับคำสั่งที่สามารถใช้ในการเขียนโปรแกรมแบบวนซ้ำในภาษา C ได้แก่ While, Do-while และ For

ตัวอย่างของการใช้คำสั่ง while, for และ do-while สามารถเขียนให้เห็นได้ดังตาราง


ซึ่งผลลัพทธ์ของโปรแกรมทั้ง 3 ข้างต้นจะให้ผลลัพท์ที่เหมือนกัน คือจะแสดงผลบนหน้าจอเป็น
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6

คราวนี้เราลองมาดูโครงสร้างของการใช้งานแต่ละคำสั่งกัน

while ( condition ) // เมื่อ เงื่อนไข (condition) เป็นจริง ก็จะทำการวนซ้ำ ใน statement ถัดไป
statement

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

sum = 0.0;
x = 5;
while (x > 0.0)
   {
    sum += x;
    x = x – 1;
   }


ในที่นี้จะเห็นว่า ค่า x มีค่าเริ่มต้นเท่ากับ 5 ซึ่ง 5 > 0 เงื่อนไขของคำสั่ง while เป็นจริง จึงทำคำสั่งถัดมาคือ sum += x; หมายความว่า sum = sum + x = 5 จากนั้นค่า x ก็มีค่าลดลงไป 1 เหลือ 4 ก็จะทำการ check เงื่อนไขว่า 4 > 0 หรือไม่ เมื่อเงื่อนไขเป็นจริง ก็จะทำการวนซ้ำ sum ก็จะมีค่าเป็น 5 + 4 = 9 และ x ก็จะมีค่าลดลงเหลือ 3 และดำเนินการวนซ้ำเช่นนี้จนกระทั่ง x มีค่าเป็น 0 ซึ่งค่า 0 ไม่ได้มีค่ามากกว่า 0.0 เงื่อนไขจึงเป็นเท็จ โปรแกรมจึงจะจบการวนซ้ำ

คราวนี้เราลองมาดูตัวอย่างของการใช้คำสั่ง while ในการเขียนโปรแกรมแบบวนซ้ำ และผลลัพท์ที่ได้

x=0;
while( x <=2 ){
   printf("%d %dn",x, x*2);
}

ผลลัพท์ที่ได้จะได้ดังนี้
 0 0
 0 0
 0 0
 :   :
 0 0  (infinite loop)


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

scanf(“%d”,&n);
a = 10;
while (a > n) {
   printf(“%dn”,a);
   a = a-1;
}


ผลลัพท์ของโปรแกรมจะสามารถแสดงให้เห็นได้ดังนี้
10
 9
 8
 7


คราวนี้เราลองมาแก้โจทย์ปัญหา การหาค่า ห.ร.ม (หารร่วมมาก) ของตัวเลข 2 ตัวใดๆ โดยอัลกอริทึม Euclidean โดยอัลกอริทึมดังกล่าว จะทำการแปลงค่าตัวเลข 2 ตัวเลขบวกใดๆ (m, n) เป็นค่า (d, 0) โดยการนำตัวเลขที่มีค่ามาก นำมาหารด้วยตัวเลขที่มีค่าน้อยกว่า นำค่าเศษที่หารได้มาแทนตัวเลขที่มีค่ามากกว่า ทำเช่นนี้จนกระทั่งได้ค่าเศษจากการหารมีค่าเป็น 0 ตัวเลขอีกตัวก็จะเป็นค่า ห.ร.ม. ยกตัวอย่างเมื่อเราทำการ Run โปรแกรม จะได้ผลดังนี้

Enter two positive integers: 532 112
The g.c.d. of 532 and 112 is 28

คราวนี้เราลองมาดูการเขียนโปรแกรมเพื่อแก้ปัญหาดังกล่าวข้างต้น สามารถเขียนได้ดังนี้

#include 
void main()
{
 int A, B, start;
     printf("Enter two positive intergers: ");
 scanf("%d %d", &A, &B);
 if(A < B) start = A;
 else start = B;
 while(((A%start) != 0)||((B%start) != 0))
 {
  start = start-1; 
 }
 printf("The g.c.d of %d and %d is %dn", A, B, start);
}


การดำเนินการทางคณิตศาสตร์ สามารถเขียนให้อยู่ในรูปแบบสั้นๆ ได้ ดังตัวอย่างในตารางดังนี้

5526


ความแตกต่างระหว่าง i++ และ ++i

i++ และ ++i จะมีความหมายใกล้เคียงกันมาก จะแตกต่างเพียง การจัดลำดับในการคำนวณ เมื่อต้องนำไปใช้กับตัวแปรตัวอื่น

A = 10;
C = A++; // A= 11, C = 10

A = 10;
C = ++A; // A = 11, C = 11

A = 10;
C = A--; // A = 9, C = 10

A = 10;
C = --A; // A = 9, C = 9

โครงสร้างการเขียนโปรแกรมแบบวนซ้ำโดยใช้คำสั่ง For

คำสัง for สามารถเขียนให้อยู่ในรูปแบบได้ดังนี้

for ( เริ่มต้น ; เงื่อนไข ; เปลี่ยนแปลง )
statement;

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

for ( count=0 ; count < 10 ; count++)
  {
   printf(“count = %dn”,count);
  }


ใน code ข้างต้น ตัวแปร count จะเริ่มต้นจากค่า 0 ซึ่งค่า 0 มีค่าน้อยกว่า 10 ก็จะทำคำสั่ง print ค่าของตัวแปร count จากนั้นค่า count ก็จะเพิ่มค่าเป็น 1 เงื่อนไข count < 10 ก็ยังคงเป็นจริง ก็จะทำการพิมพ์ ค่าของตัวแปร count วนซ้ำเช่นนี้ จนกระทั่ง count มีค่าเพิ่มขึ้นจนเป็น 10 เงื่อนไขก็จะเป็นเท็จ และจบโครงสร้างของการวนซ้ำ

การเปลี่ยนแปลงค่าของตัวแปร อาจจะมีการเปลี่ยนแปลงมากกว่า 1 ค่า ยกตัวอย่างเช่น

for ( count=0 ; count < 10 ; count += 2)  // ตัวแปร count มีค่าเปลี่ยนแปลงเพิ่มขึ้นครั้งละ 2
{
   printf(“count = %dn”,count);
}
for ( count=10 ; count > 5 ; count -= 2)   // ตัวแปร count มีค่าเปลี่ยนแปลงลดลงครั้งละ 2
{  
   printf(“count = %dn”,count);
}


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

start = 0; end = 20; step=3;
for ( count=start ; count < end ; count += step)
{
   printf(“count = %dn”,count);
}


คราวนี้ เราลองมาทดลองเขียนโปรแกรม โดยให้โปรแกรม สามารถรับค่าตัวเลขใดๆ และแสดงค่าในรูปแบบดังตัวอย่างต่อไปนี้

5527



Input the number > 4

0
0 1
0 1 2
0 1 2 3
0 1 2
0 1
0



เราสามารถแก้ปัญหาข้างต้น โดยใช้โครงสร้างการเขียนโปรแกรมแบบวนซ้ำดังต่อไปนี้

#include 
void main()
{
 int number, i, j;
     printf("Enter number: ");
 scanf("%d", &number);
 for(j= 0; j< number; j++)
 {
  for(i=0; i<= j; i++)
  {
   printf("%d ", i);
  }
  printf("n");
 }
 for(j= number-1; j>= 0; j--)
 {
  for(i=0; i< j; i++)
  {
   printf("%d ", i);
  }
  printf("n");
 }
}


โครงสร้างการเขียนโปรแกรมแบบวนซ้ำโดยใช้คำสั่ง do-while

รูปแบบของการเขียน code สำหรับโปรแกรมแบบวนซ้ำที่ใช้ do-while สามารถเขียนให้อยู่ในรูปทั่วไปได้ดังนี้

do
statement
while ( เงื่อนไข );

ตัวอย่างของโครงสร้าง do-while สามารถเขียนได้ดังนี้

sum = 0.0;
scanf(“%f”, &x);
do {
    sum += x;
    scanf(“%f”, &x);
  }
while (x > 0.0);


โปรแกรมข้างต้นจะทำการอ่านค่าจะ keyboard เมื่อ User พิมพ์ค่าที่มีค่ามากกว่าศูนย์ ก็จะทำการบวกค่าเหล่านี้ไปที่ตัวแปร sum จนกระทั่ง User พิมพ์ ตัวเลข 0 หรือค่าที่น้อยกว่า ศูนย์ ทำให้เงื่อนไขเป็นเท็จ และโปรแกรมจึงจะออกจากโครงสร้าง do-while

คราวนี้เราลองมาเขียนโปรแกรมที่ใช้โครงสร้าง do-while โดยโจทย์กำหนดให้ว่า ให้โปรแกรมสามารถรับค่าตัวเลขใดๆ (X) และ แสดงผลของตัวเลข ระหว่าง 0 ถึง X ที่สามารถหารด้วย 4 ลงตัว

#include 
void main()
{  
 int number, i;
 printf("enter the numbern");
 scanf("%d", &number);
            i = 0;
 do
 {
  if((i % 4) == 0) printf("%d ", i);
                       i++;
 }
            while(i <= number);
}


ตอนนี้ผู้อ่านก็คงจะรู้จัก การเขียนโปรแกรมแบบวนซ้ำกันแล้วนะ ต่อไป เราก็จะไปเรียนรู้เกี่ยวกับการเขียนโปรแกรมแบบแยกเป็นโมดูลกัน

หน้าที่ 5 - ฟังก์ชัน (Functions) และ โปรแกรมแยกเป็นโมดูล (Modular Programming)
4.ฟังก์ชัน และการเขียนโปรแกรมแยกเป็นโมดูล (Functions & Modular Programming)

สำหรับนักพัฒนา ที่ได้ทำการพัฒนาโปรแกรมมามากๆ บางครั้ง จำเป็นต้องเขียนโปรแกรมที่มีขนาดใหญ่และมีความสลับซับซ้อน จึงจำเป็นต้องหาวิธีในการพัฒนาโปรแกรมที่ดี ซึ่งสามารถทำได้ด้วยการ แตกปัญหาใหญ่ออกเป็นปัญหาย่อยๆ แล้วแก้ปัญหาย่อยออกไปที่ละส่วนจนกว่าจะหมด ซึ่งจะช่วยให้แก้ปัญหาใหญ่ๆ ได้สำเร็จนั่นเอง ซึ่งเราเรียกกันว่า วิธีแบ่งแยกแล้วพิชิต (Divide and Conquer) ซึ่งจะเป็นแนวการแก้ปัญหาแบบจากบนลงล่าง (Top down approach) ดังแสดงให้เห็นดังภาพที่ 4.1

5529


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

ตัวอย่างการเขียนโปรแกรมออกเป็นโมดูลต่างๆ จะแสดงให้เป็นดังผังในภาพที่ 4.2 ซึ่งในภาพดังกล่าวได้แยกโปรแกรม Main ออกเป็นส่วนๆ คือ

5530


1) ส่วนรับข้อมูล (Input) ซึ่งจะเป็นไปตามความต้องการของผู้ใช้ เช่นการป้อนข้อมุล หรือ การอ่านไฟล์ข้อมูล
2) ส่วนการคำนวณ (CAL) ซึ่งประกอบด้วยฟังก์ชันที่ใช้กับการคำนวณ ได้แก่
i. AVG สำหรับคำนวณค่าเกรดเฉลี่ยสำหรับเทอมปัจจุบัน
ii. GPA สำหรับคำนวณเกรดเฉลี่ยสะสม
3) ส่วนการแสดงผล (Display) ซึ่งเป็นการแสดงผลบนจอภาพ หรือจะพิมพ์ผลออกมาก็ได้ตามความต้องการของผู้ใช้โปรแกรม ที่กำหนดให้ผู้เขียนโปรแกรมทำให้สำเร็จตามเป้าหมาย

ภาพที่ 4.2: ตัวอย่างการเขียนโปรแกรมคำนวณค่าเกรดเฉลี่ย และ เกรดเฉลี่ยสะสมออกเป็นโมดูล

ส่วนในการตั้งชื่อฟังก์ชันนั้น ก็มีข้อกำหนดดังนี้

1) ห้ามขึ้นต้นด้วยตัวเลข
2) ห้ามเอาชื่อตารางอาเรย์ ชื่อตัวแปร หรือ คำสำคัญ (keywords) มาตั้งชื่อฟังก์ชันโดยเด็ดขาด
3) ในการใช้งานจริง นั้น ไม่ควรตั้งชื่อฟังก์ชันโดยไม่สื่อความหมายถึงการทำงานของฟังก์ชัน เพราะจะทำให้ลำบากในการซ่อมบำรุงโปรแกรม

โดยทั่วไปแล้วในการประกาศฟังก์ชันนั้น จะต้องมีการกำหนด type ให้ตัวแปรร่วมภายในฟังก์ชันด้วยเสมอ ดังตัวอย่างต่อไปนี้

ชนิดของฟังก์ชัน[function-type] ชื่อฟังก์ชัน [function-name] (บัญชีตัวแปรร่วม [parameter-list])
{
  การประกาศตัวแปรเฉพาะที่; [local-definitions declaration;]
  กลไกการทำงานของฟังก์ชัน; [function-implementation;]
  การคืนค่าถ้าชนิดฟังก์ชันไม่เป็น void; [return statement if function type NOT void]
}

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

float distance(float x, float y) // float คือ ชนิดของฟังก์ชัน ส่วน distance คือ ชื่อฟังก์ชัน
{    // float x, float y คือ บัญชีตัวแปรร่วม
 float dist;  // float dist คือ การประกาศตัวแปรเฉพาะที่;
 dist = sqrt(x*x+y*y); // dist = ระยะห่างระหว่างพิกัด (x,y) ไป (0,0) 
 return dist ;
}

ก่อนจะทำการเรียกใช้ฟังก์ชัน จำเป็นต้องมี การกำหนดฟังก์ชัน (function prototype)
ซึ่งเป็น copy ของ function heading และมีข้อกำหนดดังนี้
1) ตัวแปรร่วมในฟังก์ชัน ที่ประกาศไว้ และ ตัวแปรร่วมขณะเรียกใช้ฟังกชันนั้นต้องมีจำนวนเท่ากันและต้องมี type ตรงกัน มิฉะนั้นจะเกิด error ขึ้น
2) ต้องกำหนดการทำงานของฟังก์ชันก่อนการเรียกใช้ฟังก์ชัน ใน main function เพื่อให้ตัวแปรภาษารู้ว่าฟังก์ชันนี้ทำงานอย่างไร
3) แม้ฟังก์ชันที่ใช้จะไม่มีค่าตัวแปรร่วม ก็ต้องใส่วงเล็บ [ ( ) ] ไว้หลังชื่อฟังก์ชันเสมอ
4) การกำหนดฟังก์ชัน จะต้องกำหนดก่อนการใช้ Main function เพื่อให้ Main function ได้รับรู้ว่าฟังก์ชันดังกล่าวมีอะไรเป็นตัวแปรร่วม และ จะคืนค่าอะไรออกมา
5) กลไกการทำงานเต็มของฟังก์ชันย่อยจะแสดงหลังจากที่เรียก Main function แล้ว หรืออยู่ในไฟล์ที่แยกต่างหาก ซึ่งจะมีการแปรออกมาแล้วเชื่อมต่อกับ Main function

การกำหนดฟังก์ชันจะมีลักษณะดังนี้


float distance(float, float); // การกำหนดฟังก์ชัน [function prototype]

 void main()
 {
  float x0 = ….,y0 = …;
 float dist1 = distance(x0,y0); 
}
float distance(float x, float y) // float คือ ชนิดของฟังก์ชัน distance คือ ชื่อฟังก์ชัน
{    // float x, float y คือ บัญชีตัวแปรร่วม
 float dist;  // float dist คือ การประกาศตัวแปรเฉพาะที่;
 dist = sqrt(x*x+y*y); 
 return dist ;
}


ในการส่งผ่านตัวแปรเข้า ฟังก์ชัน (Passing argument)
นั้นมีการเรียกได้ 2 แบบ คือ
1) ส่งผ่านตามค่า (Pass by Value) ซึ่งส่งผ่านค่า ลงในตัวแปรร่วมของฟังก์ชันโดยตรง
2) ส่งผ่านตามการอ้างอิง (pass by Reference) ซึ่งส่งผ่านค่า address ในหน่วยความจำของตัวแปร ให้กับตัวแปรร่วมของฟังก์ชัน ถ้ามีการเปลี่ยนแปลงข้อมูล ณ ตำแหน่งหน่วยความจำดังกล่าว ค่าที่ส่งให้ตัวแปรร่วมของฟังก์ชันก็จะเปลี่ยนด้วย

ตัวแปรที่เกี่ยวข้องกับฟังก์ชันคือ

1) ตัวแปรเฉพาะที่ (Local variables) เป็นตัวแปรที่อยู่และเปลี่ยนแปลงเฉพาะภายในฟังก์ชันที่เรียกใช้ตัวแปรดังกล่าว การเปลี่ยนแปลงภายนอกฟังก์ชันที่เรียกใช้ตัวแปรเฉพาะที่จะไม่มีผลต่อตัวแปรเฉพาะที่ดังกล่าว ดังตัวอย่างต่อไปนี้

void test_locvar(int num)
{
 int myvar; // Local variable fro this function
 myvar = num;
}
void main()
{
 int myvar, myvar_before, myvar_after; //Local variable for this function 
 myvar = 5;
 myvar_before = myvar; // myvar_before = 5;
 test_locvar(100); // call function
 myvar_after = myvar; // myvar_after = 5;
}


2) ตัวแปรส่วนกลาง (Global variables) เป็นตัวแปรที่อยู่นอกฟังก์ชันซึ่งสามารถเปลี่ยนแปลงค่าภายในตัวแปรได้ตลอดเวลาเมื่อตัวแปรได้รับการเปลี่ยนแปลงโดยฟังก์ชัน ดังตัวอย่างต่อไปนี้

int myvar; // Global varible
void test_locvar(int num)
{
 myvar = 20;
}

void main()
{
 int myvar_before, myvar_after;  //Local variable fro this function 
 myvar = 5;
 myvar_before = myvar; // myvar_before == 5;
 test_locvar(100); // call function
 myvar_after = myvar; // myvar_after == 20;
}


ฟังก์ชันมีหลายประเภทได้แก่

1) ฟังก์ชันที่ไม่มี อาร์กิวเมนต์ และ ไม่คืนค่า เช่น ฟังก์ชัน void main()
2) ฟังก์ชันที่มี อาร์กิวเมนต์ แต่ ไม่คืนค่า เช่นฟังก์ชัน void do_something(float x, float y) ซึ่งจะทำงานตามที่ต้องการโดยอาศัยค่าอาร์กิวเมนต์ (ตัวแปร x,y) โดยไม่คืนค่าออกมา ฟังก์ชันดังกล่าวจะมีกลไกการับข้อมูลเข้าต่างหาก โดยไม่ต้องใช้ตัวแปรที่เป็น อาร์กิวเมนต์
3) ฟังก์ชันที่ไม่มี อาร์กิวเมนต์ แต่ก็คืนค่า เช่น ฟังก์ชัน float do_something2() ค่าที่คืนมาได้จาก ตัวแปรเฉพาะที่และกลไกการคำนวณภายในฟังก์ชันดังกล่าว
4) ฟังก์ชันที่มี อาร์กิวเมนต์ และ คืนค่า เช่นฟังก์ชัน float distance(float x, float y) ฟังก์ชันดังกล่าว สามารถคืนค่า โดยไม่จำเป็นต้องใช้ตัวแปรเฉพาะที่ทำหน้าที่เป็นตัวแปรชั่วคราวในการคืนค่า ตัวอย่างเช่น

float distance3D(float x, float y, float z)
{
  return sqrt((x*x)+(y*y)+(z*z));
}



หน้าที่ 6 - อาเรย์ (Arrays)
5. ตารางอาเรย์ (Arrays)


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

Type ArrayName[size];    // Format of Blank array 
Type ArrayNameInitialized[size] = {…};  // Format of initialized array 
int a[5] = {0,0,0,0,0};
double air[5];
char vowel[] = {’A’,’E’,’I’,’O’,’U’};


ในกรณีใช้ตัวแปรดัชนี้ชี้ตำแหน่งข้อมูลในตาราง ค่าของตัวแปรที่จะชี้จะต้องอยู่ในช่วงระหว่าง 0 กับ N-1 โดยที่ N คือขนาดตารางอาเรย์ ดังตัวอย่างที่แสดงในภาพที่ 5.1

5531


ถ้าค่าดัชนีน้อยกว่า 0 หรือมากกว่า N – 1 แล้วโปรแกรมจะทำงานผิดพลาดหรือหยุดทำงาน

การแยกแยะช่องตาราง (Array subscription) ทำได้โดย การใช้ชื่อตัวแปรอาเรย์ ตามด้วย วงเล็บเหลี่ยมที่มีค่าดัชนี (เช่น Data[i], i = 0, 1, 2, … N-1 )

การประกาศค่าตั้งต้นให้ตัวแปรอาเรย์สามารถทำได้โดยใช้เครื่องหมายปีกกา ( { } ) หรือจะยกประกาศค่าตั้งต้นที่ละช่องตารางก็ได้ตามความต้องการของผู้ใช้ โดยส่วนที่ ไม่ได้ใส่ค่าตั้งต้นจะมีค่าเป็นศูนย์ (กรณีตารางอาเรย์แบบ int, double, หรือ float) หรือเป็นค่าว่าง ดังตัวอย่างต่อไปนี้

int a[4] = {9, 8, 7}; ซึ่งจะได้ค่าออกมาตรงกัย a[0] = 9; a[1] = 8; a[2] = 7; และ a[3] = 0;

 กรณีที่ตารางมีหลายมิติ จะมีการเรียกใช้ตามรูปแบบตารางมิติเดียวต่อไปนี้ 
ตาราง 2 มิติ: type arrayname2D[size1][size2];
ตาราง 3 มิติ: type arrayname3D[size01][size02][size03];

 การค้นข้อมูลในตารางอาเรย์นั้น สามารถเข้าถึงได้อย่างรวดเร็วไม่ว่าตารางจะใหญ่เพียงไหน เช่น

 const int SIZE = 100; // #define SIZE 100 for C
 float A[SIZE],B[SIZE],C[SIZE];
for(i = 0; i <= SIZE-1; i++)
 {
  C[i] = B[i] – A[i];
}


ในการใช้ตัวแปรอาเรย์เป็นอาร์กิวเมนต์ให้ฟังก์ชันนั้น แบ่งได้หลายกรณีได้แก่


1) กรณีที่จะเอาข้อมูลเฉพาะช่องตาราง จะมีการส่งผ่านข้อมูลในตารางอาเรย์ดังนี้

void main()
{
float X[10], X[0] = 123.23, X[1] = 24.56, X[2] = 45.67; 
float total = sum_element(X[0],X[1],X[2]);
 … 
}
float sum_element(float A, float B, float C)
{
 return (A+B+C);
}


2) กรณีที่ใช้ข้อมูลทั้งตารางอาเรย์ การส่งผ่านข้อมูลในตารางลงในฟังก์ชันจะเป็นดังนี้
void main()
{
float X[10], X[0] = 123.23, X[1] = 24.56, X[2] = 45.67; 
float total = sum_element_array(X)
 … 
}
float sum_element_array(float A[])
{
 int index; float Sum = 0;
 for(index =0; index <= 3-1; index++)
{
  Sum +=A[index];
}
return Sum;
}


3) กรณีที่ใช้ข้อมูลทั้งตารางเพื่อให้ได้ผลลัพธ์ออกมาเป็นตารางอาเรย์ให้ทำดังนี้

void add_arrays(float ar1[],  // input array 1
  float ar2[],  // input array 2
  float ar_sum[],  // Output array
  int N)  // Array Size
{
 for(int i = 0; i <= N-1; i++)
 {
  ar_sum[i] = ar1[i] + ar[2];
}
}


การค้นหาข้อมูลในตาราง (Array searching)
ในการค้นข้อมูลนั้นมักใช้ linear search หาข้อมูลในตารางตามที่ต้องการ โดยกำหนดเงื่อนไขว่า
1) ถ้าหาข้อมูลพบ ให้แสดงดัชนีของค่าที่ค้นพบ ก่อนออกจากวงรอบการค้นหา
2) ถ้าหาข้อมูลไม่พบ ให้ออกจากโปรแกรมแล้วคืนค่า ที่อยู่นอก ขอบเขต 0 – N-1, N คือ ขนาดตารางอาเรย์ (โดยมากให้คืนค่า -1)

การสับเรียงข้อมูลในตาราง (Array Sorting)
การสับเรียงข้อมูลมีหลายวิธี ซึ่งจะกล่าวถึงในที่นี้พอสังเขปดังนี้
1) Bubble Sort ซึ่งเขียนใช้งานง่ายแต่ สับตำแหน่งข้อมูลได้ผลช้าที่สุด ซึ่งมีกลไกดังนี้

void BubbleSort(float list[], int N)
{
 int i,j;
 float temp;
 for(i = N-1; i>=0; i--)
 {
  for(j = 1; j <=i; j++)
  {
   if(list[j-1] > list[j])
   {
    temp = list[j-1];
    list[j-1] = list[j];
    list[j] = temp;
}
}

}
}


2) Selection Sort ซึ่งจะเลือกค่าที่ยังไม่ได้สับเปลี่ยนที่น้อยที่สุดเป็นหลัก ในการสลับที่
void SelectSort(float list[], int N)
{
 int i,j, MinIndex;
 float temp;
 for(i = 0; i<=N-2; i--)
 {
  MinIndex = I;
  
for(j = i+1; j <=N-1; j++)
  {
  
   if(list[j] < list[MinIndex])
   {
    minIndex = j 
} 
   
   temp = list[j-1];
   list[j-1] = list[j];
   list[j] = temp;
}
}
}


3) Insertion Sort ซึ่งจะเติมข้อมูลลงในตำแหน่งที่เหมาะสมในขั้นสุดท้าย ซึ่งจะต้องมีตัวแปรอาเรย์ อย่างน้อย 2 ตัว โดยที่อาเรย์ตัวแรกทำหน้าที่ เป็น List เก็บข้อมูล ต้นฉบับ (Source List) และอาเรย์ตัวหลังทำหน้าที่รับข้อมูลที่ผ่านการสับตำแหน่งแล้ว (Sorted List) ในการสับที่กันนั้น จะให้ข้อมูลปัจจุบันเคลื่อนผ่านข้อมูลที่ผ่านการสับตำแหน่งแล้ว จากนั้นจึงลงมือสลับข้อมูลอีกครั้งจนกว่าข้อมูลทั้งหมดจะเข้าที่
void insert_sort(float data[], int Size)
{
 int i,j;
 float DatElement;
 for(i=1;i<=Size-1;i++)
 {
  DatElement = data[i];
  j = i;
  while((j >= 1) && data[j-1] >DatElement)
  {
   data[j-1] = data[j];
   j = j-1;
}
data[j] = DatElement;
}
}



หน้าที่ 7 - ตัวแปรพอยเตอร์ (Pointers)
6. ตัวแปรพอยเตอร์ (Pointers)


Pointer คือตัวแปรดัชนีที่ เก็บค่าตำแหน่งแอดเดรสของหน่วยความจำ ซึ่งตัวแปรพอยเตอร์นั้น จะมีเครื่องหมายดอกจันทร์ (*) นำหน้าเสมอ ดังตัวอย่างต่อไปนี้

int *Num;
float *GreatNum;
char *Name;

ตัวแปรพอยเตอร์มีประโยชน์ในการลดปริมาณหน่วยความจำที่ต้องใช้ในการเขียนโปรแกรม โดยการส่งข้อมูลในรูปพอยเตอร์ เข้าไปในฟังก์ชันที่โปรแกรมเรียกพร้อมกันหลายฟังก์ชัน แทนการส่งข้อมูลในรูปตัวแปรธรรมดา ซึ่งต้องใช้ตัวแปรหลายตัว
ตัวแปรพอยเตอร์มีลักษณะคล้ายตัวแปรตารางอาเรย์ แต่ที่แตกต่างกันคือ ตัวแปรตารางอาเรย์จะเก็บเฉพาะค่าต่างๆ ที่เป็นชนิดกันเดียวกับตัวแปรอาเรย์แต่ ตัวแปรพอยเตอร์จะเก็บเฉพาะค่าตำแหน่ง Address ตัวแปรเท่านั้น โดยไม่ได้มีการจัดเตรียมหน่วยความจำแบบไดนามิกส์ (Dynamic Memory Allocation) ไว้
การเข้าถึงตำแหน่งแอดเดรสทำได้โดย ใช้เครื่องหมายแอมเปอร์แซนด์ (&) ซึ่งจะแสดงให้เห็นดังตัวอย่างต่อไปนี้ ในที่นี้กำหนดให้ตัวแปร Andy อยู่ในตำแหน่ง Address ที่ 1776 ดังตังอย่างในภาพที่ 6.1

5533


Andy = 25; // Assigning Value 25 to Andy
Fred = Andy; // Assigning Value 25 (Value of Andy) to Fred
Ted = &Andy // Assigning Value 1776 (Address of Andy) to Ted via &

อย่างไรก็ดี ถ้าต้องการให้ตัวแปร Ted สามารถ ดึงค่า จากตัวแปร Andy ได้ ให้ ใช้สัญลักษณ์ ตัวอ้างอิงโดยอ้อม (Indirect operand) ซึ่งเป็นนำเครื่องหมายดอกจันทร์ (*) นำหน้าตัวแปรที่เก็บค่าแอดเดรสตัวแปร ดังตัวอย่างในภาพที่ 6.2

5534

Beth = *Ted // Beth มีค่าเท่ากับ ค่าที่ตัวแปร Ted ชี้ซึ่งเท่ากับ 25
ตัวพอยเตอร์จะใช้กับตัวแปรชนิดใดก็ได้โดยที่ ตัวแปรพอยเตอร์ดังกล่าวจะได้รับการกำหนดหน่วยความจำตามลักษณะชนิดตัวแปรด้วย เช่น ตัวแปรพอยเตอร์ประเภท int จะได้รับการกำหนดหน่วยความจำไว้ที่ 4 ไบต์
ตัวอย่างการกำหนด ค่า Pointer จะแสดงให้เห็นดังนี้ โดยผลการกำหนดค่าจะแสดงให้เห็นในภาพที่ 6.3 สังเกตให้ดีจะพบว่า ตำแหน่งแอดเดรสหน่วยความจำเลื่อนไป 4ไบท์ ระหว่างตัวแปร j กับ k และ ตัวแปร k กับ ptr

5535


จากตัวแปรพอยเตอร์สู่ตัวแปรอาเรย์ (Pointer to Array)


ตัวแปรอาเรย์มีความเกี่ยวข้องสัมพันธ์กับตัวแปรพอยเตอร์คือ ชื่อตัวแปรอาเรย์ (Array identifier) จะมีค่าเทียบได้กับตำแหน่งแอดเดรสของตารางช่องแรกซึ่งเทียบได้กับตำแหน่งแรก ตัวแปรพอยเตอร์ จะมีค่าเท่ากับแอดเดรสของตัวแปรแรกที่ตัวแปรพอยเตอร์ชี้ ดังตัวอย่างต่อไปนี้

int Num[20]; int *Ptr;

 การกำหนดค่าต่อไปนี้ถือว่าใช้ได้เหมือนกัน
 Ptr = Num; มีลักษณะเท่าเทียมกับ Ptr = &Num[0];


ความแตกต่างจะเกิดขึ้นเมื่อ มีการกำหนดค่าตัวต่อไป โดยที่เราสามารถกำหนดค่าแอดเดรสใหม่ให้ตัวแปร Ptr ได้ทันที ขณะที่ ตัวแปร Num จะชี้ไปที่ตำแหน่งแรกของตัวแปรจำนวนเต็ม ซึ่งมี 20 ตำแหน่งเสมอ ดังนั้น ตัวแปร Ptr จึงเป็นตัวแปรพอยเตอร์ที่เปลี่ยนตำแหน่งการชี้ได้ (Variable Pointer) ขณะที่ตัวแปร Num หรือชื่อตัวแปรอาเรย์เป็นตัวแปรพอยเตอร์ที่ตำแหน่งการชี้คงที่ตายตัว (Constant pointer) ดังตัวอย่างต่อไปนี้

int Num[20]; int *y; int *Ptr;
 Ptr = Num;
 *(Ptr+1) จะแสดงค่า Num[1];
 *(Ptr+i) จะแสดงค่า Num[i];
 y = &Num[0];
 y++;  // ตัวแปรนี้จะชี้ไปที่ Num[1] เท่านั้น


ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการ ใช้ตัวแปรพอยเตอร์ชี้ไปที่ตัวแปรตารางอาเรย์ ซึ่งผลที่ได้จะแสดงให้เห็นในภาพที่ 6.4

5536


ให้ผู้อ่านลองเปลี่ยนคำสั่งในบรรทัด B เป็น printf(“ptr + d = %dn“”, i.*ptr++); ก่อนการทดสอบครั้งต่อไป เพื่อดูว่าโปรแกรมจะให้ผลออกออกมาผิดจากโปรแกรมเดิม เมื่อทดสอบแล้ว ให้ ลองแก้บรรทัด B อีกครั้งโดยแก้เป็น printf(“ptr + d = %dn“”, i.*(++ptr));

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

*(array+5) = 50 จะมีค่าเทียบเท่ากับ array[5] = 50;

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

void main(void)
{
 int i, j=1,k=2;
 int *ptr1, *ptr2; // Declare both variables as integer pointers
 float value[100], result[100];
 float *ptr3, *ptr4; // Declare both variables as integer pointers

 prt1 = &j;  // Defining ptr1 pointing to j 
ptr2 = &k;  // Defining ptr2 pointing to k
 ptr3 = value;  // ptr3 contain the address for the 1st element of value
 ptr4 = &value[0]; //  ptr4 contain the address for the 1st element of value

 // Value manipulation
*ptr1 = *ptr1+2  /* การบวกค่าที่ตัวแปรพอยเตอร์ ptr ชี้ไป อีก 2 */
*ptr2 = *ptr1  /* กำหนดให้ค่าที่ ตัวแปร ptr2 ชี้ เท่ากับค่าที่ตัวแปร prt1 ชี้ */
k = *ptr2  /* กำหนดให้ k มีค่าเท่ากับ ค่าที่ตัวแปร ptr2 ชี้ */ 
}


การเปลี่ยนแปลง ตัวแปรพอยเตอร์ด้วยวิธีการทางคณิตศาสตร์ สามารถทำได้ดังตัวอย่างต่อนี้

float *ptr3, *ptr4; float table[100];  float Pi = 3.1415927;
ptr3 = &table[0]; // กำหนดให้ ptr3 เก็บค่าตำแหน่งแอดเดรส ของตาราง table ช่องแรก
ptr3++; // เปลี่ยนค่าตำแหน่งแอดเดรส ใน ptr3 เป็นแอดเดรสตาราง table ช่องที่2 
*ptr3 = Pi;  // ข้อมูลช่องที่ 2 ของตาราง table มีค่าเท่ากับ Pi   
ptr3+=25;  // กำหนดให้ ptr3 ชี้ไปที่ตำแหน่งแอดเดรสตาราง table ช่องที่ 26 
*ptr3 = 2.2222;  // ข้อมูล ตาราง table ช่องที่ 26 มีคาเท่ากีบ 2.2222  

ptr3 = table;  // กำหนดให้ ptr3 ชี้ไปที่ table ช่องแรก
 for(int ii =0; ii < 100; ii++)
 {
  *ptr3++ = 37.0 // กำหนดให้ข้อมูลทุกช่องของ ตาราง table เท่ากับ 37 
}
ptr3 = &table[0]; //  ptr3 contain the address for the 1st element of value
ptr4 = &table[0]; //  ptr4 contain the address for the 1st element of value


ข้อพึงระมัดระวังคือ เครื่องหมาย ++ และ -– นั้นมีลำดับความสำคัญที่สูงกว่า * ดังนั้น

*ptr++ มีค่าเท่ากับ *(ptr++) ซึ่งตัวแปรทั้ง 2 หมายถึงการเพิ่มตัวเลขตำแหน่ง แอดเดรส ไม่ใช้เพิ่มค่าที่ตัวพอยเตอร์ชี้

ส่วนกรณี *p++ = *q++ นั้น การกำหนดให้ค่าที่ตัวแปรพอยเตอร์ p มึค่าเท่ากับ ค่าที่ตัวแปรพอยเตอร์ q ชี้ จากน้นจึงมีการเลื่อนตำแหน่งแอดเดรสของตัวแปรพอยเตอร์ทั้ง 2 ไปอีก 1 ช่วง ดังตัวอย่างต่อไปนี้

*p = *q; p++; q++;

ในการเตรียมพื้นที่ตารางแบบไดนามิกให้ตัวแปรพอยเตอร์นั้นทำได้ดังนี้

กรณี C: int *ptr;
Ptr = (int *)malloc(6* sizeof(int));

กรณี C++: int *ptr;
ptr = new int[6];

ส่วนการคืนหน่วยความจำเมื่อสิ้นสุดการทำงานของโปรแกรมทำได้ดังนี้ ซึ่งต้องทำทุกครั้งเมื่อใช้คำสั่งออกจากโปรแกรม มิฉะนั้นเครื่องคอมพิวเตอร์ จะเกิดอาการค้าง (Hang) เมื่อปิดโปรแกรมเพราะไม่ได้คืนหน่วยความจำอย่างที่ควรจะทำ

กรณี C: free(ptr);

กรณี C++: delete[] ptr;

ตัวแปรพอยเตอร์และการเรียกฟังก์ชัน


การเรียกฟังก์ชันมีหลายแบบ ได้แก่
1) ฟังก์ชันที่ ไม่มีตัวแปรร่วมเป็นอาร์กิวเมนต์ และ ไม่คืนค่า (function with no parameter and no return value) ฟังกชันพวกนี้เพีบงแต่มีหน้าที่ทำงานอะไรบางอย่างตามที่ผู้เขียนโปรแกรมต้องการ เช่นกรณีฟังก์ชัน void skip3(void) เพื่อการกระโดดไปครั้งละ 3 บรรทัดเป็นต้น
2) ฟังก์ชันที่มีตัวแปรร่วมแต่ไม่คืนค่า ฟังก์ชันนี้จะทำงานตามค่าที่ตัวแปรร่วมกำหนด เช่นกรณีฟังก์ชัน void skip(int num) ซึ่งจะกระโดดครั้งละกี่บรรทัดตามจำนวนที่กำหนดลงในตัวแปร num
3) ฟังก์ชันที่ไม่มีตัวแปรร่วมแต่มีการคืนค่า ฟังก์ชันนี้มักจะให้ผู้ใช้โปรแกรมป้อนข้อมูลให้ฟังก์ชันจัดการประมวลผล ก่อนที่จะคืนค่าออกมา เช่นกรณีฟังก์ชัน float input(void) ที่จะคืนค่าหลังจากผู้ใช้โปรแกรมได้ป้อนข้อมูลให้ตามที่ฟังก์ชันสั่งให้ทำแล้ว
4) ฟังก์ชันที่มีตัวแปรร่วมและมีการ คืนค่า เช่นกรณีฟังก์ชัน float diff(float x, float y) ที่จะคืนค่าเป็นผลต่างระหว่างตัวแปร x และ ตัวแปร y

ส่วนฟังก์ชันที่มีการใช้ตัวแปรร่วม นั้น มีวิธีการเรียกอยู่ 2 แบบคือ
1) กรณี เรียกตามค่า (Call by Value) กรณีนี้การเปลี่ยนค่าตัวแปรร่วมที่เกิดขึ้นในฟังก์ชัน จะไม่มีผลต่อ ค่าตัวแปรร่วมตั้งต้นดังตัวอย่างต่อไปนี้

void skipline(int num)
{
 int i ;
 for(i = 0 ; i <=num-1 ; i++)
 {
  printf(“n”);
}
num = 100;
}
void main(void)
{
 int num1 = 60;
 printf(“%d”,num1);
 skipline(num1) ;
}

กรณี เรียกตามค่านี้ ค่า num มีการเปลี่ยนแปลง แต่ไม่มีผลต่อค่า num1 ใน main function

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

2) กรณี เรียกตามการอ้างอิง (Call by Reference) ซึ่งต้องมีการเรียกใช้ตัวแปรพอยเตอร์กรณีนี้การเปลี่ยนค่าตัวแปรร่วมที่เกิดขึ้นในฟังก์ชัน จะมีผลต่อ ค่าตัวแปรร่วมตั้งต้นดังตัวอย่างต่อไปนี้

void flip(int *x, int *y)
{
 int temp;
 temp = *x;
 *x = *y;
 *y = temp; 
}


ในการเรียกใช้ฟังก์ชัน flip นั้น จะต้องเรียกจาก แอดเดรสตัวแปร ดังนี้ flip(&a,&b)

มีบางกรณีที่มีการ เรียกตามค่าและเรียกตามการอ้างอิงในฟังก์ชันเดียวกันเช่นการแก้สมการกำลัง 2

bool quardsolve(float a, float b, float c, float *root1, float *root2)
{
 float disc = (b*b) – (4*a*c);
 if(disc < 0.0)
 {
  return false;
} 
else
{
 *root1 = (-b + sqrt(disc))/(2*a);
 *root2 = (-b – sqrt(disc))/(2*a);
 return true;
}
}


ตัวแปรดัชนี root1 และ root2 ทำหน้าที่เป็นตัวแปรร่วม Output ที่เรียกโดยการใช้อ้างอิง (Call by Reference) และ ตัวแปร a b c เป็นตัวแปรร่วม Input ที่ใช้กับการเรียกตามค่า (Call by Value) ถ้า การแก้สมการกำลัง 2 มีคำตอบให้คืนค่าเป็น true และ ค่า root1 และ root2 เป็นจำนวนจริง มิฉะนั้นให้ฟังก์ชันคืนค่า false ออกมาแทน เนื่องจาก ค่า root1 และ root2 นั้นได้รับการประกาศให้เป็น ตัวแปรร่วมอ้างอิง (Reference parameter) มีผลทำให้ฟังก์ชันต้นแบบ (Function Prototype) มีลักษณะเป็น
bool quardsolve(float, float, float, float *, float *)

เมื่อเรียกใช้งานฟังก์ชัน quardsolve ให้เรียกใช้งานฟังก์ชันดังนี้

float x1, y1; float test = quardsolve(1.0,2.0,1.0,&x,&y);

ซึ่งการเรียกใช้ฟังก์ชันดังกล้าวคล้ายคลึงกับการเรียกใช้คำสั่ง scanf

int number; scanf(“%d”, &number);

ตัวอย่างการเรียกใช้ฟังก์ชันที่มีตัวแปรพอยเตอร์จะแสดงให้เห็นดังตัวอย่างต่อไปนี้
1) ฟังก์ชันที่ มี ตัวแปรพอยเตอร์ที่แสดงในภาพที่ 6.5 นั้น จะอยู่ที่ฟังก์ชัน cal_cum โดยในฟังก์ชัน cal_sum มี ตัวแปร a และ b เป็น ตัวแปร input ขณะที่ตัวแปรพอยเตอร์ *r เป็นตัวแปร output เวลาเรียกใช้งานฟังก์ชัน cal_sum ในฟังก์ชัน main จะเรียกใช้งานดังนี้

double num1 = 2.4, num2 = 1.2,result;
cal_cum(num1,num2,&result);

แอดเดรสของตัวแปร result (&result) จะมีค่าเท่ากับชื่อตัวแปรพอยเตอร์ แม้ว่าตัวแปร result จะไม่ใช่ตัวแปรพอยเตอร์ก็ตาม

2) ฟังก์ชันที่มี ตัวแปรพอยเตอร์ที่แสดงในภาพที่ 6.6 นั้น จะอยู่ที่ฟังก์ชัน order ซึ่งใช้ตัวแปรพอยเตอร์ เป็น input และ output ทั้งคู่ ฟังก์ชัน order จะทำหน้าที่ จากเล็กไปหาใหญ่ โดยที่ ถ้า ค่าที่ตัวแปรพอยเตอร์ *small ชี้ขนาดใหญ่กว่าค่าที่ตัวแปรพอยเตอร์ *big ชี้ ให้ลงมือสับตำแหน่งการชี้ค่า

5537



หน้าที่ 8 - ตัวแปรสตริง
7. ตัวแปรสตริง (String)

ตัวแปร string หรือแถวตารางอาเรย์ของตัวหนังสือนั้น ภาษา C ไม่มีกลไกที่จะสนับสนุนได้โดยตรง ถ้าต้องการให้ภาษา C สามารถรองรับข้อมูลที่เป็นตัวหนังสือแล้ว จะต้องมีการเตรียมพื้นที่หน่วยความจำให่เหมาะสมหรือการใช้สตริงค่าคงที่ (strong constant) ซึ่งจะทำให้โปรแกรมภาษา C เข้าถึงข้อมูลสตริงซึ่งจะเป็นทั้งข้อมูลที่เข้ามาในโปรแกรมและเปป็นข้อมูลที่ออกจากโปรแกรมได้

โดยทั่วไปแล้วโปรแกรมภาษา C จะสนับสนุนข้อมูลที่เป็นอักษรโดยกำหนดให้อักษรแต่ละตัว ในรูปแบบตัวแปรประเภท char และข้อมูลสตริงนั้นโปรแกรมที่เขียนด้วยภาษา C จะถือว่าเป็นชุดข้อมูลตัวหนังสือที่เรียงกันตามลำดับ ขณะที่โปรแกรมที่เขียนด้วยภาษา C++ จะถือว่าสตริงเป็นตารางอาเรย์ข้อมูลประเภทตัวหนังสือ (Array of char) โดยตัวหนังสือจะอยู่ในตารางแต่ละช่อง และ ณ ตำแหน่งสิ้นสุดของตารางอาเรย์ที่เก็บตัวแปรสตริงนั้นจะมีสัญลักษณ์ “{PBODY}” (ตัวสิ้นสุดสตริง หรือ ตัว NULL) บรรจุอยู่ ดังนั้น ในการเตรียมเนื้อที่สำหรับตัวแปรสตริงที่ประกอบด้วยตัวหนังสือ n ตัวนั้นจะต้องเตรียมตารางไดนามิกขนาด n+1 ช่องเสมอ ดังตัวอย่างต่อไปนี้

1) ตัวหนังสือ ‘a’ เป็นตัวแปร char จะใช้เนื้อที่ 1 ไบต์ แต่ “a” เป็นตัวแปร string แล้ว จะต้องใช้เนื้อที่ขนาด 2 ไบต์เพื่อจุตัวอักษร ‘a’ และ ตัวสิ้นสุดสตริง ({PBODY})
2) ตัวแปรสตริงสามารถประกาศให้อยู่ในรูปตารางต่อไปนี้ได้
a. ตารางอาเรย์ตัวหนังสือ char String1[10] สามารถรองรับตัวหนังสือได้มากที่สุด 9 ตัว และ ตัวสิ้นสุดสตริง
b. ตัวแปรพอยเตอร์ตัวหนังสือ char *String2 สามารถรองนับตัวหนังสือได้ตามที่ผู้เขียนโปรแกรมได้เตรียมเนื้อที่ตารางไดนามิกส์ไว้

ถ้าต้องการทำอะไรกับตัวแปรสตริงแล้วให้เรียกใช้เฮดเดอร์ไฟล์ string.h ซึ่งเป็นไลบรารีไฟล์มาตรฐานสำหรับจัดการกับตัวแปรสตริง

เราสามารถตั้งค่าเริ่มต้นให้ตัวแปรสตริงได้ 2 วิธีหลักคือ
char S1[] = “Example”; ซึ่งภายในตัวแปรประกอบด้วย

S1 |E|x|a|m|p|l|e|{PBODY}|

ทั้งนี้เนื่องจากการเตรียมพื้นที่ขนาด 8 ช่องตาราง โดย 7 ช่องตารางแรกเก็บตัวหนังสือ และ ข้อมูลช่องสุดท้ายเก็บตัวสิ้นสุดสตริง

char S2[20] = “Another Example”; ซึ่งภายในตัวแปรประกอบด้วย

S2            |A|n|o|t|h|e|r| |E|x|a|m|p|l|e|{PBODY}||?|?|?|?| 


ทั้งนี้เนื่องจากการเตรียมพื้นที่ขนาด 20 ช่องตาราง แต่ใช้จริง 16 ช่อง คือตัวหนังสือ 15 ช่องและ ช่องที่ 16 เก็บตัวสิ้นสุดสตริง

การตั้งต้นตัวแปรสตริง ดังตัวอย่างต่อไปนี้ถือว่าเป็นการตั้งต้นตัวแปรสตริงที่ถูกต้อง

5538


ดังนั้น สิ่งที่เกี่ยวข้องสตริงสามารถสรุปออกมาได้ดังนี้
- ตัวแปรสตริงคือ ลำดับตัวหนังสือที่อยู่ภายในเครื่องหมายอัญประกาศ หรือเครื่องหมายคำพูด (“ ”) หรือ ตัวแปรสตริงคงที่ (String Constant)
- อีกทางหนึ่งตัวแปรสตริงถือว่าเป็นตัวแปรตารางอาเรย์ ของตัวหนังสือ (Array of char)
- สามารถใช้คำสั่ง printf scanf และ print ของภาษา C กับตัวแปรสตริงได้
- การประมวลเท็กซ์ คือการประมวลตัวแปรสตริง
- หน่วยข้อมูลพื้นฐานของตัวแปรสตริงคือตัวหนังสือหรือ ชุดตัวหนังสือ ไมใช่ตัวเลข
- การประยุกต์ใช้ตัวแปรสตริงนั้นคือ Word Processing หรือ Text Editor

ตัวอย่างการใช้ตัวแปรสตริงจะแสดงให้เห็นดังนี้
#include  
void main()
{
 char first[100], last[100];
 int i;

 printf("nEnter your first name:");
 scanf("%s", first );
 printf("nEnter your last name:");
 scanf("%s", last );
 printf("nYour full name is: %s %sn", first, last );
 printf("First name is: ");
 for( i=0; (i<100 && first[i] != '{PBODY}') ; i++ ){
    printf("%c ",first[i]);
 }
 printf("nLast name is: ");
 for( i=0; (i<100 && last[i] != '{PBODY}') ; i++ ){
    printf("%c ",last[i]);
 }
 printf("n");
} 


ผลที่ได้การเขียนโปรแกรมเติมชื่อและนามสกุลจะแสดงให้เห็นในภาพที่ 7.1

5539


การใช้งานตัวแปรสตริงนั้นมีหลักการดังนี้
- เตรียมพื้นที่ตารางอาเรย์ให้ใหญ่พอรับตัวหนังสือแทนคำและ ตัวสิ้นสุดสตริงดังต่อไปนี้

char str1[6] = “Hello”;
char str2[] = “Hello”;
char *str3 = “Hello”;
char str4[6] = {‘H’,’e’,’l’,’l’,’o’,’{PBODY}’};

ตัวแปรสตริงแต่ละตัวถือว่าเป็นตัวแปรคงที่ ดังนั้นจึง Assign ค่าแทนกันเช่นตัวอย่างต่อไปนี้ไม่ได้

Str1 = Str2; // ห้ามทำเช่นนี้กับตัวแปรสตริง เพราะมีวิธีอื่นที่ดีกว่านั้น

ตัวอย่างต่อไปนี้แสดงให้เห็น วิธี Assign ตัวแปรสตริงโดยใช้ตัวแปรพอยเตอร์ เข้าช่วย

char *S1 = “Hello”, *S2 = “Goodbye”; // ตัวแปร S1 และ S2 แก่ไขเปลี่ยนแปลงไม่ได้
char *S3;
S3 = S1; // บังคับให้ S3 ชี้ไปที่ แอดเดรสของตัวแปรพอยเตอร์ S1
S3 = S2; // บังคับให้ S3 ชี้ไปที่ แอดเดรสของตัวแปรพอยเตอร์ S2

อีกตัวอย่างในการ Assign ตัวแปรสตริงสามารถแสดงให้เห็นดังนี้
char Str[6] = “Hello”; // ตั้งต้นตัวแปร Str ด้วยคำว่า Hellow
char Str[0] = ‘Y’; char Str[4] = ‘{PBODY}’; // การแก้ไขให้ Str = “Yell”

อย่างไรก็ตาม วิธีแก้ไขเปลี่ยนแปลงข้อมูลสตริงในตารางมีเงื่อนไขว่า

- ตารางอาเรย์ที่ทำหน้าที่เป็นสตริงจะต้องปิดท้ายด้วย ‘{PBODY}’ เสมอ มิฉะนั้นตัวแปรอาเรย์ที่ทำหน้าที่เป็นสตริงจะไม่มีจุดสิ้นสุดสตริง
- สตริงที่แก้ไขต้องมีขนาดน้อยกว่าขนาดของตารางอาเรย์ที่เก็บค่าสตริง

การเปลี่ยนแปลงแก้ไขตัวแปรสตริงโดยใช้ไดนามิกอาเรย์นั้นต้องใช้ความระมัดระวังเป็นอย่างยิ่ง เพราะตารางไดนามิกส์นั้นต้องเตรียมพื้นที่ให้ ตัวสิ้นสุดสตริง (NULL) มิฉะนั้นโปรแกรมจะพิมพ์ข้อมูลสตริงออกมาผิดไปจากที่ต้องการ ดังตัวอย่างต่อไปนี้

char *s; 
 s = (char *)malloc(sizeof(char) * 0); // - WRONG, need to allocate 1 more space
 strcpy(s, "linux"); 
 printf("%sn", s);  


กรณีที่ใช้คำสั่ง strlen เพื่อหา ขนาดอาเรย์ก่อนทำ Dynamic allocation (malloc) นั้น พึงรับทราบว่า คำสั่ง strlen จะมีขนาดเพียงพอสำหรับตัวจำนวนอักษรที่ใช้ในตัวแปรสตริงเท่านั้น แต่ไม่ได้นับตัวแปร NULL ที่ใช้กำหนดจุดสิ้นสุดของสตริงด้วย โดยที่รายละเอียดเกี่ยวกับคำสั่ง strlen จะกล่าวถึงในตอนต่อไป


การกำหนดค่าให้ตัวแปรสตริง (String Assignment)


ตัวอย่างการกำหนดค่าให้ตัวแปรสตริง

char s[5]="SIIT";       // ใช้ได้, กำหนดค่าตัวแปรสตริงให้ตัวแปรอาเรย์
 char dept[5], *d=dept;  // ใช้ได้, กำหนดค่าตัวแปรสตริงให้ตัวแปรพอยเตอร์
 char name[20];
 name = “C. Sinthanayothin"; // ผิด อย่ากำหนดตัวแปรสตริงให้ตัวแปรอาเรย์ด้วยวิธีนี้
 strcpy(name,“C. Sinthanayothin"); // ใช้ได้, ให้ก๊อปปี้ค่าสตริงโดยใช้คำสั่ง strcpy  
 strcpy(dept,"IT");  // ใช้ได้, ก๊อปปี้ค่าสตริง ลงในตัวแปร dept
 printf("%s %s %sn",d,s,dept); // ใช้ได้, แสดงผลค่าสตริงด้ว printf
 d = strcpy(s,"EE");  // ใช้ได้, นี่คือการคืนค่าตัวแปรสตริง ลงใน ตัวแปร d ด้วยคำสั่ง strcpy
 printf("%s %s %s %sn",name,d,s,dept);

 char c1[30], c2[30]=“This is new c1 string”;
 char s[30] = "c programming "; 
 char str1[30];  // วิธี้ทำให้ตัวแปร str1 ไม่เป็น l-value เพราะเป็น constant array
 char *str; // วิธี้ทำให้ตัวแปร str เป็น l-value เพราะเป็น ตัวแปรพอยเตอร์ 
 strcpy(c1, c2);   // ใช้ได้, ก๊อปปี้ข้อมูลใน c2 ลงใน c1
 str = strcat(s,"is great!!"); // ใช้ได้, คือค่าเป็น ตัวแปรพอยเตอร์ประเภท char 
 str1 = strcat(s,"is great!!"); // ผิด, ฟังก์ชัน strcat ตอ้งคืนค่าด้วยตัวแปรพอยเตอร์ประเภท char ไม่ใช่ตารางอาเรย์ประเภท char


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

ตัวแปรสตริงนั้นจะใช้ %s เป็นตัว place holder ขณะที่ใช้คำสั่ง sscanf ในการอ่านข้อมูลจำพวกสตริงแล้วจัดการแปลงข้อมูลให้อยู่รูปตามที่กำหนด ดังตัวอย่างต่อไปนี้

int sscanf( const char *buffer, const char *format [, argument ] ... ); 

char ch, int inum, float fnum; 
char buffer[100] = “A10 50.0”;
sscanf(buffer,”%c%d%f”,&ch,&inum,&fnum); /* puts ‘A’ in ch, 10 in inum and 50.0 in fnum */

sscanf("  85  96.2  hello","%d%.3lf%s",&num,&val,word);

// results: num=85, val = 96.2, word = "hello"
  ส่วน sprintf ใช้แสดงผลลัพธ์ที่อยู่ในรูปแบบต่างๆ ให้เป็นตัวแปรสตริงดังตัวอย่างต่อไปนี้
int sprintf( char *buffer, const char *format [, argument] ... );

char buffer[100];
sprintf(buffer,”%s, %s”,LastName,FirstName);
if (strlen(buffer) > 15)
  printf(“Long name %s %sn”,FirstName,LastName);
ถ้าต้องการแปลงตัวเลขให้เป็นข้อมูลสตริงให้ใช้ฟังก์ชัน sprintf และ ตัวแปรดัชนี้ char ทำตามคำสั่งต่อไปนี้
int sscanf(char *buffer, const char *format [, argument ] ... ); 
char S[10]; int day, month,year;
sprintf(S,”%d/%d/%d”, day, month, year);
ถ้า day = 23, month = 8, year = 2001 ผลลัพธ์คือ S = “23/8/2001”
ต่อไปนี้คือตัวอย่างการใช้ sprintf กับ sscanf เมื่อเปรียบเทียบกับ printf
#include 
#include 
int main()
{
 char s[30]="85  96.2  hello";
 int num, mon=8,day=23,year=2001;
 double val;
 char word[10];  // can we use: char *word;   
  // make sure your assign the proper address for pointer
 sscanf("  85  96.2  hello","%d%lf%s",&num,&val,word);
 printf("num=%d   val=%.3lf   word=%sn",num,val,word);
 sprintf(s,"%d/%d/%d", mon, day, year);
 printf("s = %sn",s);
 return 0;
}
ข้อทบทวน: ภาษาซี มีฟังก์ชันที่ใช้ดัดแปลงแก้ไขตัวแปรสตริง หลายแบบตามความต้องการของผู้ใช้ได้แก่: strlen(str) – คำนวณความยาวสตริง ซึ่งจะมีการนับไปเรื่อยๆ จนกว่าจะพบตัวแปร NULL ถึงจะหยุดโดยไม่มีการนับตัวแปร NULL ที่สิ้นสุดประโยคเข้าไปด้วย ดังตัวอย่างต่อไปนี้ int strlen(char *str); char str1 = “hello”; strlen(str1) จะคืนค่าออกเป็น 5 เพราะ มีอักษร 5 ตัว strcpy(dst,src) – ก็อปปี้ข้อมูลจากตัวแปรสตริง src พร้อมตัวสิ้นสุดสตริง NULL ไปที่ตัวแปรสตริง dst แต่มีเงื่อนไขว่าตัวแปร des ต้องได้รับการเตรียมพื้นที่ให้ใหญ่พอๆกับตัวแปร src และการเตรียมพื้นที่ ให้ ตัวแปร src และ ตัวแปร dst ต้องไม่ทับซ้อนกันมิฉะนั้นจะให้ผลที่คาดเดาไม่ได้ การประกาศใช้ฟังก์ชัน strcpy จะทำได้ดังนี้ char *strcpy(char *dst, char *src) strncpy(dst,src,n) – ก็อปปี้ข้อมูลจากตัวแปรสตริง src โดยไม่ก๊อปปี้ตัวสิ้นสุดสตริง NULL ไปที่ตัวแปรสตริง dst เนื่องจากมีจำนวนตัวอักษร n เป็นตัวจำกัดไว้ แต่มีเงื่อนไขว่าตัวแปร des ต้องได้รับการเตรียมพื้นที่ให้ใหญ่พอๆกับตัวแปร src และการเตรียมพื้นที่ ให้ ตัวแปร src และ ตัวแปร dst ต้องไม่ทับซ้อนกันมิฉะนั้นจะให้ผลที่คาดเดาไม่ได้ การประกาศใช้ฟังก์ชัน strncpy จะทำได้ดังนี้ char *strncpy(char *dst, char *src, int n) strcmp(str1,str2) – เปรียบเทียบข้อมูลในตัวแปร str1 กับข้อมูลในตัวแปรสตริง str2 โดยใช้อักษรตัวแรกที่เริ่มต่างกันเป็นหลักซึ่งให้ผลดังนี้ น้อยกว่า 0 -- ถ้าค่า ASCII ที่เริ่มแตกต่างใน str1 มีขนาดเล็กกว่า str2 หรือ str1 เริ่มต้นเหมือนกับ str2 แต่ str2 นั้นมีตัวอักษรมากกว่า มากกว่า 0 -- ถ้าค่า ASCII ที่เริ่มแตกต่างใน str1 มีขนาดใหญ่กว่า str2 หรือ str1 เริ่มต้นเหมือนกับ str2 แต่ str1 นั้นมีตัวอักษรมากกว่า 0 ถ้าตัวแปรสตริงทั้ง 2 ตัวนั้นใช้ตัวอักษรเดียวกันและความยาวเท่ากัน การประกาศใช้ฟังก์ชัน strcmp จะทำได้ดังตัวอย่างต่อไปนี้
int strcmp(char *str1, char *str2) 
#include 
#include 
void main()
{
 printf("%d n", strcmp("hello","hello")); // returns 0
 printf("%d n", strcmp("yello","hello")); //returns value > 0
 printf("%d n", strcmp("Hello","hello")); // returns value < 0
 printf("%d n", strcmp("hello","hello there")); // returns value < 0
 printf("%d n", strcmp("some diff","some dift")); //returns value<0
}
strncmp(str1,str2,n) – เปรียบเทียบข้อมูลในตัวแปร str1 กับข้อมูลในตัวแปรสตริง str2 เป็นจำนวนอักษร n ตัวโดยใช้อักษรตัวแรกที่เริ่มต่างกันเป็นหลัก และ จะมีการเปรียบเทียบในกรณีที่อักษรในตัวแปรมีจำนวนน้อยกว่า n การประกาศใช้ฟังก์ชัน strncmp จะทำได้ดังตัวอย่างต่อไปนี้ int strncmp(char *str1, char *str2,int n) ความแตกต่างระหว่าง strcmp และ strncmp จะแสดงให้เห็นดังตัวอย่างต่อไปนี้ strcmp(“some diff”,”some DIFF”) -- returns value > 0 strncmp(“some diff”,”some DIFF”,4) -- returns 0 strcat(str1,str2) – ใช้ในการนำข้อมูลในตัวแปร str2 ไปต่อท้ายตัวแปร str1 ซึ่งจะคืนค่าเป็น str1 ที่ได้รับการต่อให้ยาวขึ้น ตัวอย่างเช่น char *s1 = “C. “, *s3 = strcat(s1,“Sinthanayothin”); ผลลัพธ์: s1 = s3 = “C. Sinthanayothin” การประกาศใช้ฟังก์ชัน strcat จะทำได้ดังตัวอย่างต่อไปนี้ char* strcat(char *s1, const char* s2); strncat(str1,str2,n) – ใช้ในการนำข้อมูลในตัวแปร str2 จำนวน n อักษรไปต่อท้ายตัวแปร str1 ซึ่งจะคืนค่าเป็น str1 ที่ได้รับการต่อให้ยาวขึ้น ตัวอย่างเช่น char s1[10] = "IT "; char *s3 = strncat(s1,"050Basic",3); printf("%s n", s1); printf("%s n", s3); ผลลัพธ์: s1 = s3 = IT 050 การประกาศใช้ฟังก์ชัน strncat จะทำได้ดังตัวอย่างต่อไปนี้ char* strncat(char *s1, const char* s2ม int n); ฟังก์ชันเหล่านี้มีอยู่ในไลบรารีเฮดเดอร์ไฟล์ string.h ซึ่งต้องใช้คำสั่ง #include ถึงจะเรียกใช้ได้ เราสามารถสร้างตารางอาเรย์ให้ตัวแปรสตริง (Array of string) ดังตัวอย่างต่อไปนี้ char month[12][10] = {“January”, “February”, “March”, “April”, “May”, “June”, “July”, “August”, “September”, “October”, “November”, “December” }; แต่ถ้าต้องการให้ตารางอาเรย์สำหรับตัวแปรสตริง สามารถรองรับข้อมูลสตริงที่มีความยาวต่างกัน (Ragged array of string) ให้ดังนี้ char *MonthNames[13]; /* an array of 13 strings */ MonthNames[1] = “January”; /* String with 8 chars */ MonthNames[2] = “February”; /* String with 9 chars */ MonthNames[3] = “March”; /* String with 6 chars */ … ตัวอย่างตารางอาเรย์ให้ตัวแปรสตริงแสดงวันทั้ง 7 แบบ Ragged array of string จะแสดงให้เห็นในตัวอย่างต่อไปนี้
#include 
#include 
void main() 
{
  char *days[7]; char TheDay[10];int day;
  days[0] = "Sunday"; days[1] = "Monday";
  days[2] = "Tuesday"; days[3] = "Wednesday";
  days[4] = "Thursday"; days[5] = "Friday";
  days[6] = "Saturday";
  printf("Please enter a day: ");
  scanf("%9s",TheDay);
  day = 0;
  while ((day < 7) && (strcmp(TheDay,days[day])))
        day++;
  if (day < 7) printf("%s is day %d.n",TheDay, day);
  else printf("No day %s!n",TheDay);
} 
ในการรับและส่งข้อมูลจำพวกสตริงนั้นทำได้หลายวิธี ได้แก่ 1) ในกรณีที่ใช้คำสั่ง printf และ scanf นั้นให้ใช้ %s ในการอ่านข้อมูลและ แสดงข้อมูลสตริงออกมา 2) ใช้คำสั่ง gets ในการข้อมูลสตริงออกมาทีละบรรทัด ดังตัวอย่างต่อไปนี้ char name1[10], name2[30]; scanf(“%s”,name1); // Input: IT 050 gets(name2); // Input: C Programming printf(“Course is %sn”,name); // Output: IT 050 printf(“Detail is %sn”,name2); // C Programming
5540
การใช้งานคำสั่งกับตัวแปร อักษร (char operators) นี่คือตัวอย่างการใช้งานฟังก์ชันที่คุมการรับส่งข้อมูลตัวอักษร char ch; ch = getchar(); // Input ch – ใช้แทน scanf("%c",&ch); putchar(ch); // Output ch – ใช้แทน printf("Character is %cn",ch); ch = 'S'; // ใช้ได้ ตัวอย่างการกำหนดค่าตัวอักษรให้ตัวแปร char putchar(ch); // ผลที่ได้ออกมาคือ S putchar('T'); // ผลที่ได้ออกมาคือ T ฟังก์ชันที่ใช้กำหนดการทำงานให้ตัวแปร char ได้แก่ คำสั่ง: isalpha(ch); การใช้งาน: คืนค่าเป็น TRUE (จริง) ถ้า ch มีค่าในช่วง A-Z หรือ a-z ตัวอย่าง: c = isalpha(ch); // คืนค่า TRUE ถ้า ch=‘M’ และ คืนค่า FALSE ถ้า ch=‘5’ คำสั่ง: isdigit(ch); การใช้งาน: คืนค่าเป็น TRUE (จริง) ถ้า ch มีค่าในช่วง0-9 ตัวอย่าง: d = isdigit(ch); // คืนค่า FALSE ถ้า ch=‘M’ และคืนค่า TRUE ถ้า ch=‘5’ คำสั่ง: islower(ch); การใช้งาน: คืนค่าเป็น TRUE (จริง) ถ้า ch มีค่าในช่วง a-z ตัวอย่าง: c = islower(ch); // คืนค่า FALSE ถ้า ch=‘M’ return false, และคืนค่า TRUE ถ้า ch=‘m’ คำสั่ง: isupper(ch); การใช้งาน: คืนค่าเป็น TRUE (จริง) ถ้า ch มีค่าในช่วง A-Z ตัวอย่าง: c = isupper(ch); // คืนค่า TRUE ถ้า ch=‘M’ return false, และคืนค่า FALSE ถ้า ch=‘m’ คำสั่ง: isspace(ch); การใช้งาน: คืนค่า TRUE ถ้า ch คิอช่องว่างขาว (space, newline, tab ) ตัวอย่าง: c = isspace(ch); // คืนค่า TRUE ถ้า ch = ‘n’ คืนค่า FALSE ถ้า ch=‘m’ คำสั่ง: tolower(ch); การใช้งาน: คืนค่าตัวพิมพ์เล็กจากค่าอักษรในตัวแปร ch ถ้าเป็นไปได้ ตัวอย่าง: c = tolower(ch); // คืนค่า ‘m’ ให้ c ถ้า ch=‘M’ คำสั่ง: toupper(ch); การใช้งาน: คืนค่าตัวพิมพ์ใหญ่จากค่าอักษรในตัวแปร ch ถ้าเป็นไปได้ คำสั่ง: c = toupper(ch); // คืนค่า ‘M’ ให้ c ถ้า ch=‘m’
หน้าที่ 9 - โครงสร้างสตรักเจอร์
8. โครงสร้างสตรักเจอร์ (Structure) สตรักเจอร์ เป็นวิธีการเก็บตัวแปรหลากชนิดให้อยู่เป็นกลุ่มก้อนเดียวกัน ทำให้สามารถแยกโปรแกรมออกเป็นหน่วย (Modular Programming) ซึ่งแก้ไขได้ง่ายเพราะสามารถแยกฟังก์ชันและตัวแปรออกเป็นหน่วยๆ ซึ่งก็เป็นประโยชน์ในการสร้างฐานข้อมูลด้วย การตั้งต้นสร้างสตรักเจอร์ สามารถทำได้หลายรูปแบบดังตัวอย่างต่อไปนี้ 1) การใช้ คำสั่ง struct สร้างตัวแปรชนิด student_a ผ่านตัวแปร student struct student
{ 
   char *first; 
   char *last; 
   char SSN[9]; 
   float gpa; 
   char **classes; 
}; 
struct student student_a; 2) การใช้ คำสั่ง struct สร้างตัวแปรชนิด student_a โดยตรง struct
{ 
    char *first; 
    char *last; 
    char SSN[10]; 
    float gpa; 
    char **classes; 
} student_a; 
3) การใช้ คำสั่ง typedef struct เพื่อสร้างตัวแปรชนิด student_a ผ่านตัวแปร student typedef struct
{ 
    char *first; 
    char *last; 
    char SSN[9]; 
    float gpa; 
    char **classes; 
} student; 

student student_a;
การกำหนดชนิดตัวแปร ทำได้โดยใช้คำสั่ง typedef ตามหลักการใช้งานดังนี้ typedef type name (typedef ชื่อของชนิดตัวแปร ชื่อที่ใช้เรียกชนิดตัวแปรจริง) ตัวอย่าง:
typedef int INTEGER;
  INTEGER x;  /* x คือตัวแปรตระกูล INTEGER */

  typedef char *STRING;
  STRING sarray[10];  
/* sarray คือตารางอาเรย์ ของ char* ซึ่งเทียบได้กับการประกาศว่า 
char *sarray[10] */
ในโครงสร้างตัวแปรที่อยู่ในรูป structure นั้นประกอบด้วยตัวแปรที่เกี่ยวข้องในลักษณะเดียวกับตารางอาเรย์ แม้จะไม่ใช้ตัวแปรประเภทเดียวกัน โดยจะแบ่งตัวแปรออกเป็น field โดยที่กลุ่มตัวแปรใน structure จะถือว่าเป็นส่วนหนึ่งของหน่วยใหญ่หน่วยเดียวกันดังที่แสดงในภาพที่ 8.1 ส่วนการเปรียบเทียบความแตกต่างระหว่าง ตารางอาเรย์และตัวแปร struct ที่มีตัวแปรที่เป็นตารางอาเรย์ จะแสดงให้เห็นในภาพที่ 8.2
5542
การเรียกใช้งาน สตรักเจอร์ โดยปกติการสร้างสตรักเจอร์ ทำได้โดยใช้คำสั่ง typedef และ struct ซึ่งโครงสร้างภายในของสตรักเจอร์ จะประกอบด้วยตัวแปรต่างๆ ซึ่งแยกออกเป็น field ตัวแปรชนิดต่างๆ และในแต่ละ field จะมีการกำหนดพื้นที่หน่วยความจำ จนเพียงพอสำหรับแต่ละ field ดังตัวอย่างต่อไปนี้
typedef struct {
  Type1 FieldName1;
  Type2 FieldName2;
  Type3 FieldName3;
  /* as needed */
} VarName;
VarName คือชื่อชนิดตัวแปรสตรักเจอร์ทั้งหมด นี่คือตัวอย่างการประกาศใช้ตัวแปร struct ชนิด student_t เพื่อบันทึกข้อมูลนักเรียน พร้อมตัวอย่างการกำหนดตัวแปร ชนิด student_t ซึ่งสามารถทำได้อย่างตัวแปรทั่วไป เช่นเดียวกับ int float
#define   MAX_LEN  12      /*  Length of name  */
typedef   struct  {
   char      name[MAX_LEN];
   int      id;
   float     gpa;
   char major[10];
}  student_t;

student_t   s;
student_t  stu1, stu2, stu3;
การเข้าถึง ตัวแปร field ภายในตัวแปร struct ทำได้โดยการใช้ จุด (.)แล้วตามด้วยชื่อ field ดังตัวอย่างกรณีการเข้าถึงตัวแปร ชนิด DATE ที่จะแสดงให้เห็นดังต่อไปนี้ และผลที่ได้จะแสดงให้เห็นในภาพที่ 8.3 หลักการเข้าถึงตัวแปร struct: VarName.FieldName typedef struct
{
  int month, day, year;
} DATE;

void main() {
  DATE d1;

  d1.month = 12;
  d1.day = 2;
  d1.year = 1970;
}
5543