uint8_t không thể được in bằng cout


146

Tôi có một vấn đề kỳ lạ về làm việc với các số nguyên trong C ++.

Tôi đã viết một chương trình đơn giản đặt giá trị thành một biến và sau đó in nó, nhưng nó không hoạt động như mong đợi.

Chương trình của tôi chỉ có hai dòng mã:

uint8_t aa = 5;

cout << "value is " << aa << endl;

Đầu ra của chương trình này là value is

Tức là, nó in trống cho aa.

Khi tôi đổi uint8_tsang uint16_tđoạn mã trên hoạt động như một lá bùa.

Tôi sử dụng Ubuntu 12.04 (Chính xác Pangolin), 64-bit và phiên bản trình biên dịch của tôi là:

gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

2
Bản sao có thể có của hành vi iostream uint8_t
phuclv

Câu trả lời:


150

Nó không thực sự in một khoảng trống, nhưng rất có thể là ký tự ASCII có giá trị 5, không thể in được (hoặc vô hình). Có một số mã ký tự ASCII vô hình , hầu hết trong số chúng dưới giá trị 32, thực tế là trống.

Bạn phải chuyển đổi aađể unsigned intxuất giá trị số, vì ostream& operator<<(ostream&, unsigned char)cố gắng xuất giá trị ký tự hiển thị.

uint8_t aa=5;

cout << "value is " << unsigned(aa) << endl;

24
Vì các diễn viên kiểu C được tán thành, nên làm static_cast sẽ tốt hơn?
Tim Seguine

37
Nó nên được chuyển đổi thành int. Diễn viên là một cách để làm điều đó, nhưng không phải là cách duy nhất. +aacũng hoạt động.
Pete Becker

5
không int (var) và (int) var thực sự giống nhau?
paulm

9
Xem câu hỏi được liên kết bằng cách sử dụng loại (var) giống như (loại) var giống như diễn viên C - hãy thử với const vv, nó sẽ loại bỏ nó!
paulm

13
Câu trả lời "Số phôi kiểu c không được khuyến khích cho c ++ vì một số lý do." thành "không phải int (var) và (int) var thực sự giống nhau?" chắc chắn làm cho nó có vẻ như bạn không nhận ra int(var)(int)varcó cùng một ý nghĩa. int(var)không được khuyến khích trong chính xác những trường hợp đó (int)var, vì những lý do chính xác giống nhau, bởi vì nó có nghĩa chính xác là cùng một điều. (Tôi có thể hiểu được lý do tại sao bạn muốn đi cho nó ở đây dù sao, mặc dù, vì vậy tôi không nói rằng bạn cần phải sử dụng static_castTôi chỉ nghĩ rằng con đường mòn bình luận ở đây có một chút không cần thiết gây nhầm lẫn..)

46

uint8_trất có thể sẽ là một typedefcho unsigned char. Các ostreamlớp học có một tình trạng quá tải đặc biệt dành cho unsigned char, tức là nó in các nhân vật với số lượng 5, đó là không thể in được, vì thế mà không gian trống rỗng.


14
Tôi muốn tiêu chuẩn thực sự được coi std :: uint8_t là một loại riêng biệt và không chỉ là một typedef của friggin. Không có lý do chính đáng để áp dụng ngữ nghĩa ký tự cho các loại này khi được sử dụng cùng với các đối tượng luồng.
antred

37

Thêm một toán tử unary + trước biến của bất kỳ kiểu dữ liệu nguyên thủy nào sẽ cho giá trị số có thể in thay vì ký tự ASCII (trong trường hợp kiểu char).

uint8_t aa = 5;
cout<<"value is "<< +aa <<endl;

Đó là tốt đẹp, nhưng tại sao không c ++ điều trị uint8_tnhư unsigned charđó sẽ là giá trị số?
R1S8K

@ R1S8K điều này là do mặc dù uint8_tchỉ là một loại def của chính nó unsigned char, nhưng unsigned charnó được xử lý bằng ostreamcách thích charvà in giá trị ASCII của nó.
Harsh

@Harsh Cảm ơn người đàn ông! Vì vậy, đó là một loại def của unsigned charđiều đó giải thích rất nhiều. Vì vậy, số nguyên duy nhất là int, phải không?
R1S8K

@ R1S8K Chà, loại số nguyên nhỏ nhất sẽ short intchiếm 2 byte. Có một vài biến thể khác của loại số nguyên là tốt.
Khắc nghiệt

@Harsh Ngoài ra tôi nghĩ khi lập trình và khai báo các biến, sẽ không có vấn đề gì nếu tôi khai báo một biến chỉ xử lý các số nhỏ không vượt quá 250 với kích thước đăng ký lớn như longhoặc intvì trình biên dịch sẽ tối ưu hóa việc sử dụng RAM hoặc flash theo những gì điền vào đăng ký, tôi có đúng không?
R1S8K

16

Đó là bởi vì toán tử đầu ra xử lý uint8_tgiống như một char( uint8_tthường chỉ là bí danh unsigned char), vì vậy nó in ký tự bằng mã ASCII (là hệ thống mã hóa ký tự phổ biến nhất) 5.

Xem ví dụ này tham khảo .


Tại sao? trình biên dịch C coi nó như một số. Tôi nghĩ rằng C ++ là khác nhau ở điểm này.
R1S8K

@PerchEagle Nếu bạn đọc tham chiếu được liên kết, bạn sẽ thấy toán tử bị quá tải cho cả hai signedunsignedký tự (ngoài đồng bằng charmà trong C ++ thực sự là loại riêng biệt thứ ba). Vì vậy, nếu uint8_tlà một bí danh cho unsigned char(rất có thể) đó là những gì sẽ được sử dụng.
Một số lập trình viên anh chàng

Bạn có thể kiểm tra câu trả lời của tôi về chủ đề này và cho tôi biết nếu câu trả lời của tôi là đúng hay không? stackoverflow.com/questions/15585267/ , câu trả lời của tôi là trước câu cuối cùng. Cảm ơn bạn rất nhiều.
R1S8K

14
  • Sử dụng ADL (Tra cứu tên phụ thuộc đối số):

    #include <cstdint>
    #include <iostream>
    #include <typeinfo>
    
    namespace numerical_chars {
    inline std::ostream &operator<<(std::ostream &os, char c) {
        return std::is_signed<char>::value ? os << static_cast<int>(c)
                                           : os << static_cast<unsigned int>(c);
    }
    
    inline std::ostream &operator<<(std::ostream &os, signed char c) {
        return os << static_cast<int>(c);
    }
    
    inline std::ostream &operator<<(std::ostream &os, unsigned char c) {
        return os << static_cast<unsigned int>(c);
    }
    }
    
    int main() {
        using namespace std;
    
        uint8_t i = 42;
    
        {
            cout << i << endl;
        }
    
        {
            using namespace numerical_chars;
            cout << i << endl;
        }
    }
    

    đầu ra:

    *
    42
    
  • Một trình điều khiển luồng tùy chỉnh cũng sẽ có thể.

  • Toán tử unary plus là một thành ngữ gọn gàng quá ( cout << +i << endl).

8
Không phải KISS vẫn là một mô hình hợp lệ sao ??
πάντα ῥεῖ


4
@ πάτῥεῖ ok, hãy tiếp tục thực hiện hàng tấn phôi kiểu c trong mã c ++, bất cứ ai cũng có thể tự do làm việc theo cách đó, trong môi trường mà anh ấy / cô ấy phù hợp nhất.
pepper_chico

5
@ πάτ nghiêm túc? phong cách chức năng đúc, cũng, chỉ là c phong cách đúc. thay đổi cái này từ cái khác không giúp ích gì trong việc rời khỏi vương quốc C, kiểm tra: stackoverflow.com/a/4775807/1000282 . pete-becker cũng nhận xét điều này về câu trả lời của bạn, nhưng dường như bạn đã bỏ lỡ bình luận cuối cùng của anh ấy.
pepper_chico

3
Giải pháp này rất thanh lịch & hiệu quả, bởi vì nó hoạt động với các mẫu. Trên thực tế, đó là giải pháp duy nhất tôi phát hiện ra có hiệu quả với tôi. Mặc dù vậy, một cảnh báo, hàm đầu tiên có lỗi, vì 'os' bị ràng buộc với một loại duy nhất và do đó, giá trị đã ký hoặc không dấu sẽ được gửi đến phiên bản sai của toán tử << (). Cách khắc phục đủ đơn giản:return std::is_signed<char>::value ? os << static_cast<int>(c) : os << static_cast<unsigned int>(c);
Georges

7

coutđược điều trị aanhư chargiá trị ASCII 5mà là một nhân vật chưa in ra, hãy thử typecasting đến inttrước khi in.


4

Sự operator<<()quá tải giữa istreamcharlà một chức năng không phải là thành viên. Bạn rõ ràng có thể sử dụng hàm thành viên để coi một char(hoặc a uint8_t) là một int.

#include <iostream>
#include <cstddef>

int main()
{
   uint8_t aa=5;

   std::cout << "value is ";
   std::cout.operator<<(aa);
   std::cout << std::endl;

   return 0;
}

Đầu ra:

value is 5

2

Như những người khác đã nói trước khi sự cố xảy ra vì luồng tiêu chuẩn coi char đã ký và char không dấu là ký tự đơn và không phải là số.

Đây là giải pháp của tôi với các thay đổi mã tối thiểu:

uint8_t aa = 5;

cout << "value is " << aa + 0 << endl;

Thêm "+0"là an toàn với bất kỳ số bao gồm cả dấu chấm động.

Đối với các kiểu số nguyên, nó sẽ thay đổi loại kết quả thành intnếu sizeof(aa) < sizeof(int). Và nó sẽ không thay đổi loại nếu sizeof(aa) >= sizeof(int).

Giải pháp này cũng tốt cho việc chuẩn bị int8_tđược in để phát trực tuyến trong khi một số giải pháp khác không tốt lắm:

int8_t aa = -120;

cout << "value is " << aa + 0 << endl;
cout << "bad value is " << unsigned(aa) << endl;

Đầu ra:

value is -120
bad value is 4294967176

Giải pháp PS với ADL được cung cấp bởi pepper_chico và πά thực sự rất đẹp.

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.