การเขียนโปรแกรมภาษา C (ตอนที่ 2)#

 

หัวข้อสำหรับการเรียนรู้#

  • นิพจน์ (Expression) และ ตัวดำเนินการ (Operators)
  • ชนิดข้อมูลพื้นฐาน (Primitive Data Types)
  • การแปลงชนิดข้อมูล (Data Type Conversion)
  • ชนิดข้อมูลเชิงประกอบ (Compound Data Types)
  • อาร์เรย์และตัวอย่างการใช้งานในเบื้องต้น (Arrays)

นิพจน์ (Expressions) และ ตัวดำเนินการ (Operators)#

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

"ตัวดำเนินการ" (Operators) อาจจำแนกได้ตามจำนวนของข้อมูลที่ต้องใช้ หรือ เรียกว่า "ตัวถูกดำเนินการ" หรือ "โอเปอแรนด์" (Operands)

  • ตัวดำเนินการที่มีหนึ่งตัวถูกดำเนินการ (Unary Operators)
  • ตัวดำเนินการที่มีสองตัวถูกดำเนินการ (Binary Operators)
  • ตัวดำเนินการที่มีสามตัวถูกดำเนินการ (Ternary Operators)

ตัวดำเนินการในภาษาซี ยังสามารถจำแนกได้เป็นกลุ่มต่าง ๆ ดังนี้

  • ตัวดำเนินการเชิงตัวเลข (Arithmetic Operators) ใช้สำหรับการสร้างนิพจน์เพื่อคำนวณเชิงตัวเลข เช่น การบวก ลบ คูณ หาร เป็นต้น
  • ตัวดำเนินการสัมพันธ์ (Relational Operators) ใช้สำหรับการเปรียบเทียบค่ามากกว่า น้อยกว่า หรือ เท่ากับ เป็นต้น และให้ค่าเป็นจริง (1) หรือ เท็จ (0)
  • ตัวดำเนินการตรรกะ (Logical Operators) ใช้สำหรับการสร้างนิพจน์ที่เป็นบูลีน (Boolean Expression) ให้ค่าเป็นจริง (1) หรือ เท็จ (0)
  • ตัวดำเนินการระดับบิต (Bitwise Operators) ใช้สำหรับการสร้างนิพจน์ที่มีการดำเนินการกับข้อมูลในแต่ละบิต และใช้กับข้อมูลที่เป็นเลขจำนวนเต็ม
  • ตัวดำเนินการเพื่อกำหนดค่าให้ตัวแปร (Assignment Operator) ใช้ในการกำหนดค่าให้ตัวแปรโดยใช้ค่าจากนิพจน์
  • ตัวดำเนินการแบบอื่น ๆ เช่น sizeof() เพื่อระบุขนาด (จำนวนไบต์) ของชนิดข้อมูล และตัวดำเนินการที่เกี่ยวข้องกับการใช้ตัวแปรประเภทที่เรียกว่า "ตัวชี้" หรือ "พอยน์เตอร์" (Pointers)

ตัวดำเนินการเชิงตัวเลข#

ตารางแสดงตัวดำเนินการเชิงตัวเลขในภาษาซี มีดังนี้

Arithmetic Operator Description
+ Addition (การบวก)
Subtraction (การลบ)
* Multiplication (การคูณ)
/ Division (การหาร)
% Modulus (การหารเลขจำนวนเต็มเหลือเศษ)
++ (Pre- / Post-) Increment (การเพิ่มค่าทีละหนึ่ง แบบทำก่อนหรือหลัง)
-- (Pre- / Post-) Decrement (การลดค่าทีละหนึ่ง แบบทำก่อนหรือหลัง)
<< Shift Left (การเลื่อนบิตไปทางซ้าย)
>> Shift Right (การเลื่อนบิตไปทางขวา)

 

ตัวอย่างการเขียนนิพจน์ โดยให้ x และ y เป็นตัวแปรที่มีชนิดข้อมูลแบบ int (เลขจำนวนเต็ม)

  • (x + y)/2 หมายถึง การนำค่าของตัวแปร x มาบวกกับค่าของตัวแปร y แล้วจึงหารด้วย 2 ได้เป็นค่าของนิพจน์
  • x / 10 หมายถึง การนำค่าของตัวแปร x มาหารด้วย 10 เพื่อใช้เป็นค่าของนิพจน์ เนื่องจาก x มีชนิดข้อมูลเป็นเลขจำนวนเต็ม เมื่อหารแล้วเหลือเศษ ให้ตัดเศษทิ้งไป เช่น ถ้า x เท่ากับ 53 จะได้ค่าของนิพจน์เป็น 5
  • x % 10 หมายถึง การนำค่าของตัวแปร x มาหารด้วย 10 แล้วนำเศษที่ได้ไปใช้เป็นค่าของนิพจน์ เช่น ถ้า x เท่ากับ 53 จะได้ค่าของนิพจน์เป็น 3
  • (x << 8) + y หมายถึง การนำค่าของตัวแปร x ซึ่งเป็นเลขจำนวนเต็ม (ให้มองเป็นเลขฐานสอง) มาเลื่อนบิตไปทางซ้าย 8 ตำแหน่ง แล้วจึงนำผลลัพธ์ที่ได้ไปบวกกับค่าของตัวแปร y ได้เป็นค่าของนิพจน์
  • ++x * 2 หมายถึง การเพิ่มค่าของตัวแปร x อีกหนึ่ง เนื่องจากมีตัวดำเนินการ ++ อยู่ข้างหน้าตัวแปร x (การทำงานในลักษณะนี้เรียกว่า Pre-increment สำหรับตัวแปร x) แล้วนำค่าใหม่ของตัวแปรนั้น มาคูณกับ 2
  • x++ * 2 หมายถึง การนำค่าของตัวแปร x มาคูณกับ 2 แล้วได้เป็นค่าของนิพจน์ และหลังจากนั้นแล้ว ให้เพิ่มค่าของตัวแปร x ขึ้นอีก 1 เนื่องจากมีตัวดำเนินการ ++ ตามหลังตัวแปร x (เป็นการทำงานแบบ Post-increment) การทำงานของโอเปอเรเตอร์ ++ ในลักษณะนี้ มีผลข้างเคียง (Side Effect) ที่เกิดขึ้นกับค่าของตัวแปร x
  • --x หมายถึง การนำตัวแปร x มาลดค่าลงอีกหนึ่งก่อน แล้วนำค่าที่ได้ไปใช้เป็นค่าของนิพจน์
  • -(-x) จะได้ค่าของตัวแปร x (ใช้ตัวดำเนินการ - อยู่ข้างหน้าตัวแปร x สองครั้ง) และค่าของตัวแปร x ไม่มีการเปลี่ยนแปลง

ข้อสังเกต: ถ้าใช้ตัวดำเนินการเลื่อนบิตกับตัวแปรหรือค่าคงที่มีชนิดข้อมูลเป็นเลขจำนวนเต็ม การเลื่อนบิตไปทางซ้ายหนึ่งตำแหน่ง ให้ผลเหมือนการคูณด้วย 2 และการเลื่อนบิตไปทางขวาหนึ่งตำแหน่ง ให้ผลเหมือนการหารด้วย 2 (แล้วปัดเศษทิ้ง)

 


ตัวดำเนินการสัมพันธ์#

ตารางแสดงตัวดำเนินการสัมพันธ์ในภาษาซีสำหรับการเปรียบเทียบค่าของนิพจน์ มีดังนี้

Relational Operator Description
== Equal (เท่ากัน)
!= Not Equal (ไม่เท่ากัน)
> Greater Than (มากกว่า)
>= Greater Than or Equal (มากกว่าหรือเท่ากัน)
< Less Than (น้อยกว่า)
<= Less Than or Equal (น้อยกว่าหรือเท่ากัน)

ตัวอย่างการเขียนนิพจน์ โดยให้ x และ y เป็นตัวแปรที่มีชนิดข้อมูลพื้นฐาน เช่น char int หรือ float ที่ให้ค่าเป็นเลขจำนวนเต็ม หรือ เลขทศนิยม

  • x > y หมายถึง นิพจน์นี้ให้ค่าเป็น 1 ถ้า x มีค่ามากกว่า y แต่ถ้าไม่ใช่ จะได้ค่าเป็น0
  • x <= y หมายถึง นิพจน์นี้ให้ค่าเป็น 1 ถ้า x มีค่าน้อยกว่าหรือเท่ากับ y แต่ถ้าไม่ใช่ จะได้ค่าเป็น 0
  • 1 - (x!=y) หมายถึง ถ้า x ไม่เท่ากับ y จะได้ค่าเป็น 0 แต่ถ้าตัวแปรทั้งสองมีค่าเท่ากัน จะได้ค่าของนิพจน์เป็น 1 หรือจะเขียนนิพจน์ใหม่ที่ให้ผลลัพธ์เหมือนกันคือ (x==y)

 


ตัวดำเนินการตรรกะ#

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

Logical Operator Description
! Logical NOT (กลับค่าลอจิก หรือ นิเสธ)
&& Logical AND (ทำเหมือนลอจิกเกต "และ")
|| Logical OR (ทำเหมือนลอจิกเกต "หรือ")

ตารางค่าความจริง

x y !x !y (x&&y) (x||y) (!x||!y) !(x&&y) !(x||y)
0 0 1 1 0 0 1 1 1
0 1 1 0 0 1 1 1 0
1 0 0 1 0 1 1 1 0
1 1 0 0 1 1 0 0 0

ตัวอย่างการเขียนนิพจน์

  • !x จะได้ค่าเป็น 1 ถ้าค่าของตัวแปร x เท่ากับ 0 (ในกรณีอื่น จะได้ค่าเป็น 0) ในภาษาซี ค่าที่ไม่เท่ากับ 0 เมื่อถูกนำไปใช้กับตัวดำเนินการตรรกะ จะให้ค่าเหมือน 1 (ลอจิก)
  • !(!x) หรือ !!x หรือ (x!=0) จะให้ค่านิพจน์เหมือนกัน
  • x && (!x) จะได้ค่าเป็น 0 และ !(x && (!x)) จะได้ค่าเป็น 1 โดยไม่ขึ้นอยู่กับค่าของตัวแปร x ในขณะนั้น
  • (!x) || x และ !(x && (!x)) จะให้ค่าของนิพจน์เหมือนกัน ซึ่งเป็นไปตามกฎเดอมอร์แกน (De Morgan's laws) ในวิชาตรรกศาสตร์
  • 1 - (x!=y) และ (x==y) ให้ค่าของนิพจน์เหมือนกัน และสามารถเขียนนิพจน์ใหม่ที่ให้ผลเหมือนกันได้เป็น 1 - !(x==y) และ !(x!=y) โดยมีการใช้ตัวดำเนินการ ! เพื่อกลับค่าลอจิก (การกลับค่าลอจิก หมายถึง ถ้าโอเปอแรนด์มีค่าเป็น 0 ก็จะได้ค่าเป็น 1 หรือตรงกันข้าม)

ในการสร้างนิพจน์โดยใช้ตัวดำเนินการ && (AND) และ || (OR) มีหลักการในการคำนวณค่าของนิพจน์ ดังต่อไปนี้

  • ให้คำนวณค่าของโอเปอแรนด์ที่อยู่ด้านซ้ายมือของตัวดำเนินการก่อน
  • ถ้าโอเปอแรนด์ทางซ้ายมือของ && มีค่าเป็นเท็จ ก็จะไม่มีการคำนวณค่าของโอเปอแรนด์ที่อยู่ขวามืออีกต่อไป เพราะว่าค่าของนิพจน์ได้เป็นเท็จแล้ว
  • ถ้าโอเปอแรนด์ทางซ้ายมือของ || มีค่าเป็นจริง ก็จะไม่มีการคำนวณค่าของโอเปอแรนด์ที่อยู่ขวามืออีกต่อไป เพราะว่าค่าของนิพจน์ได้เป็นจริงแล้ว

การคำนวณค่าของนิพจน์ในลักษณะนี้เรียกว่า Short-Circuit Evaluation

ในไฟล์ <stdbool.h> มีการกำหนดชนิดข้อมูล bool และค่าคงที่ true และ false ซึ่งมีค่าเป็น 1 และ 0 ตามลำดับ

  printf( "sizeof(bool) = %lu\n", sizeof(bool) ); // 4 bytes
  printf( "true  = %d\n", true  ); // 1
  printf( "false = %d\n", false ); // 0

 


ตัวดำเนินการระดับบิต#

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

Bitwise Operator Description
~ Bitwise NOT (กลับค่าบิตทุกตำแหน่ง) หรือ 1's Complement
& Bitwise AND
| Bitwise OR
^ Bitwise XOR (Exclusive OR)

ตัวอย่างการใช้ตัวดำเนินการกับข้อมูลที่เป็นเลขจำนวนเต็มแบบ 8 บิต (8-bit unsigned intger และใช้ชนิดข้อมูลที่เรียกว่า unsigned char หรือ uint8_t ที่มีค่าของข้อมูลได้ในช่วง 0 .. 255 (หรือ 0x00 .. 0xff ในฐานสิบหก)

ถ้าให้ตัวแปร x และ y มีค่าเป็น 0b00110110 (0x36) และ 0b00001111 (0x0f) จะสามารถหาค่าของนิพจน์ต่อไปนี้ได้

  • นิพจน์ x&y ได้ค่าเป็น 0b00000110 (0x06) และ ~(x&y) ได้ค่าเป็น 0b11111001 (0xf9)
  • นิพจน์ x|y ได้ค่าเป็น 0b00111111 (0x3f) และ ~(x|y) ได้ค่าเป็น 0b11000000 (0xc0)
  • นิพจน์ x^y ได้ค่าเป็น 0b00111001 (0x39) และ ~(x^y) ได้ค่าเป็น 0b11000110 (0xc6)
x 0b00110110   x 0b00110110   x 0b00110110
y 0b00001111   y 0b00001111   y 0b00001111 
  ----------     ----------     ----------
& 0b00000110   | 0b00111111   ^ 0b00111001
~ 0b11111001   ~ 0b11000000   ~ 0b11000110

 

โค้ดตัวอย่างในภาษาซี: การใช้ตัวดำเนินการตรรกะกับตัวแปร x และ y

#include <stdio.h>
#include <inttypes.h>  // defines uint8_t (8-bit unsigned int)

int main() {
  uint8_t x = 0b00110110; 
  uint8_t y = 0b00001111;
  printf( "x = 0x%02x\n", x );  // 0x36
  printf( "y = 0x%02x\n", y );  // 0x0f
  printf( "(x&y)  = 0x%02x\n", (x&y) ); // 0x06
  printf( "(x|y)  = 0x%02x\n", (x|y) ); // 0x3f
  printf( "(x^y)  = 0x%02x\n", (x^y) ); // 0x39
  printf( "~(x&y) = 0x%02x\n", (uint8_t) ~(x&y) ); // 0xf9 
  printf( "~(x|y) = 0x%02x\n", (uint8_t) ~(x|y) ); // 0xc0
  printf( "~(x^y) = 0x%02x\n", (uint8_t) ~(x^y) ); // 0xc6
  return 0;
}

 


ตัวดำเนินการกำหนดค่าให้ตัวแปร#

ตารางแสดงตัวดำเนินการกำหนดค่าให้ตัวแปรในภาษาซี

Assignment Operator Description
= Assignment
+= Addition and Assignment
-= Subtraction and Assignment
*= Multiplication and Assignment
/= Division and Assignment
%= Modulus and Assignment
<<= Shift Left Assignment
>>= Shift Right Assignment
&= Bitwise AND and Assignment
|= Bitwise OR and Assignment
^= Bitwise XOR and Assignment

 

ตัวอย่างการเขียนนิพจน์หรือประโยคคำสั่ง

  • x = x+1; หมายถึง ให้คำนวณค่าของนิพจน์ทางขวาของเครื่องหมาย = ซึ่งก็คือ การนำค่าของตัวแปร x มาบวกกับ 1 แล้วใช้ผลลัพธ์ที่ได้เป็นค่าใหม่ของตัวแปร x หรือจะเขียนใหม่เป็นประโยคคำสั่ง x++; หรือ x +=1; ซึ่งให้ผลเหมือนกัน
  • x = x << 1; หมายถึง ให้นำมาของตัวแปร x มาเลื่อนบิตไปทางซ้ายหนึ่งตำแหน่ง แล้วนำไปใช้เป็นค่าใหม่ของตัวแปร x หรือจะเขียนประโยคคำสั่ง x <<= 1; ก็ให้ผลเหมือนกัน
  • x = y = 0; เป็นการกำหนดค่าให้ตัวแปร y เป็น 0 ก่อน แล้วใช้ค่าดังกล่าวไปกำหนดค่าให้ตัวแปร x ในลำดับถัดไป (ดำเนินการจากขวาไปซ้าย)
  • x = y, y = 0; เป็นการกำหนดค่าให้ตัวแปร x เท่ากับค่าของตัวแปร y แล้วจึงทำให้ค่าของตัวแปร y เป็น 0 ตามลำดับ (ดำเนินการจากซ้ายไปขวา)
  • x += y <<= 1; จะให้ผลเหมือนกับการเขียนประโยคคำสั่ง x += (y <<= 1); โดยดำเนินการจากขวาไปซ้าย หรืออาจจะเขียนใหม่ โดยแบ่งเป็นสองประโยคคำสั่งดังนี้ y <<= 1; x += y;

 


ลำดับความสำคัญตัวดำเนินการ (Precedence of Operators)#

ตัวดำเนินการในภาซี มีลำดับก่อนหรือหลังในการดำเนินการ (เรียกว่า Operator Precedence) ที่แตกต่างกันได้ หรือมีลำดับความสำคัญไม่เท่ากัน ถ้ามีลำดับการดำเนินการเท่ากัน ให้พิจารณาทิศทางการดำเนินการ หรือทิศทางการจัดกลุ่มของ ตัวถูกดำเนินการ (Associativity) ของตัวดำเนินการ ซึ่งส่วนใหญ่จะเป็นแบบจากซ้ายไปขวา (Left-to-Right) แต่บางกรณีก็เป็นแบบจากขวาไปซ้าย (Right-to-Left)

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

ตัวดำเนินการและลำดับการดำเนินการก่อนหลัง เรียกจากมาก (ให้ทำก่อน) ไปน้อย

ประเภทของตัวดำเนินการ ตัวดำเนินการ ทิศทางการจัดกลุ่ม
Postfix () [] -> . ++ -- Left to right
Unary + - ! ~ ++ -- (type) * & sizeof() Right to left
Multiplicative * / % Left to right
Additive + - Left to right
Shift << >> Left to right
Relational < <= > >= Left to right
Equality == != Left to right
Bitwise AND & Left to right
Bitwise XOR ^ Left to right
Bitwise OR | Left to right
Logical AND && Left to right
Logical OR || Left to right
Conditional ? : Right to left
Assignment = += -= *= /= %= >>= <<= &= ^= |= Right to left
Comma , Left to right

 

ตัวอย่างการเขียนนิพจน์โดยใช้โอเปอเรเตอร์กับตัวแปร เช่น x y ข้อมูลค่าคงที่

  • นิพจน์ x - y + 1 : เนื่องจาก + และ - มีลำดับการทำงานเท่ากัน ให้ดำเนินการจากซ้ายไปขวา ดังนั้นจึงให้ผลลัพธ์เหมือนนิพจน์ที่มีการใช้วงเล็บ (x-y)+1
  • นิพจน์ 1 + x * y / 2 : เนื่องจาก * และ / มีลำดับการทำงานเท่ากัน แต่สูงกว่า + จึงให้ผลลัพธ์เหมือนนิพจน์ที่มีการใช้วงเล็บ 1 + ((x*y)/2)
  • นิพจน์ x+y > 0 || x-y < 2 : ให้ผลลัพธ์เหมือนนิพจน์ ((x+y) > 0) || ((x-y) < 2)
  • นิพจน์ y>0 || y==0 && x!=0 : ให้ผลลัพธ์เหมือนนิพจน์ (y>0) || ((y==0) && (x!=0))
  • นิพจน์ x+1 << 1 > 15 : ให้ผลลัพธ์เหมือนนิพจน์ ((x+1) << 1) > 15)

รูป: แผนผังของนิพจน์ (Expression Tree) สำหรับ 1 + x * y / 2 ที่แสดงลำดับการดำเนินการ

 


ชนิดข้อมูลพื้นฐานในภาษาซี#

ตัวแปรจะต้องมีการระบุชื่อและชนิดของข้อมูลที่จะใช้กับตัวแปร ชนิดของข้อมูลพื้นฐานในภาษาซี ได้แก่

  • ข้อมูลที่เป็นเลขจำนวนเต็ม int อาจมีคำเขียนกำกับไว้ข้างหน้า เช่น signed และ unsigned เพื่อระบุว่า มีค่าเป็นลบได้หรือไม่ หรือเพื่อกำหนดขนาดของข้อมูล เช่น char, short, long และ long long เป็นต้น
  • ข้อมูลที่มีขนาดเล็กสุด คือ char หรือ Character มีขนาดหนึ่งไบต์ ใช้แทนตัวอักขระได้หนึ่งตัวในชุดอักขระแอสกี หรือจะใช้กับค่าเลขจำนวนเต็มขนาด 8 บิตก็ได้
  • ข้อมูลที่เป็นเลขทศนิยม ได้แก่ float, double และ long double ซึ่งจะมีขนาดข้อมูลไม่เท่ากันและมีช่วงในการเก็บค่าได้ไม่เท่ากัน

ลำดับของชนิดข้อมูล (Ranks of Data Types) ซึ่งพิจารณาจากขนาดของข้อมูล (จำนวนไบต์ที่ต้องใช้) เรียงน้อยไปมากคือ

  • ข้อมูลที่เป็นเลขจำนวนเต็ม: char, short, int, long, long long ตามลำดับ (และ unsigned กับ signed ของชนิดข้อมูลเหมือนกัน เช่น int มีลำดับเท่ากัน)
  • ข้อมูลที่เป็นเลขทศนิยม: float, double, long double

ในไฟล์ <inttypes.h> ซึ่งมีการนำเข้าไฟล์ <stdint.h> ได้มีการประกาศชนิดข้อมูลสำหรับเลขจำนวนเต็มที่จำแนกตามจำนวนบิต (Fixed width integer types) ตามมาตรฐาน C99 เช่น

  • int8_t, int16_t, int32_t, int64_t สำหรับชนิดข้อมูลเลขจำนวนเต็มแบบ signed int ที่มีขนาด 8 บิต , 16 บิต, 32 บิต และ 64 บิต ตามลำดับ
  • uint8_t, uint16_t, uint32_t, uint64_t สำหรับขนิดข้อมูลเลขจำนวนเต็มแบบ unsigned int ที่มีขนาด 8 บิต , 16 บิต, 32 บิต และ 64 บิต ตามลำดับ
  • INT8_MIN, INT16_MIN, INT32_MIN, INT64_MIN เป็นค่าต่ำสุด สำหรับชนิดข้อมูล int8_t, int16_t, int32_t, int64_t ตามลำดับ
  • INT8_MAX, INT16_MAX, INT32_MAX, INT64_MAX เป็นค่าสูงสุดสำหรับชนิดข้อมูล int8_t, int16_t, int32_t, int64_t ตามลำดับ
  • UINT8_MAX, UINT16_MAX, UINT32_MAX, UINT64_MAX เป็นค่าสูงสุดสำหรับชนิดข้อมูล uint8_t, uint16_t, uint32_t, uint64_t ตามลำดับ

 

ในไฟล์ <limits.h> มีการประกาศสัญลักษณ์หรือค่าคงที่สำหรับชนิดข้อมูลที่เป็นเลขจำนวนเต็ม เช่น

Constant Meaning Value
CHAR_BIT Number of bits of a byte 8
SCHAR_MIN Min. value for signed char -128
SCHAR_MAX Max. value for signed char +127
UCHAR_MAX Max. value for unsigned char +255 (0xff)
CHAR_MIN Min. value for char -128
CHAR_MAX Max. value for char +127
SHRT_MIN Min. value for short -32768
SHRT_MAX Max. value for short +32767
USHRT_MAX Max. value for unsigned short +65535 (0xffff)
INT_MIN Min. value for int -2147483648
INT_MAX Max. value for int +2147483647
UINT_MAX Max. value for unsigned int +4294967295 (0xffffffff)
LONG_MIN Min. value for long -2147483648
LONG_MAX Max. value for long +2147483647
ULONG_MAX Max. value for unsigned long +4294967295 (0xffffffff)
LLONG_MIN Min. value for long long -9,223,372,036,854,775,808
LLONG_MAX Max. value for long long +9,223,372,036,854,775,807
ULLONG_MAX Max. value for unsigned long long +18,446,744,073,709,551,615 (0xffffffffffffffff)

ความแตกต่างระหว่าง unsigned กับ signed สำหรับชนิดข้อมูลที่เป็นเลขจำนวนเต็ม

  • unsigned หมายถึง มีค่าเป็น 0 หรือบวกเท่านั้น
  • signed หมายถึง มีค่าเป็นลบหรือบวกได้

ถ้าให้ เป็นตัวแปรแบบ signed char หรือ char หรือ int8_t และใช้เก็บข้อมูลที่มีขนาด 8 บิต แสดงอยู่ในเลขฐานสองดังนี้

ค่าของ สามารถคำนวณได้ตามสูตรต่อไปนี้

บิตที่ 7 หรือ หรือ Most-Significant Bit (MSB) ของข้อมูลขนาด 8 บิต จะทำหน้าที่เป็น "บิตเครื่องหมาย" (Sign Bit) ถ้ามีค่าเป็น 1 หมายความว่า จะมีค่าเป็นลบ

แต่ถ้าให้ เป็นตัวแปรแบบ unsigned char หรือ uint8_t ค่าของ สามารถคำนวณได้ตามสูตรต่อไปนี้

ตัวอย่าง: ให้ มีค่าในเลขฐานสองเท่ากับ และมีชนิดข้อมูลเป็น unsigned (8 บิต) ดังนั้นจะมีเท่ากับ ในเลขฐานสิบ

ถ้าต้องการหาค่าเป็นลบของ ซึ่งก็คือ สามารถคำนวณได้จากการหา 1's Complment หรือการกลับค่าบิตทุกตำแหน่ง แล้วบวกด้วยหนึ่ง การคำนวณตามวิธีการนี้เรียกว่า 2's Complement

ถ้าเป็นชนิดข้อมูลเลขจำนวนเต็มขนาด 16, 32 และ 64 บิต แบบ signed ก็ใช้วิธีการคำนวณเหมือนกัน

 


โค้ดตัวอย่างในภาษาซี: แสดงค่าต่ำสุดและสูงสุดของชนิดข้อมูลที่เป็นเลขจำนวนเต็ม

#include <stdio.h>
#include <limits.h>

int main() {
  printf( "The number of bits in a byte %d\n", CHAR_BIT );
  printf( "The min. value of CHAR = %+d\n", CHAR_MIN );
  printf( "The max. value of CHAR = %+d\n", CHAR_MAX );

  printf("The min. value of SIGNED CHAR = %+d\n", SCHAR_MIN );
  printf( "The max. value of SIGNED CHAR = %+d\n", SCHAR_MAX );
  printf( "The max. value of UNSIGNED CHAR = %+d\n", UCHAR_MAX);

  printf( "The min. value of SHORT INT = %+d\n", SHRT_MIN );
  printf( "The max. value of SHORT INT = %+d\n", SHRT_MAX );

  printf( "The min. value of INT = %+d\n", INT_MIN );
  printf( "The max. value of INT = %+d\n", INT_MAX );
  printf( "The max. value of UNSIGNED INT = %u\n", UINT_MAX );

  printf( "The min. value of LONG = %+ld\n", LONG_MIN );
  printf( "The max. value of LONG = %+ld\n", LONG_MAX );
  printf( "The max. value of UNSIGNED LONG = %lu\n",  ULONG_MAX );

  printf( "The min. value of LONG LONG = %+lld\n", LLONG_MIN );
  printf( "The max. value of LONG LONG = %+lld\n", LLONG_MAX );

  printf( "The max. value of UNSIGNED LONG = %lu\n", ULONG_MAX );
  printf( "The max. value of UNSIGNED LONG LONG = %llu\n", ULLONG_MAX );

  return 0;
}

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

#include <stdio.h>
#include <float.h>

int main() {
  printf( "Max. value of type:\n" );
  printf( "     float  : %e\n",    FLT_MAX );
  printf( "     double : %e\n",    DBL_MAX );
  printf( "long double : %Le\n\n", LDBL_MAX );

  printf( "The closest absolute value to 0 of type:\n" );
  printf( "      float : %e\n",  FLT_MIN );
  printf( "     double : %e\n",  DBL_MIN );
  printf( "long double : %Le\n", LDBL_MIN );
  return 0;
}

 

การแสดงขนาด (จำนวนไบต์ที่ใช้) ของชนิดข้อมูลในภาษา C จะใช้คำสั่ง sizeof() ซึ่งจะให้ค่าเป็นเลขจำนวนเต็มเป็นแบบ size_t (มีการประกาศไว้ในไฟล์ <stddef.h>) ซึ่งหมายถึง unsigned long int ที่มีขนาด 32 บิต แล้วนำไปแสดงค่าด้วยคำสั่ง printf() และจะต้องใช้ %lu เป็น Format Specifier เพื่อแสดงข้อความสำหรับข้อมูลที่เป็น unsigned long

โค้ดตัวอย่าง: แสดงขนาดของชนิดข้อมูลในภาษาซี

#include <stdio.h>

int main(void) {
  // show the sizes (in bytes) of primitive data types in C
  printf( "sizeof(char)        : %lu\n", sizeof(char)       );  // 1
  printf( "sizeof(short)       : %lu\n", sizeof(short)      );  // 2
  printf( "sizeof(int)         : %lu\n", sizeof(int)        );  // 4
  printf( "sizeof(long)        : %lu\n", sizeof(long)       );  // 8
  printf( "sizeof(long long)   : %lu\n", sizeof(long long)  );  // 8
  printf( "sizeof(float)       : %lu\n", sizeof(float)      );  // 4
  printf( "sizeof(double)      : %lu\n", sizeof(double)     );  // 8
  printf( "sizeof(long double) : %lu\n", sizeof(long double));  // 16
  return 0;
}

ขนาดของข้อมูลชนิด ถ้าคอมไพล์โค้ดตัวอย่างโดยใช้คำสั่ง gcc สำหรับ 64-bit Linux จะได้ผลลัพธ์ เป็นไปตามตารางต่อไปนี้

Type Size (bytes) Format Specifier for printf()
char 1 %c
unsigned char 1 %u
short 2 %hd
unsigned short 2 %hu
int, signed int 4 %d, %i
unsigned int 4 %u
long, signed long 8 %ld, %li
unsigned long 8 %lu
long long, signed long long 8 %lld, %lli
unsigned long long 8 %llu
float 4 %f, %e, %g
double 8 %lf, %e, %g
long double 16 %Lf, %Le, %Lg

 

โค้ดตัวอย่างในภาษาซีสำหรับแสดงขนาดของข้อมูลค่าคงที่

#include <stdio.h>

int main(void) {
  // show the sizes (in bytes) of literals in C
  printf( "sizeof((char)'.')\t: %lu\n", sizeof((char)'.') );  // 1
  printf( "sizeof('.')    \t\t: %lu\n", sizeof('.') );        // 4
  printf( "sizeof(\"\")   \t\t: %lu\n", sizeof("") );         // 1
  printf( "sizeof(1UL)    \t\t: %lu\n", sizeof(1UL) );        // 8
  printf( "sizeof(1LL)    \t\t: %lu\n", sizeof(1LL) );        // 8
  printf( "sizeof(1ULL)   \t\t: %lu\n", sizeof(1ULL) );       // 8
  printf( "sizeof(1.e0f)  \t\t: %lu\n", sizeof(1.e0f) );      // 4
  printf( "sizeof(1.e0)   \t\t: %lu\n", sizeof(1.e0 ) );      // 8
  printf( "sizeof(1.e0l)  \t\t: %lu\n", sizeof(1.e0l) );      // 16
  return 0;
}

 


การแปลงชนิดข้อมูล (Data Type Conversion)#

ในโปรแกรมภาษาซี ตัวแปรจะต้องมีชนิดข้อมูลที่เกี่ยวข้องเมื่อประกาศใช้และเอาไว้เก็บข้อมูล และการอ่านค่าจากตัวแปรหรือค่าของนิพจน์เพื่อนำไปใช้กับตัวแปรใด ๆ จะต้องตรงกับชนิดข้อมูลของตัวแปรนั้น แต่ถ้าไม่ตรงกัน ก็จะต้องมีการแปลงชนิดข้อมูลก่อน ซึ่งแบ่งเป็นสองกรณี คือ การแปลงโดยอัตโนมัติ (Implicit Data Type Conversion) และการแปลงแบบเจาะจง (Type Casting)

การแปลงชนิดของข้อมูลยังแบ่งได้อีกเป็นสามกรณีคือ

  1. การแปลงระหว่างชนิดข้อมูลที่มีขนาดเท่ากันแต่ต่างชนิดกัน (ใช้จำนวนไบต์เท่ากัน)
  2. การแปลงระหว่างชนิดข้อมูลที่มีขนาดมากขึ้น (Widening Cast) ใช้จำนวนไบต์ในการเก็บข้อมูลได้มากขึ้น
  3. การแปลงระหว่างชนิดข้อมูลที่มีขนาดลดลง (Narrowing Cast) ใช้จำนวนไบต์ในการเก็บข้อมูลได้น้อยลง ซึ่งอาจทำให้ค่าของข้อมูลใหม่ที่ได้จากการแปลงไม่เหมือนเดิม

การแปลงแบบอัตโนมัติ จะเกิดขึ้นได้เช่น

  • เมื่อใช้ตัวดำเนินการกับข้อมูลที่ต่างชนิดกัน ข้อมูลที่มีขนาดเล็กกว่า จะถูกแปลงให้เป็นข้อมูลที่มีชนิดข้อมูลที่มีขนาดใหญ่กว่า แล้วนำไปใช้กับตัวดำเนินการ
  • ถ้าตัวดำเนินการใช้กับ unsigned int และ int จะต้องมีการแปลงจาก int ให้เป็น unsigned int ก่อน และผลลัพธ์ของตัวดำเนินการที่ได้ก็จะเป็น unsigned int เช่นกัน

การแปลงแบบเจาะจง โดยการเขียนชื่อชนิดข้อมูลในวงเล็บไว้ด้านหน้าของนิพจน์ จะทำให้คอมไพเลอร์แปลงชนิดข้อมูลตามที่ระบุ เช่น การแปลงจากข้อมูลแบบ float ให้เป็น int (ปัดเศษหลังจุดทศนิยมทิ้งไป) หรือการแปลงข้อมูลจาก int ให้เป็น unsigned char (ลดขนาดข้อมูลจาก 4 ไบต์ เหลือ 1 ไบต์)

โค้ดตัวอย่าง:

#include <stdio.h>

int main() {
  int x;
  unsigned char y;
  x = (int) 257.55;      // convert from float to int
  y = (unsigned char) x; // convert from int to unsigned char
  printf( "x=%d (dec), y=%u (dec)\n", x, y );
  return 0;
}

ข้อความเอาต์พุต:

x=257 (dec), y=1 (dec)

257.55 จะถูกแปลงให้เป็น int ซึ่งเป็นเลขจำนวนเต็ม ดังนั้นจึงตัดเศษหลังจุดทศนิยมออก ได้ค่าเป็น 257 แล้วนำไปกำหนดค่าให้ตัวแปร x

ในคำสั่งบรรทัดถัดไป ค่าของตัวแปร x จะถูกแปลงให้เป็น unsigned char ซึ่งมีขนาดข้อมูลเพียงหนึ่งไบต์ และมีค่าอยู่ในช่วง 0..255 แต่ค่าของตัวแปร x มีค่า 257 ดังนั้นจึงตัดบิตที่เกินขนาดออกไป เหลือค่าเป็น 1 แล้วนำไปใช้เป็นค่าของตัวแปร y


โค้ดตัวอย่าง:

#include <stdio.h>

int main() {
  short x = -1;
  unsigned short y = 1;
  y = x - y; // 0xffff - 0x0001 = 0xfffe or 65534
  printf( "y=%u (dec), 0x%x (hex)\n", y, y );
  return  0;
}

ข้อความเอาต์พุต:

y=65534 (dec), 0xfffe (hex)

ตัวแปร x มีชนิดข้อมูลเป็น short ขนาด 16 บิต หรือ 2 ไบต์ เก็บค่าได้ในช่วง -32768..+32767 ในขณะที่ตัวแปร y มีชนิดข้อมูลเป็น unsigned short ขนาด 16 บิต หรือ 2 ไบต์ แต่เก็บค่าได้ในช่วง 0..+65535

ในการคำนวณค่าของนิพจน์ x - y จะมีการแปลงชนิดข้อมูลของค่าที่ได้จากตัวแปร x โดยแปลงจาก short (มีค่าเป็น -1) ให้เป็น unsigned short ซึ่งจะได้ค่าเป็น 65535 หรือ 0xffff ในเลขฐานสิบหก แล้วนำไปลบด้วยค่าของตัวแปร y (มีค่าเป็น 1) ผลลัพธ์ที่ได้ซึ่งนำไปกำหนดค่าใหม่ให้ตัวแปร y จึงมีค่าเป็น 65534 หรือ 0xfffe ในเลขฐานสิบหก


โค้ดตัวอย่าง:

#include <stdio.h>

int main( ) {
  char x = 127;
  char y = 1;
  unsigned char z = 255;
  int expr1, expr2;
  expr1 = x + y;
  expr2 = x + z;
  printf( "x=%d, y=%d, z=%d\n", x, y, z );
  printf( "sizeof(x + y) = %lu bytes\n", sizeof(x + y) );
  printf( "sizeof(x + z) = %lu bytes\n", sizeof(x + z) );
  printf( "expr1 = %d\n", expr1 );
  printf( "expr2 = %d\n", expr2 );
  return 0;
}

ข้อความเอาต์พุต:

x=127, y=1, z=255
sizeof(x + y) = 4 bytes
sizeof(x + z) = 4 bytes
expr1 = 128
expr2 = 382

ตัวแปร expr1 และ expr2 มีชนิดข้อมูลเป็น int (ขนาด 4 ไบต์) และมีการกำหนดค่าให้ตัวแปรทั้งสอง โดยใช้ค่าจากนิพจน์ x+y และ x+z ตามลำดับ ตัวแปร x และ y มีชนิดข้อมูลเป็น char และตัวแปร z มีขนิดข้อมูลเป็น unsigned char

ค่าของนิพจน์ x + y ได้เท่ากับ 128 ซึ่งอยู่นอกช่วง -128 .. +127 สำหรับชนิดข้อมูล char แต่เนื่องจากได้มีการแปลงชนิดข้อมูลของตัวแปร ทั้งสองให้เป็น int ก่อน แล้วจึงนำค่าของตัวแปร x (มีค่าเท่ากับ 127) มาบวกกับ y (มีค่าเท่ากับ 1) และผลลัพธ์จึงได้เป็น int แล้วนำไปใช้กับตัวแปรที่อยู่ทางซ้ายมือของโอเปอเรเตอร์ = (กำหนดค่าให้ตัวแปร)

ค่าของนิพจน์ x + z ได้เท่ากับ 382 ซึ่งคำนวณได้จากการนำค่าของตัวแปร x (มีค่าเท่ากับ 127) มาบวกกับค่าของตัวแปร y (มีค่าเท่ากับ 255) แต่แปลงให้เป็นชนิดข้อมูลแบบ int ก่อน


โค้ดตัวอย่าง:

#include <stdio.h>

int main( ) {
  unsigned char b1 = 0x81, b0 = 0x3a;
  unsigned short x = (b1 << 8) | b0;
  unsigned int   y =  (x << 8) | 0x01;
  printf( "sizeof( x ) = %lu bytes\n", sizeof( x ) );
  printf( "sizeof( (b1<<8) | b0 ) = %lu bytes\n", sizeof( (b1<<8) | b0 ) );
  printf( "x = 0x%x\n", x );
  printf( "y = 0x%x\n", y );
  return 0;
}

ตัวแปร x มีชนิดข้อมูลเป็น unsigned short มีขนาด 16 บิต และมีค่าเริ่มต้นได้จาก นิพจน์ (b1 << 8) | b0 ซึ่งใช้ค่าจากตัวแปร b1 และ b0 ที่มีชนิดข้อมูลเป็น char และในการคำนวณค่า จะมีการแปลงชนิดข้อมูลจาก unsigned char ให้เป็น unsigned int ก่อนการคำนวณ และเมื่อคำนวณผลลัพธ์ได้แล้วจะต้องแปลงให้เป็น unsigned short

ตัวแปร y มีชนิดข้อมูลเป็น unsigned int มีขนาด 32 บิต และมีค่าเริ่มต้นได้จากนิพจน์ (x << 8) | 0x01 และในการคำนวณค่า จะมีการแปลงชนิดข้อมูลจาก unsigned short ให้เป็น unsigned int ก่อนการคำนวณ แล้วนำผลลัพธ์ไปใช้กับตัวแปร y

ข้อความเอาต์พุต:

sizeof(x) = 2 bytes
sizeof(b1<<8 | b0) = 4 bytes
x = 0x813a
y = 0x813a01

 


ข้อมูลเชิงประกอบ (Compound Data Types)#

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

  • ชนิดข้อมูลที่เป็นอาร์เรย์ซึ่งเป็นกลุ่มข้อมูลชนิดเดียวกัน มีการจัดเรียงและเข้าถึงตามลำดับที่อยู่ แบ่งอาร์เรย์ได้เป็นแบบมิติเดียว สองมิติ หรือมากกว่านั้น เช่น
    • int numbers[] = {1,2,3}; เป็นการประกาศตัวแปรชื่อ numbers ที่เก็บข้อมูลที่เป็น int โดยมีการกำหนดค่าเริ่มต้นให้ข้อมูลภายในอาร์เรย์เป็น 1,2,3 ตามลำดับ ดังนั้นจึงเป็นการกำหนดจำนวนข้อมูลในอาร์เรย์ที่สามารถเก็บค่าได้
    • double m[3][3]; เป็นการประกาศตัวแปรชื่อ m ที่เป็นอาร์เรย์แบบสองมิติ (ขนาด 3x3) ดังนั้นจึงมีข้อมูลสมาชิกทั้งหมด 9 ตัว และมีชนิดข้อมูลเป็น double
    • char str[8]; เป็นการประกาศตัวแปรชื่อ x ที่มีขนาด 8 ไบต์ หรือ 8 ตัวอักขระ แต่ไม่มีการกำหนดค่าเริ่มต้นให้ตัวแปร
  • struct เป็นกลุ่มของตัวแปรที่สามารถมีการสร้างและเก็บไว้ข้อมูลไว้ภายในได้ "ข้อมูลภายในเรียกว่า "ข้อมูลสมาชิก" (Member Data) จะมีชนิดข้อมูลเหมือนกันทั้งหมด หรือแตกต่างกันได้ และนำไปใช้กับตัวแปรได้สำหรับการอ้างอิงกลุ่มข้อมูลดังกล่าว
  • การกำหนดขนาดหรือจำนวนบิตของข้อมูลสมาชิกภายใน struct ที่มีชนิดข้อมูลเป็นเลขจำนวนเต็ม (เรียกว่า Bit Fields)
  • union เป็นชนิดข้อมูลเหมือน struct แต่ข้อมูลสมาชิกภายใน union จะต้องแชร์การใช้หน่วยความจำร่วมกัน ซึ่งแตกต่างกับกรณีของ struct ที่ข้อมูลสมาชิกภายในมีหน่วยความจำแยกกัน
  • enum (Enumeration) เป็นกลุ่มของสัญลักษณ์ที่มีค่าคงที่ไม่ซ้ำกันและเป็นตัวเลขจำนวนเต็ม

ตัวอย่างการสร้างชนิดข้อมูล struct และการประกาศใช้ตัวแปร

struct point {  // (x,y) coordinate
  int x; 
  int y;
};

struct point p1, p2;

จากโค้ดตัวอย่างเป็นการสร้างชนิดข้อมูลสำหรับเก็บค่าพิกัดบนระนาบ (x,y) ที่เป็นเลขจำนวนเต็ม และมีการประกาศตัวแปร p1 และ p2 เพื่อใช้กับชนิดข้อมูลดังกล่าว

#include <stdio.h>
#include <math.h> // uses the math library

struct point {  // (x,y) coordinate
  int x;
  int y;
};

int main( ) {
  struct point p1 = { .x = 1, .y = -1 }, p2;
  float dist = sqrt(pow(p1.x,2) + pow(p1.y,2));
  printf( "distance of p1 from (0,0) = %f\n", dist );
  p2 = p1; // copy operator
  printf( "p2 = (%d,%d)\n", p2.x, p2.y );
  return 0;
}

จากโค้ดตัวอย่าง ตัวแปร p1 มีขนิดข้อมูลเป็น struct point และมีการกำหนดค่าเริ่มต้นสำหรับข้อมูลสมาชิก .x และ .y ให้เป็น 1 และ -1 ตามลำดับ

ตัวแปร dist มีชนิดข้อมูลแบบ float และเก็บค่าที่คำนวณได้จากการนำค่าของ p1.x และ p1.y แต่ละตัวมายกกำลังสองโดยใช้คำสั่ง pow() แล้วจึงบวกกัน ผลบวกที่ได้นำไปหาค่าของรากที่สอง หรือ Square Root โดยใช้คำสั่ง sqrt() ที่มีการประกาศไว้ใน <math.h>

p2 = p1; เป็นประโยคคำสั่งที่นำค่าของตัวแปร p1 ไปกำหนดค่าให้ข้อมูลสมาชิกของตัวแปร p2 ที่มีชนิดข้อมูลเดียวกัน

 

อีกตัวอย่างหนึ่งเป็นชนิดข้อมูลสำหรับเก็บค่าของเลขจำนวนเชิงซ้อน (Complex Number) ที่ประกอบด้วยส่วนที่เป็นจำนวนจริง และจำนวนจินตภาพ (มีข้อมูลสมาชิกชื่อ re และ im ตามลำดับ) และมีการประกาศตัวแปร x และ y ที่มีชนิดข้อมูลเป็น struct complex ด้วยเช่นกัน

struct complex { 
  double re;  // real part
  double im;  // imaginary part
} x, y;

struct complex z = { 
  .re = 0.0, 
  .im = 1.0 
};

ประโยคคำสั่งถัดมา เป็นการประกาศตัวแปร z ที่มีชนิดข้อมูลเป็น struct complex และมีการกำหนดค่าเริ่มต้นให้ตัวแปร โดยเขียนให้อยู่ภายใน { ... } เช่น ให้ตัวแปร z มีค่าเป็น โดยที่ หมายถึง ในตัวอย่างนี้จะเห็นได้ว่ามีการใช้สัญลักษณ์ . แล้วตามตัวชื่อข้อมูลสมาชิกภายในของตัวแปร เช่น .re หรือ .im ซึ่งเป็นวิธีการเข้าถึงข้อมูลสมาชิกดังกล่าว สำหรับการอ่านหรือเขียนค่า

 

ข้อสังเกต: C99 ได้มีการประกาศชนิดข้อมูล เช่น float complex และ double complex ไว้ในไฟล์ <complex.h> รวมถึงสัญลักษณ์ I (มีชนิดข้อมูลเป็น float complex) ซึ่งหมายถึง และฟังก์ชันที่เกี่ยวข้อง เช่น creal(), cimag(), cabs(), carg() ดังนั้นหากต้องการคำนวณเลขเชิงซ้อน ก็สามารถนำมาใช้งานได้

 


โค้ดตัวอย่าง:

// compile: gcc -Wall main.c -o ./main -lm
#include <complex.h>
#include <math.h>
#include <stdio.h>

int main() {
  float complex z = 1 + 1*I;  // 1+i1
  float complex conj_z = creal(z) - I*cimag(z); // conjugate of z or conj(z)
  printf( "z  = %.1f %+.1fI\n", creal(z), cimag(z) );
  printf( "z* = %.1f %+.1fI\n", creal(conj_z), cimag(conj_z) );
  printf( "|z| = %.3f\n", cabs(z) );
  printf( "arg(z) = %.1f deg.\n", carg(z)*180/M_PI );
  return  0;
}

ข้อความเอาต์พุต

z  = 1.0 +1.0I
z* = 1.0 -1.0I
|z| = 1.414
arg(z) = 45.0 deg.

 


ตัวอย่างการสร้างชนิดข้อมูล union และการประกาศใช้ตัวแปร เช่น

union data_unit { 
  unsigned int  value;
  unsigned char bytes[4];
};

data_unit เป็นชื่อของชนิดข้อมูลแบบ union ได้ถูกกำหนดขึ้นมา โดยมีสมาชิกแบ่งเป็นสองกรณีในการใช้งานคือ value ซึ่งมีชนิดข้อมูลแบบ unsigned int (มีขนาด 4 ไบต์) และ bytes ซึ่งเป็นอาร์เรย์สำหรับชนิดข้อมูล unsigned char ที่มีขนาด 4 ไบต์ และการเข้าถึงข้อมูลในอาร์เรย์ จะต้องระบุตำแหน่งของข้อมูลสมาชิกตัวนั้นในอาร์เรย์ ซึ่งเป็นเลขจำนวนเต็ม เริ่มต้นนับที่ 0

สมาชิกทั้งสองตัว จะแชร์กันใช้หน่วยความจำ และในกรณีนี้คือ มีขนาด 4 ไบต์ หรือกล่าวได้ว่า หากเข้าถึงข้อมูล value ของ data_unit จะใช้ข้อมูลแบบ unsigned int แต่ถ้าเข้าถึงข้อมูล bytes จะเป็นข้อมูลไบต์ในอาร์เรย์ขนาด 4 ไบต์ ในหน่วยความจำเดียวกัน

ในภาษาซี สามารถใช้คำสั่ง typedef เพื่อประกาศใช้หรือตั้งชื่อชนิดข้อมูลใหม่ได้ เช่น

  • typedef struct complex { double re, im; } complex_t;
  • typedef unsigned int uint32_t;
  • typedef enum { FALSE=0; TRUE=1; } bool_t;

 


โค้ดตัวอย่าง: การสร้างชนิดข้อมูล struct ที่มีการกำหนดขนาดบิตให้ข้อมูลสมาชิกภายใน และชนิดข้อมูล union ที่มี struct ซ้อนอยู่ภายใน

#include <stdio.h>
#include <inttypes.h>

typedef struct bitfields {
  uint8_t clk_div  : 2;
  uint8_t mode     : 2;
  uint8_t reserved : 3;
  uint8_t enable   : 1;
} bitfields_t;

typedef union {
  bitfields_t bits;
  uint8_t     value;
} config_t;

int main( ) {
  config_t config = {
    //.value = 0b10001101,
    .bits = {
      .clk_div  = 0b01,  // 1
      .mode     = 0b11,  // 3
      .reserved = 0b000, // 0
      .enable   = 0b1    // 1
    }
  };
  printf( "sizeof(bitfields_t) = %lu byte\n", sizeof(bitfields_t) );
  printf( "sizeof(config_t)    = %lu byte\n", sizeof(config_t) );
  printf( "clk_div = %u\n", config.bits.clk_div );
  printf( "mode    = %u\n", config.bits.mode    );
  printf( "enable  = %u\n", config.bits.enable  );
  printf( "value   = 0x%02x\n", config.value    );
  return 0;
}

ในโค้ดตัวอย่าง มีการประกาศชนิดข้อมูล struct bitfields หรือ bitfields_t ซึ่งภายในประกอบด้วยข้อมูลสมาชิกแบบ Bit Fields และมีขนาดทั้งหมดเท่ากับ 8 บิต หรือ 1 ไบต์ แบ่งเป็น

  • clk_div มีขนาด 2 บิต
  • mode มีขนาด 2 บิต
  • reserved มีขนาด 3 บิต
  • enable มีขนาด 1 บิต (มีค่าเป็น 0 หรือ 1 ได้เท่านั้น)

นอกจากนั้นยังมีการประกาศ config_t ซึ่งเป็น union โดยมีข้อมูลสมาชิกเป็น bits (ชนิดข้อมูล bitfields_t) และ value (ชนิดข้อมูล uint8_t) ซึ่งจะต้องแชร์หน่วยความจำร่วมกันขนาดหนึ่งไบต์

ข้อความเอาต์พุตจากโปรแกรม

sizeof(bitfields_t) = 1 byte
sizeof(config_t)    = 1 byte
clk_div = 1
mode    = 3
enable  = 1
value   = 0x8d

 


โค้ดตัวอย่าง: การสร้างชนิดข้อมูลด้วย union และการใช้งานร่วมกับตัวแปรชื่อ data โดยมีการกำหนดค่าเริ่มต้นของข้อมูลสมาชิกชื่อ value ให้เท่ากับ 0x778899aa จากนั้นเป็นการลองอ่านค่าของข้อมูลสมาชิก โดยใช้วิธีเข้าถึงผ่านสมาชิกชื่อ bytes เพื่ออ่านข้อมูลไบต์แต่ละตัว

#include <stdio.h>

typedef union data_unit {
  unsigned int  value;
  unsigned char bytes[4];
} data_unit_t;

int main( ) {
  data_unit_t data = { .value = 0x778899aa };

  printf( "sizeof(data) = %lu bytes\n", sizeof(data) );
  printf( "data.value = 0x%08x (hex)\n", data.value );
  for ( int i=0; i < 4; i++ ) {
    printf( "data.bytes[%d] = 0x%02x\n", i, data.bytes[i] );
  }
  return 0;
}

ข้อความเอาต์พุตของโปรแกรมตัวอย่าง มีดังนี้

sizeof(data) = 4 bytes
data.value = 0x778899aa (hex)
data.bytes[0] = 0xaa
data.bytes[1] = 0x99
data.bytes[2] = 0x88
data.bytes[3] = 0x77

 


อาร์เรย์และตัวอย่างการใช้งานในเบื้องต้น#

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

#include <stdio.h>

int main( ) {
  int data [] = { 5, 10, 20 };
  size_t elements_count = sizeof(data)/sizeof(int);
  printf( "Number of data in array: %lu\n", elements_count );
  printf( "read data[0] : %d\n", data[0] );
  printf( "read data[1] : %d\n", data[1] );
  printf( "read data[2] : %d\n", data[2] );
  return 0;
}

โค้ดตัวอย่างนี้สาธิตการประกาศตัวแปรชื่อ data ภายในฟังก์ชัน และใช้กับอาร์เรย์ที่มีชนิดข้อมูล int มีการกำหนดค่าสำหรับสมาชิกข้อมูลในอาร์เรย์เป็น { 5, 10, 20 } ดังนั้นขนาดของอาร์เรย์จึงถูกกำหนดไว้ให้เท่ากับ 3 ซึ่งหมายถึง เก็บข้อมูลได้ 3 ตำแหน่ง อ้างอิงด้วยหมายเลขของข้อมูลและเป็นเลขจำนวนเต็ม เริ่มต้นที่ 0 ไปตามลำดับ

ตัวอย่างข้อความเอาต์พุตจากโปรแกรม

Number of data in array: 3
read data[0] : 5
read data[1] : 10
read data[2] : 20

 


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

#include <stdio.h>

int main( ) {
  int data [3]; // an array of 3 integers, but uninitialized 
  // assign values to array's elements
  data[0] = 5;  // assign a value to the first element of data
  data[1] = 10; // assign a value to the second element of data
  data[2] = 20; // assign a value to the third element of data
  size_t elements_count = sizeof(data)/sizeof(int);
  printf( "Number of data in array: %lu\n", elements_count );
  // read elements of the arrary
  printf( "read data[0] : %d\n", data[0] );
  printf( "read data[1] : %d\n", data[1] );
  printf( "read data[2] : %d\n", data[2] );
  return 0;
}

 


โค้ดตัวอย่างในภาษาซี: การใช้งานตัวแปรชื่อ matrix ที่มีชนิดข้อมูลเป็นอาร์เรย์สองมิติสำหรับ float มีขนาดหรือมิติเท่ากับ 2x3 พร้อมข้อมูลสมาชิกเริ่มต้น แถวแรกคือ {-1.,2.,-3} และแถวที่สองคือ {1.,0.,-2} การเข้าถึงข้อมูลของอาร์เรย์ จะใช้ชื่อตัวแปรตามด้วยตัวดำเนินการ [ ][ ] และระบุหมายเลขข้อมูลในแต่ละมิติ เช่น matrix[0][0] หมายถึง ข้อมูลตัวแรกในอาร์เรย์

int main( ) {
  float matrix [2][3] = { {-1.,2.,-3}, {1.,0.,-2} };
  printf( "read element [0,0] : %f\n", matrix[0][0] );
  printf( "read element [1,0] : %f\n", matrix[1][0] );
  printf( "read element [1,3] : %f\n", matrix[1][3] );
  return 0;
}

 


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

#include <stdio.h>
#include <string.h> // defines strlen() and strcpy()

int main( ) {
  char s1[]   = { 'H', 'e', 'l', 'l', 'o', '\0' };
  char s2[]   = "Hello";
  char s3[10];

  strcpy( s3, s2 ); // copy the data of s2 to s3
  printf( "s1 = \"%s\"\n", s1 );
  printf( "s2 = \"%s\"\n", s2 );
  printf( "s3 = \"%s\"\n", s3 );
  printf( "sizeof(s1) = %lu bytes\n", sizeof(s1) );
  printf( "sizeof(s2) = %lu bytes\n", sizeof(s2) );
  printf( "sizeof(s3) = %lu bytes\n", sizeof(s3) );
  printf( "String length of s1 = %lu chars\n", strlen(s1) );
  printf( "String length of s2 = %lu chars\n", strlen(s2) );
  printf( "String length of s3 = %lu chars\n", strlen(s3) );
  printf( "s2[5] = %d (dec)\n", s2[ strlen(s2) ] );
  return 0;
}

จากโค้ดตัวอย่าง มีคำอธิบายดังนี้

  • ตัวแปรทั้งสาม (s1 s2 และ s3) เป็นตัวแปรที่ใช้กับอาร์เรย์ของข้อมูลแบบ char
  • ตัวแปร s1 และ s2 มีรูปแบบการกำหนดค่าเริ่มต้นให้อาร์เรย์ไม่เหมือนกัน แต่เป็นข้อความที่เหมือนกันคือ "Hello" (อาร์เรย์ของแต่ละตัวแปรใช้พื้นที่ในหน่วยความจำแยกกัน)
  • ตัวแปร s3 ไม่มีค่าเริ่มต้นเมื่อประกาศใช้ตัวแปร แต่มีการกำหนดขนาดให้เป็น 10 ไบต์ (ตัวอักขระ) และถือว่าเป็น "ข้อความว่างเปล่า" (Empty String) ถัดไปมีการเรียกใช้ฟังก์ชัน strcpy() เพื่อทำสำเนาข้อความ (String Copy) จากอาร์เรย์ของ s2 ไปใส่ในอาร์เรย์ของ s3
  • มีการใช้ฟังก์ชัน strlen() เพื่อตรวจสอบความยาวของข้อความในภาษาซี (C String) ที่เก็บอยู่ในอาร์เรย์ของตัวแปรแต่ละตัว ซึ่งจะได้เท่ากับ 5 (มี 5 ตัวอักขระ) เหมือนกัน
  • ตัวแปร s1 และ s2 มีขนาดของอาร์เรย์เท่ากับ 6 ไบต์ เหมือนกัน แต่ตัวแปร s3 มีขนาดเท่ากับ 10 ไบต์

ข้อความในภาษาซี จะถูกเก็บในหน่วยความจำเหมือนกับอาร์เรย์ของ char และต้องมีข้อมูล \0 (มีค่าเท่ากับ 0) เป็นข้อมูลไบต์ปิดท้ายข้อความ (Null Termination) ในหน่วยความจำของอาร์เรย์

ตัวอย่างข้อความเอาต์พุตของโปรแกรม

s1 = "Hello"
s2 = "Hello"
s3 = "Hello"
sizeof(s1) = 6 bytes
sizeof(s2) = 6 bytes
sizeof(s3) = 10 bytes
String length of s1 = 5 chars
String length of s2 = 5 chars
String length of s3 = 5 chars
s2[5] = 0 (dec)

 


อ่านเนื้อหา: "การเขียนโปรแกรมภาษา C" (ตอนที่ 1) | (ตอนที่ 3)


This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Created: 2022-09-04 | Last Updated: 2022-11-13