การเขียนโปรแกรมภาษา 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)
การแปลงชนิดของข้อมูลยังแบ่งได้อีกเป็นสามกรณีคือ
- การแปลงระหว่างชนิดข้อมูลที่มีขนาดเท่ากันแต่ต่างชนิดกัน (ใช้จำนวนไบต์เท่ากัน)
- การแปลงระหว่างชนิดข้อมูลที่มีขนาดมากขึ้น (Widening Cast) ใช้จำนวนไบต์ในการเก็บข้อมูลได้มากขึ้น
- การแปลงระหว่างชนิดข้อมูลที่มีขนาดลดลง (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