C có đủ các kiểu số nguyên nhỏ, lớn hơn, và lớn nhất kiểu int, long và thế nọ thế kia. Và bạn có thể xem trong phần giới hạn để thấy int lớn nhất là gì với INT_MAX và tương tự.
Các kiểu đó to bao nhiêu? Tức là, chúng chiếm mấy byte? Ta có thể dùng sizeof để ra câu trả lời.
Nhưng nếu tôi muốn đi ngược lại thì sao? Nếu tôi cần kiểu chính xác 32 bit (4 byte) hoặc ít nhất 16 bit hay đại loại thế?
Làm sao khai báo kiểu có kích thước nhất định?
Header <stdint.h> cho ta cách.
Với cả số nguyên có dấu và không dấu, ta có thể chỉ định kiểu có số bit nhất định, với vài cảnh báo, đương nhiên.
Và có ba nhóm chính các kiểu này (trong các ví dụ, N sẽ được thay bằng số bit cụ thể):
intN_t)int_leastN_t)int_fastN_t)189fast nhanh hơn bao nhiêu? Chắc chắn có lẽ nhanh hơn một lượng nào đó. Có thể. Spec không nói nhanh hơn bao nhiêu, chỉ nói chúng sẽ là nhanh nhất trên kiến trúc này. Tuy nhiên, đa số compiler C khá tốt, nên bạn chắc sẽ chỉ thấy cái này được dùng ở chỗ cần đảm bảo tốc độ tối đa có thể (chứ không chỉ là hy vọng compiler xuất ra code đủ-nhanh-phết, mà nó đang làm vậy).
Cuối cùng, các kiểu số không dấu này có thêm chữ u ở đầu để phân biệt.
Ví dụ, các kiểu này có nghĩa tương ứng được liệt kê:
int32_t w; // w is exactly 32 bits, signed
uint16_t x; // x is exactly 16 bits, unsigned
int_least8_t y; // y is at least 8 bits, signed
uint_fast64_t z; // z is the fastest representation at least 64 bits, unsignedCác kiểu sau được đảm bảo được định nghĩa:
int_least8_t uint_least8_t
int_least16_t uint_least16_t
int_least32_t uint_least32_t
int_least64_t uint_least64_t
int_fast8_t uint_fast8_t
int_fast16_t uint_fast16_t
int_fast32_t uint_fast32_t
int_fast64_t uint_fast64_tCó thể có các kiểu khác với bề rộng khác, nhưng chúng là tuỳ chọn.
Ê! Các kiểu cố định kiểu int16_t đâu? Hoá ra chúng hoàn toàn tuỳ chọn… trừ khi có điều kiện nhất định được thoả190. Và nếu bạn có hệ máy tính hiện đại trung bình bình thường, các điều kiện đó khả năng cao được thoả. Và nếu thoả, bạn sẽ có các kiểu này:
int8_t uint8_t
int16_t uint16_t
int32_t uint32_t
int64_t uint64_tCác biến thể khác với bề rộng khác có thể được định nghĩa, nhưng chúng tuỳ chọn.
Có kiểu bạn có thể dùng giữ số nguyên biểu diễn được lớn nhất có sẵn trên hệ, cả có dấu và không dấu:
intmax_t
uintmax_t
Dùng các kiểu này khi bạn muốn đi to hết mức.
Rõ ràng là giá trị từ bất kỳ kiểu số nguyên nào khác cùng dấu sẽ vừa vào kiểu này, đương nhiên.
Nếu bạn có một hằng mà bạn muốn nó vừa vào số bit nhất định, bạn có thể dùng các macro này để tự động thêm hậu tố đúng vào số (ví dụ 22L hay 3490ULL).
INT8_C(x) UINT8_C(x)
INT16_C(x) UINT16_C(x)
INT32_C(x) UINT32_C(x)
INT64_C(x) UINT64_C(x)
INTMAX_C(x) UINTMAX_C(x)
Lại nữa, mấy cái này chỉ chạy với giá trị số nguyên hằng.
Ví dụ, ta có thể dùng một trong số đó để gán giá trị hằng như vầy:
uint16_t x = UINT16_C(12);
intmax_t y = INTMAX_C(3490);
Ta cũng có một số giới hạn được định nghĩa để bạn lấy được giá trị lớn nhất và nhỏ nhất cho các kiểu này:
INT8_MAX INT8_MIN UINT8_MAX
INT16_MAX INT16_MIN UINT16_MAX
INT32_MAX INT32_MIN UINT32_MAX
INT64_MAX INT64_MIN UINT64_MAX
INT_LEAST8_MAX INT_LEAST8_MIN UINT_LEAST8_MAX
INT_LEAST16_MAX INT_LEAST16_MIN UINT_LEAST16_MAX
INT_LEAST32_MAX INT_LEAST32_MIN UINT_LEAST32_MAX
INT_LEAST64_MAX INT_LEAST64_MIN UINT_LEAST64_MAX
INT_FAST8_MAX INT_FAST8_MIN UINT_FAST8_MAX
INT_FAST16_MAX INT_FAST16_MIN UINT_FAST16_MAX
INT_FAST32_MAX INT_FAST32_MIN UINT_FAST32_MAX
INT_FAST64_MAX INT_FAST64_MIN UINT_FAST64_MAX
INTMAX_MAX INTMAX_MIN UINTMAX_MAX
Lưu ý MIN cho mọi kiểu không dấu là 0, nên như vậy, không có macro cho nó.
Để in các kiểu này, bạn cần truyền đúng format specifier cho printf(). (Và vấn đề tương tự khi lấy input với scanf().)
Nhưng làm sao bạn biết được kiểu to bao nhiêu dưới mui? May thay, lần nữa, C cung cấp vài macro để giúp chuyện này.
Tất cả chuyện này có thể thấy trong <inttypes.h>.
Giờ, ta có một đống macro. Kiểu một vụ nổ phức tạp của macro. Nên tôi sẽ thôi liệt ra từng cái và chỉ đặt chữ thường n ở chỗ mà bạn nên đặt 8, 16, 32, hay 64 tuỳ nhu cầu.
Nhìn qua các macro cho in số nguyên có dấu:
PRIdn PRIdLEASTn PRIdFASTn PRIdMAX
PRIin PRIiLEASTn PRIiFASTn PRIiMAXNhìn pattern ở đó. Bạn có thể thấy có biến thể cho kiểu fixed, least, fast, và max.
Và bạn cũng có chữ thường d và chữ thường i. Các chữ đó tương ứng với format specifier %d và %i của printf().
Nên nếu tôi có thứ kiểu:
int_least16_t x = 3490;Tôi có thể in cái đó với format specifier tương đương với %d bằng PRIdLEAST16.
Nhưng sao? Ta dùng macro đó sao?
Trước hết, macro đó chỉ định một chuỗi chứa chữ cái hay các chữ cái printf() cần dùng để in kiểu đó. Ví dụ, nó có thể là "d" hay "ld".
Nên tất cả ta cần làm là nhúng nó vào chuỗi format của lời gọi printf().
Để làm chuyện này, ta có thể tận dụng một chuyện về C bạn có thể đã quên: chuỗi literal kề nhau được nối tự động thành một chuỗi. Ví dụ:
printf("Hello, " "world!\n"); // Prints "Hello, world!"Và vì các macro này là chuỗi literal, ta có thể dùng chúng như vầy:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void)
{
int_least16_t x = 3490;
printf("The value is %" PRIdLEAST16 "!\n", x);
}
Ta cũng có một đống macro để in kiểu không dấu:
PRIon PRIoLEASTn PRIoFASTn PRIoMAX
PRIun PRIuLEASTn PRIuFASTn PRIuMAX
PRIxn PRIxLEASTn PRIxFASTn PRIxMAX
PRIXn PRIXLEASTn PRIXFASTn PRIXMAXTrong trường hợp này, o, u, x, và X tương ứng với các format specifier đã documented trong printf().
Và, như trước, chữ thường n nên được thay bằng 8, 16, 32, hay 64.
Nhưng ngay khi bạn tưởng mình đã chán macro, hoá ra ta có một bộ hoàn chỉnh đối ứng cho scanf()!
SCNdn SCNdLEASTn SCNdFASTn SCNdMAX
SCNin SCNiLEASTn SCNiFASTn SCNiMAX
SCNon SCNoLEASTn SCNoFASTn SCNoMAX
SCNun SCNuLEASTn SCNuFASTn SCNuMAX
SCNxn SCNxLEASTn SCNxFASTn SCNxMAX
Nhớ: khi bạn muốn in một kiểu số nguyên kích thước cố định bằng printf() hay scanf(), lấy format specifier tương ứng đúng từ <inttypes.h>.