Trong C / C ++, an unsigned char
dùng để làm gì? Làm thế nào nó khác với thường xuyên char
?
Trong C / C ++, an unsigned char
dùng để làm gì? Làm thế nào nó khác với thường xuyên char
?
Câu trả lời:
Trong C ++, có ba loại ký tự riêng biệt :
char
signed char
unsigned char
Nếu bạn đang sử dụng các loại ký tự cho văn bản , hãy sử dụng loại không đủ tiêu chuẩn char
:
'a'
hay '0'
."abcde"
Nó cũng hoạt động như một giá trị số, nhưng không xác định được giá trị đó được coi là đã ký hay chưa ký. Coi chừng so sánh các ký tự thông qua sự bất bình đẳng - mặc dù nếu bạn giới hạn bản thân ở ASCII (0-127) thì bạn chỉ an toàn.
Nếu bạn đang sử dụng các loại ký tự làm số , hãy sử dụng:
signed char
, cung cấp cho bạn ít nhất phạm vi -127 đến 127. (-128 đến 127 là phổ biến)unsigned char
, cung cấp cho bạn ít nhất phạm vi từ 0 đến 255."Ít nhất", bởi vì tiêu chuẩn C ++ chỉ đưa ra phạm vi giá trị tối thiểu mà mỗi loại số được yêu cầu để che. sizeof (char)
được yêu cầu là 1 (tức là một byte), nhưng về lý thuyết, một byte có thể là 32 bit. sizeof
vẫn sẽ được báo cáo kích thước của nó là1
- có nghĩa là bạn có thể có sizeof (char) == sizeof (long) == 1
.
sizeof
vì nó không phải là hàm mà là toán tử. Đó là phong cách thậm chí tốt hơn để bỏ qua dấu ngoặc đơn khi lấy kích thước của một biến. sizeof *p
hoặc sizeof (int)
. Điều này làm cho nó rõ ràng nhanh chóng nếu nó áp dụng cho một loại hoặc biến. Tương tự như vậy, nó cũng là dư thừa để đặt dấu ngoặc sau return
. Đây không phải là một chức năng.
char
: đó là loại chữ nhân vật thích 'a'
hay '0'
." đúng trong C ++ nhưng không phải C. Trong C, 'a'
là một int
.
Điều này phụ thuộc vào việc triển khai, vì tiêu chuẩn C KHÔNG xác định mức độ đã ký của char
. Tùy thuộc vào nền tảng, char có thể signed
hoặc unsigned
, vì vậy bạn cần yêu cầu rõ ràng signed char
hoặc unsigned char
nếu việc triển khai của bạn phụ thuộc vào nó. Chỉ sử dụng char
nếu bạn có ý định đại diện cho các ký tự từ các chuỗi, vì điều này sẽ khớp với những gì nền tảng của bạn đặt trong chuỗi.
Sự khác biệt giữa signed char
và unsigned char
là như bạn mong đợi. Trên hầu hết các nền tảng, signed char
sẽ là một 8-bit số bù hai của từ -128
để 127
, và unsigned char
sẽ là một-cắn 8 unsigned integer ( 0
để 255
). Lưu ý rằng tiêu chuẩn KHÔNG yêu cầu các char
loại có 8 bit, chỉ sizeof(char)
trả về 1
. Bạn có thể lấy tại số bit trong một char với CHAR_BIT
trong limits.h
. Có rất ít nếu bất kỳ nền tảng nào ngày nay, nơi đây sẽ là một cái gì đó khác hơn 8
, mặc dù.
Có một bản tóm tắt tốt đẹp về vấn đề này ở đây .
Như những người khác đã đề cập kể từ khi tôi đăng bài này, bạn nên sử dụng int8_t
và uint8_t
nếu bạn thực sự muốn đại diện cho các số nguyên nhỏ.
CHAR_BIT
được yêu cầu tối thiểu 8 bit theo tiêu chuẩn.
Bởi vì tôi cảm thấy nó thực sự được yêu cầu, tôi chỉ muốn nêu ra một số quy tắc của C và C ++ (chúng giống nhau về vấn đề này). Thứ nhất, tất cả các bit của unsigned char
tham gia trong việc xác định giá trị nếu bất kỳ đối tượng unsigned char. Thứ hai, unsigned char
được tuyên bố rõ ràng không dấu.
Bây giờ, tôi đã thảo luận với ai đó về những gì xảy ra khi bạn chuyển đổi giá trị -1
của kiểu int thành unsigned char
. Anh ta từ chối ý tưởng rằng kết quả unsigned char
có tất cả các bit của nó được đặt thành 1, vì anh ta lo lắng về biểu diễn dấu hiệu. Nhưng anh không phải làm thế. Ngay lập tức tuân theo quy tắc này là chuyển đổi thực hiện những gì được dự định:
Nếu loại mới không được ký, giá trị được chuyển đổi bằng cách lặp lại hoặc trừ đi nhiều hơn một giá trị tối đa có thể được biểu thị trong loại mới cho đến khi giá trị nằm trong phạm vi của loại mới. (
6.3.1.3p2
trong bản nháp C99)
Đó là một mô tả toán học. C ++ mô tả nó theo tính toán modulo, điều này dẫn đến cùng một quy tắc. Dù sao, điều không được đảm bảo là tất cả các bit trong số nguyên -1
là một trước khi chuyển đổi. Vì vậy, chúng ta có gì để chúng ta có thể tuyên bố rằng kết quả unsigned char
có tất cả các CHAR_BIT
bit của nó được chuyển thành 1?
UCHAR_MAX+1
để -1
sẽ mang lại một giá trị trong phạm vi, cụ thể làUCHAR_MAX
Thế là đủ rồi! Vì vậy, bất cứ khi nào bạn muốn có một unsigned char
bit của nó, bạn làm
unsigned char c = (unsigned char)-1;
Nó cũng theo sau rằng một chuyển đổi không chỉ là cắt các bit thứ tự cao hơn. Sự kiện may mắn cho sự bổ sung của hai người là nó chỉ là một sự cắt ngắn ở đó, nhưng điều tương tự không nhất thiết đúng với các biểu diễn dấu hiệu khác.
UCHAR_MAX
?
(unsigned type)-1
là một loại thành ngữ. ~0
không phải.
int x = 1234
và char *y = &x
. Đại diện nhị phân 1234
là 00000000 00000000 00000100 11010010
. Máy của tôi là một endian nhỏ nên nó đảo ngược nó và lưu trữ trong bộ nhớ 11010010 00000100 00000000 00000000
LSB đến trước. Bây giờ phần chính. nếu tôi sử dụng printf("%d" , *p)
. printf
sẽ đọc byte đầu tiên 11010010
chỉ ra là -46
nhưng 11010010
là 210
vậy tại sao nó in -46
. Tôi thực sự bối rối, tôi đoán một số chương trình khuyến mãi số nguyên đang làm gì đó nhưng tôi không biết.
Ví dụ như tập quán của char không dấu :
unsigned char
thường được sử dụng trong đồ họa máy tính, mà rất thường xuyên (mặc dù không phải lúc nào) chỉ định một byte cho mỗi thành phần màu. Người ta thường thấy màu RGB (hoặc RGBA) được biểu thị là 24 (hoặc 32) bit, mỗi bit unsigned char
. Vì unsigned char
các giá trị nằm trong phạm vi [0,255], các giá trị thường được hiểu là:
Vì vậy, bạn sẽ kết thúc với màu đỏ RGB là (255,0,0) -> (đỏ 100%, xanh 0%, xanh 0%).
Tại sao không sử dụng a signed char
? Số học và bit dịch chuyển trở thành vấn đề. Như đã giải thích, signed char
phạm vi của về cơ bản được thay đổi bởi -128. Một phương pháp rất đơn giản và ngây thơ (hầu hết không được sử dụng) để chuyển đổi RGB sang thang độ xám là trung bình cả ba thành phần màu, nhưng điều này gặp vấn đề khi các giá trị của các thành phần màu là âm. Màu đỏ (255, 0, 0) trung bình đến (85, 85, 85) khi sử dụng unsigned char
số học. Tuy nhiên, nếu các giá trị là signed char
s (127, -128, -128), chúng tôi sẽ kết thúc bằng (-99, -99, -99), sẽ là (29, 29, 29) trong unsigned char
không gian của chúng tôi , không chính xác .
Nếu bạn muốn sử dụng một ký tự như một số nguyên nhỏ, cách an toàn nhất là sử dụng các kiểu int8_t
và uint8_t
.
int8_t
và uint8_t
là tùy chọn và không được xác định trên các kiến trúc trong đó kích thước byte không chính xác là 8 bit. Ngược lại, signed char
và unsigned char
luôn có sẵn và được đảm bảo giữ ít nhất 8 bit. Nó có thể là một cách phổ biến nhưng không phải là an toàn nhất .
signed char
và unsigned char
? Hoặc bạn muốn đề xuất một sự thay thế "an toàn" tốt hơn trong trường hợp cụ thể đó? Ví dụ để gắn bó với các loại số nguyên "thực" signed int
và unsigned int
thay vào đó vì một số lý do?
signed char
và unsigned char
có thể mang theo cho tất cả các triển khai tuân thủ và sẽ tiết kiệm không gian lưu trữ nhưng có thể làm tăng một số kích thước mã. Trong một số trường hợp, người ta sẽ tiết kiệm nhiều không gian lưu trữ hơn bằng cách lưu trữ các giá trị nhỏ trong bitfield hoặc các bit đơn của các loại số nguyên thông thường. Không có câu trả lời tuyệt đối cho câu hỏi này, sự liên quan của phương pháp này phụ thuộc vào trường hợp cụ thể trong tay. Và câu trả lời này không giải quyết câu hỏi nào.
char
và unsigned char
không được đảm bảo là loại 8 bit trên tất cả các nền tảng, chúng được đảm bảo là loại 8 bit hoặc lớn hơn. Một số nền tảng có byte 9 bit, 32 bit hoặc 64 bit . Tuy nhiên, các nền tảng phổ biến nhất hiện nay (Windows, Mac, Linux x86, v.v.) có byte 8 bit.
signed char
có phạm vi -128 đến 127; unsigned char
có phạm vi từ 0 đến 255.
char
sẽ tương đương với char đã ký hoặc char không dấu, tùy thuộc vào trình biên dịch, nhưng là một loại khác biệt.
Nếu bạn đang sử dụng chuỗi kiểu C, chỉ cần sử dụng char
. Nếu bạn cần sử dụng ký tự cho số học (khá hiếm), chỉ định rõ ràng đã ký hoặc không dấu cho tính di động.
An unsigned char
là một giá trị byte không dấu (0 đến 255). Bạn có thể nghĩ char
về việc trở thành một "nhân vật" nhưng nó thực sự là một giá trị số. Thông thường char
được ký, vì vậy bạn có 128 giá trị và các giá trị này ánh xạ tới các ký tự sử dụng mã hóa ASCII. Nhưng trong cả hai trường hợp, những gì bạn đang lưu trữ trong bộ nhớ là một giá trị byte.
Về mặt giá trị trực tiếp, char thông thường được sử dụng khi các giá trị được biết là nằm giữa CHAR_MIN
và CHAR_MAX
trong khi char không dấu cung cấp gấp đôi phạm vi ở đầu dương. Ví dụ: nếu CHAR_BIT
là 8, phạm vi thông thường char
chỉ được đảm bảo là [0, 127] (vì có thể được ký hoặc không dấu) trong khi unsigned char
sẽ là [0, 255] và signed char
sẽ là [-127, 127].
Về mặt sử dụng, các tiêu chuẩn cho phép các đối tượng của POD (dữ liệu cũ đơn giản) được chuyển đổi trực tiếp thành một mảng char không dấu. Điều này cho phép bạn kiểm tra biểu diễn và mẫu bit của đối tượng. Sự bảo đảm tương tự của loại picky an toàn không tồn tại đối với char hoặc char đã ký.
unsigned char
, không phải là một mảng cụ thể, và bất kỳ "chuyển đổi" chỉ chính thức được xác định bằng cách sao chép từ đối tượng đến một thực tế, tuyên bố mảng của unsigned char
& sau đó kiểm tra sau này. Không rõ liệu OR có thể được giải thích lại trực tiếp như một mảng như vậy hay không, với các khoản phụ cấp cho số học con trỏ mà nó sẽ đòi hỏi, tức là liệu "chuỗi" ==
"mảng" trong cách sử dụng này. Có một vấn đề cốt lõi # 1701 được mở ra với hy vọng làm rõ điều này. Rất may, vì sự mơ hồ này thực sự làm tôi khó chịu gần đây.
unsigned char
của OR và sau đó tiếp tục sử dụng ++ptr
từ đó để đọc từng byte của nó ... nhưng AFAICT, nó không được định nghĩa cụ thể là được phép, vì vậy chúng tôi còn lại để suy luận rằng 'có lẽ OK' từ rất nhiều đoạn khác (và theo nhiều cách, sự tồn tại đơn thuần memcpy
) trong Tiêu chuẩn, gần giống với trò chơi ghép hình. Mà không lý tưởng. Vâng, có thể từ ngữ sẽ cải thiện cuối cùng. Đây là vấn đề CWG tôi đã đề cập nhưng thiếu không gian để liên kết - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
unsigned char
là trái tim của tất cả các mánh khóe bit. Trong hầu hết TẤT CẢ trình biên dịch cho nền tảng TẤT CẢ, unsigned char
chỉ đơn giản là một byte và một số nguyên không dấu (thường) 8 bit có thể được coi là một số nguyên nhỏ hoặc một gói bit.
Trong nghiện, như người khác đã nói, tiêu chuẩn không xác định dấu hiệu của một char. vì vậy bạn có 3 phân biệt char
các loại: char
, signed char
, unsigned char
.
Nếu bạn thích sử dụng các loại khác nhau có độ dài cụ thể và signedness, có lẽ bạn đang tốt hơn off với uint8_t
, int8_t
, uint16_t
, vv đơn giản chỉ vì họ thực hiện chính xác những gì họ nói.
Một số googling tìm thấy điều này , nơi mọi người đã thảo luận về điều này.
Một char không dấu về cơ bản là một byte đơn. Vì vậy, bạn sẽ sử dụng điều này nếu bạn cần một byte dữ liệu (ví dụ: có thể bạn muốn sử dụng nó để đặt và tắt cờ được chuyển đến một chức năng, như thường được thực hiện trong API Windows).
Một char không dấu sử dụng bit được dành riêng cho dấu của một char thông thường như một số khác. Điều này thay đổi phạm vi thành [0 - 255] trái ngược với [-128 - 127].
Các ký tự không dấu thường được sử dụng khi bạn không muốn có một dấu hiệu. Điều này sẽ tạo ra sự khác biệt khi thực hiện những việc như dịch chuyển bit (shift mở rộng dấu hiệu) và những thứ khác khi xử lý char dưới dạng byte thay vì sử dụng nó làm số.
trích dẫn từ cuốn sách "hành vi lập trình c":
Vòng loại signed
hoặc unsigned
có thể được áp dụng cho char hoặc bất kỳ số nguyên nào. số không dấu luôn luôn dương hoặc bằng 0 và tuân theo định luật modulo số học 2 ^ n, trong đó n là số bit trong loại. Vì vậy, ví dụ, nếu các ký tự là 8 bit, các biến char không dấu có các giá trị trong khoảng từ 0 đến 255, trong khi các ký tự được ký có các giá trị trong khoảng từ -128 đến 127 (trong máy bổ sung hai). phụ thuộc, nhưng các ký tự có thể in luôn luôn tích cực.
signed char
và unsigned char
cả hai đại diện cho 1byte, nhưng chúng có phạm vi khác nhau.
Type | range
-------------------------------
signed char | -128 to +127
unsigned char | 0 to 255
Trong trường signed char
hợp chúng tôi xem xét char letter = 'A'
, 'A' đại diện cho nhị phân 65 in ASCII/Unicode
, Nếu 65 có thể được lưu trữ, -65 cũng có thể được lưu trữ. Không có giá trị nhị phân âm trong ASCII/Unicode
đó vì không cần phải lo lắng về giá trị âm.
Thí dụ
#include <stdio.h>
int main()
{
signed char char1 = 255;
signed char char2 = -128;
unsigned char char3 = 255;
unsigned char char4 = -128;
printf("Signed char(255) : %d\n",char1);
printf("Unsigned char(255) : %d\n",char3);
printf("\nSigned char(-128) : %d\n",char2);
printf("Unsigned char(-128) : %d\n",char4);
return 0;
}
Đầu ra -:
Signed char(255) : -1
Unsigned char(255) : 255
Signed char(-128) : -128
Unsigned char(-128) : 128