2010-03-25 36 views
16

Mã cổng từ 32 bit đến 64bit. Rất nhiều địa điểm vớiint vs size_t trên 64bit

int len = strlen(pstr); 

Tất cả đều tạo cảnh báo ngay bây giờ vì strlen() trả về size_t là 64 bit và int vẫn là 32 bit. Vì vậy, tôi đã thay thế chúng bằng

size_t len = strlen(pstr); 

Nhưng tôi chỉ nhận ra rằng đây không phải là an toàn, như size_t là unsigned và nó có thể được coi là chữ ký của mã (Tôi thực sự chạy vào một trường hợp nó gây ra một vấn đề, cảm ơn bạn, kiểm tra đơn vị!).

Quay trở lại một cách mù quáng khi quay trở lại (int) cảm thấy bẩn. Hoặc có lẽ nó không nên?
Vì vậy, câu hỏi đặt ra là: có một giải pháp thanh lịch cho việc này không? Tôi có thể có hàng nghìn dòng mã như thế trong codebase; Tôi không thể kiểm tra thủ công từng cái một và vùng phủ sóng thử nghiệm hiện ở đâu đó giữa 0,01 và 0,001%.

+1

Bạn có một ví dụ nơi chiều dài này được xử lý như đã ký kết? – kroimon

+0

Ví dụ có thể là một cái gì đó dọc theo các dòng: 'len--; if (len <0) {break} ' – Tim

Trả lời

5

Để thỏa hiệp, bạn có thể sử dụng ssize_t (nếu có). Giả mạo nó nếu không, sử dụng long long, int_fast64_t, intmax_t hoặc có tiêu đề chuyển nền tảng cho phép loại phù hợp được chỉ định cho nền tảng. ssize_t có trong POSIX không phải là tiêu chuẩn C hoặc C++, nhưng nếu bạn đã từng nhấn vào một nền tảng không có loại đã ký có cùng kích thước như size_t thì tôi thông cảm.

Truyền tới int gần như an toàn (giả sử 32 bit int trên nền tảng 64 bit của bạn, điều này có vẻ hợp lý), bởi vì chuỗi không dài hơn 2^31 byte. Một diễn viên cho một loại chữ ký lớn hơn thậm chí còn an toàn hơn. Khách hàng có thể mua được 2^63 byte của bộ nhớ là những gì được biết trong thương mại là "một vấn đề tốt để có" ;-)

Tất nhiên, bạn có thể kiểm tra xem nó:

size_t ulen = strlen(pstr); 
if (ulen > SSIZE_MAX) abort(); // preferably trace, log, return error, etc. 
ssize_t len = (ssize_t) ulen; 

Chắc chắn có một overhead , nhưng nếu bạn có 1000 trường hợp thì tất cả chúng đều không thể hoạt động quan trọng. Đối với những người (nếu có), bạn có thể thực hiện công việc để điều tra xem liệu len có được ký thực sự là vấn đề hay không. Nếu không, hãy chuyển sang size_t. Nếu có, viết lại hoặc chỉ chấp nhận rủi ro khi không bao giờ gặp một đối tượng vô cùng to lớn. Mã ban đầu hầu như chắc chắn sẽ làm điều sai trái trên nền tảng 32 bit, nếu len bị âm do kết quả của việc trả về một giá trị lớn hơn INT_MAX.

+0

Tôi đồng ý rằng cast to int là gần như an toàn, nhưng tôi không hiểu điểm của ssize_t: nó cũng là * nerly * an toàn. Nó an toàn hơn một chút so với int, nhưng vẫn - size_t có thể lớn hơn ssize_t. –

+0

@MK, 'ssize_t' phải bằng kích thước' size_t' – osgx

+2

@MK: Tôi nghĩ rằng ý định chung của 'ssize_t' là trong thực tế, việc triển khai POSIX sẽ không cho phép các đối tượng riêng lẻ lớn hơn một nửa kích thước của không gian địa chỉ có sẵn. Thật dễ dàng để thực thi điều này là 'malloc', mặc dù tôi không nghĩ rằng nó được đảm bảo. Sẽ rất hữu ích khi có loại kích thước có chữ ký để biểu diễn các khoảng trống được phép là số âm. –

1

Bạn có thể coi site_t được ký một cách an toàn trong hầu hết các trường hợp. Size_t chưa ký sẽ chỉ được coi là âm khi nó (hoặc kết quả trung gian trong biểu thức) lớn hơn 2^31 (đối với 32 bit) hoặc 2^63 cho 64 bit.

CẬP NHẬT: Xin lỗi, size_t sẽ không an toàn trong các công trình như while ((size_t)t >=0). Câu trả lời đúng là sử dụng ssize_t.

+1

Tôi có nghĩa là trường hợp tôi sau đó giảm len đến một điểm mà nó trở thành tiêu cực. Giống như trong vòng lặp khi (len> 0) –

+0

vòng lặp 'while (len> 0)' nên dừng tại 'len == 0'. Vui lòng cho chúng tôi biết ví dụ của bạn, sự cố trong đó đã được phát hiện với các bài kiểm tra đơn vị. – osgx

+2

Blah, xin lỗi, ý tôi là nếu (len <0). Tôi đã có một vòng lặp với kiểm tra nghịch đảo của "nếu (len <0) bỏ qua một cái gì đó;" thay vì "nếu (len> = 0) làm điều gì đó;" –

5

Đặt cảnh báo trình biên dịch ở mức tối đa sẽ giúp bạn có được báo cáo tốt đẹp về mọi chuyển đổi ký hiệu không chính xác. Trong gcc, '-Wall -Wextra' nên làm.

Bạn cũng có thể sử dụng trình phân tích mã tĩnh như cppcheck để xem mọi thứ có đúng hay không.

+0

và -wall sẽ tìm tất cả các vị trí mà size_t đang được sử dụng trong ngữ cảnh đã ký. Bạn thực sự nên sử dụng size_t – pm100

4

Bạn có thể sử dụng ssize_t (biến thể đã ký của size_t).

7

Một thời gian trước, tôi gửi một ghi chú ngắn về loại vấn đề trên blog của tôi và câu trả lời ngắn gọn là:

Always use proper C++ integer types

Long trả lời: Khi lập trình trong C++, đó là một ý tưởng tốt để sử dụng các kiểu số nguyên thích hợp liên quan đến ngữ cảnh cụ thể. Một chút nghiêm ngặt luôn trả lại. Không phải là hiếm khi thấy xu hướng bỏ qua các loại tích phân được xác định là cụ thể cho các vùng chứa chuẩn, cụ thể là size_type. Nó có sẵn cho số vùng chứa tiêu chuẩn như std :: string hoặc std :: vector. Sự thiếu hiểu biết đó có thể dễ dàng trả thù.

Dưới đây là ví dụ đơn giản về loại được sử dụng không chính xác để thu được kết quả của std :: string :: find function. Tôi khá chắc chắn rằng nhiều người sẽ mong đợi không có gì sai với int không dấu ở đây. Nhưng, thực ra đây chỉ là một lỗi. Tôi chạy Linux trên kiến ​​trúc 64-bit và khi tôi biên dịch chương trình này, nó hoạt động như mong đợi. Tuy nhiên, khi tôi thay thế chuỗi trong dòng 1 bằng abc, nó vẫn hoạt động nhưng không như mong đợi :-)

Khắc phục rất đơn giản. Chỉ cần thay thế int unsigned với std :: string :: size_type. Vấn đề có thể tránh được nếu ai đó đã viết chương trình này đã sử dụng đúng loại. Chưa kể rằng chương trình sẽ được di chuyển ngay lập tức.

Tôi đã nhìn thấy loại vấn đề này khá nhiều lần, đặc biệt là trong mã được viết bởi các lập trình viên C cũ, những người không thích đeo mõm nghiêm ngặt mà hệ thống kiểu C++ thi hành và yêu cầu. Ví dụ trên là một điều nhỏ nhặt, nhưng tôi tin rằng nó trình bày gốc rễ của vấn đề.

Tôi khuyên bạn nên xuất sắc bài viết 64-bit development được viết bởi Andrey Karpov, nơi bạn có thể tìm thấy nhiều hơn về chủ đề này.

+2

Mặc dù tôi thường đồng ý với "sử dụng các loại chính xác", 'std :: some_container :: size_type' sôi xuống thành' size_t' trong tất cả các triển khai phong nha.Theo như tôi thấy, ít nhất 'std :: bitset :: size_type',' std :: mảng :: size_type', 'std :: initializer_list' và' std :: allocator :: size_type' là typedefs cho ' size_t'. Vì vậy, trừ khi bạn đang sử dụng một cấp phát điên hoặc tham số mẫu rất đặc biệt, 'size_t' là đủ. – rubenvb

1

Nếu trình biên dịch của bạn hỗ trợ C++ 0x:

auto len = strlen(pstr);