In các ký tự thập lục phân trong C


103

Tôi đang cố đọc một dòng ký tự, sau đó in ra ký tự tương đương hệ thập lục phân.

Ví dụ: nếu tôi có một chuỗi "0xc0 0xc0 abc123", trong đó 2 ký tự đầu tiên c0ở dạng hex và các ký tự còn lại abc123ở dạng ASCII, thì tôi sẽ nhận được

c0 c0 61 62 63 31 32 33

Tuy nhiên, printfviệc sử dụng %xmang lại cho tôi

ffffffc0 ffffffc0 61 62 63 31 32 33

Làm cách nào để tôi có được đầu ra mà tôi muốn mà không có "ffffff"? Và tại sao chỉ có c0 (và 80) có ffffff, mà không có các ký tự khác?


Chuỗi khớp với mảng byte của bạn sẽ là ..."\xc0\xc0abc123"
burito

Câu trả lời:


132

Bạn đang thấy ffffffbởi vì charđược ký trên hệ thống của bạn. Trong C, các hàm vararg chẳng hạn như printfsẽ thúc đẩy tất cả các số nguyên nhỏ hơn intđến int. Vì charlà một số nguyên (số nguyên có dấu 8 bit trong trường hợp của bạn), các ký tự của bạn đang được thăng cấp intthông qua phần mở rộng dấu.

c080có 1 bit đứng đầu (và âm là số nguyên 8 bit), chúng đang được mở rộng dấu hiệu trong khi các số khác trong mẫu của bạn thì không.

char    int
c0 -> ffffffc0
80 -> ffffff80
61 -> 00000061

Đây là một giải pháp:

char ch = 0xC0;
printf("%x", ch & 0xff);

Thao tác này sẽ che đi các bit trên và chỉ giữ lại 8 bit dưới mà bạn muốn.


15
Giải pháp của tôi sử dụng một dàn diễn viên đến unsigned charlà một trong những hướng dẫn nhỏ hơn trong gcc4.6 cho x86-64 ...
lvella

1
Có lẽ tôi có thể giúp. Đây là hành vi không xác định (về mặt kỹ thuật) vì trình xác định xyêu cầu một kiểu không dấu, nhưng ch được thăng cấp thành int. Mã chính xác chỉ đơn giản là sẽ đúc ch để unsigned, hoặc sử dụng một dàn diễn viên để char unsigned và sự xác định: hhx.
2501

1
Nếu tôi có printf("%x", 0), không có gì được in.
Gustavo Meira

Nó không in bất cứ thứ gì vì giá trị tối thiểu được đặt thành 0. Để khắc phục điều này, hãy thử printf("%.2x", 0);điều này sẽ tăng các ký tự tối thiểu được vẽ lên 2. Để đặt giá trị tối đa, hãy thêm vào trước. với một số. Ví dụ: bạn chỉ có thể buộc 2 ký tự được vẽ bằng cách thực hiệnprintf("%2.2x", 0);
dùng2262111

Bất kỳ lý do nào tại sao printf("%x", ch & 0xff)nên tốt hơn là chỉ sử dụng printf("%02hhX", a)như trong câu trả lời của @ wild_lobster ?
maxschlepzig

62

Thật vậy, có chuyển đổi kiểu thành int. Ngoài ra, bạn có thể buộc loại thành ký tự bằng cách sử dụng% hhx specifier.

printf("%hhX", a);

Trong hầu hết các trường hợp, bạn sẽ muốn đặt độ dài tối thiểu cũng như để điền vào ký tự thứ hai bằng các số 0:

printf("%02hhX", a);

ISO / IEC 9899: 201x cho biết:

7 Các từ bổ nghĩa độ dài và ý nghĩa của chúng là: hh Chỉ định rằng một từ bổ sung chuyển đổi d, i, o, u, x hoặc X sau đây áp dụng cho một đối số char có dấu hoặc không dấu (đối số sẽ được thăng cấp theo các thăng hạng số nguyên, nhưng giá trị của nó sẽ được chuyển thành ký tự có dấu hoặc ký tự không có dấu trước khi in); hoặc sau đó


30

Bạn có thể tạo một ký tự không dấu:

unsigned char c = 0xc5;

In nó sẽ cho C5và không ffffffc5.

Chỉ những ký tự lớn hơn 127 mới được in ffffffbởi vì chúng là âm (ký tự là ký tự).

Hoặc bạn có thể truyền chartrong khi in:

char c = 0xc5; 
printf("%x", (unsigned char)c);

3
+1 câu trả lời thực sự hay nhất, nhập rõ ràng càng gần với khai báo dữ liệu càng tốt (nhưng không gần hơn).
Bob Stein

13

Có thể bạn đang lưu trữ giá trị 0xc0 trong một charbiến, có thể là kiểu có dấu và giá trị của bạn là âm (tập hợp bit quan trọng nhất). Sau đó, khi in, nó được chuyển đổi thành int, và để giữ sự tương đương về ngữ nghĩa, trình biên dịch sẽ chèn thêm các byte bằng 0xff, do đó, âm intsẽ có cùng giá trị số của âm của bạn char. Để khắc phục điều này, chỉ cần truyền đến unsigned charkhi in:

printf("%x", (unsigned char)variable);

13

Bạn có thể sử dụng hhđể cho biết printfrằng đối số là một ký tự không dấu. Sử dụng 0để lấy phần đệm bằng 0 và 2đặt chiều rộng thành 2. xhoặc Xcho các ký tự hex viết thường / viết hoa.

uint8_t a = 0x0a;
printf("%02hhX", a); // Prints "0A"
printf("0x%02hhx", a); // Prints "0x0a"

Chỉnh sửa : Nếu người đọc lo ngại về khẳng định của 2501 rằng bằng cách nào đó đây không phải là từ định dạng 'chính xác', tôi khuyên họ nên đọc lại printfliên kết . Đặc biệt:

Mặc dù% c mong đợi đối số int, nhưng vẫn an toàn để chuyển một char vì sự thăng hạng số nguyên diễn ra khi một hàm variadic được gọi.

Các thông số kỹ thuật chuyển đổi chính xác cho các loại ký tự có độ rộng cố định (int8_t, v.v.) được xác định trong tiêu đề <cinttypes>(C ++) hoặc <inttypes.h>(C) (mặc dù PRIdMAX, PRIuMAX, v.v. đồng nghĩa với% jd,% ju, v.v.) .

Đối với quan điểm của anh ấy về có dấu và không có dấu, trong trường hợp này không quan trọng vì các giá trị phải luôn dương và dễ dàng phù hợp với một int có dấu. Không có mã định dạng hexideximal có dấu nào cả.

Chỉnh sửa 2 : (ấn bản "khi-thừa-nhận-bạn đã sai"):

Nếu bạn đọc tiêu chuẩn C11 thực tế trên trang 311 (329 của PDF), bạn thấy:

hh: Chỉ định rằng một sau d, i, o, u, x, hoặc Xxác định chuyển đổi áp dụng cho một signed charhoặc unsigned charđối số (đối số sẽ được thăng chức theo chương trình khuyến mãi số nguyên, nhưng giá trị của nó sẽ được chuyển đổi sang signed charhoặc unsigned chartrước khi in); hoặc rằng một nchỉ định chuyển đổi sau áp dụng cho một con trỏ đến một signed charđối số.


Các chỉ định không đúng cho loại uint8_t. Các loại chiều rộng cố định sử dụng các chỉ định in đặc biệt. Xem:inttypes.h
2501

Vâng, nhưng tất cả các số nguyên varargs đều được thăng cấp ngầm thành int.
Timmmm

Điều đó có thể là như vậy, nhưng theo như C được định nghĩa, hành vi không được xác định nếu bạn không sử dụng từ xác định chính xác.
2501

Nhưng% x giá trị xác định chính xác. ( charunsigned charđược thăng cấp lên int) [ en.cppreference.com/w/cpp/language/variadic_arguments] . Bạn sẽ chỉ cần sử dụng các specifiers PRI cho những thứ không phù hợp với nền tảng của bạn int- ví dụ unsigned int.
Timmmm

%xlà đúng cho int unsigned không phải int. Các loại char và unsigned char được thăng cấp thành int. Ngoài ra, không có gì đảm bảo rằng uint8_t được định nghĩa là char không dấu.
2501

2

Bạn có thể đang in từ một mảng ký tự đã ký. In từ một mảng char không dấu hoặc che giá trị bằng 0xff: ví dụ: ar [i] & 0xFF. Các giá trị c0 đang được mở rộng dấu do bit (dấu) cao được đặt.


-1

Hãy thử một cái gì đó như sau:

int main()
{
    printf("%x %x %x %x %x %x %x %x\n",
        0xC0, 0xC0, 0x61, 0x62, 0x63, 0x31, 0x32, 0x33);
}

Cái nào tạo ra cái này:

$ ./foo 
c0 c0 61 62 63 31 32 33
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.