2015-01-31 23 views
5

Tôi vừa thấy một số mã C++ như thế này. Nó đang sử dụng một điều kiện để quyết định có nên đi về phía trước hoặc phía sau thông qua một số std::vector. Trình biên dịch không phàn nàn, nhưng tôi nghĩ rằng size_t chưa được ký. Điều này có nguy hiểm không?Có an toàn khi sử dụng các số nguyên âm với size_t không?

vector<int> v { 1,2,3,4,5 };  
bool rev = true; 

size_t start, end, di; 
if (rev) { 
    start = v.size()-1; 
    end = -1; 
    di = -1; 
} 
else { 
    start = 0; 
    end = v.size(); 
    di = 1; 
} 

for (auto i=start; i!=end; i+=di) { 
    cout << v[i] << endl; 
} 
+1

Tiêu chuẩn xác định 'std :: string :: npos' là' static const size_type npos = -1; 'về cơ bản là một mẹo tương tự. Để được pedantic bạn có thể thích: 'std :: vector :: size_type bắt đầu, kết thúc, di;'. – Galik

+0

@Galic: Tôi không nhìn thấy những gì các pedantry (như bạn gọi nó) đạt được, những gì lợi thế nó có.Nếu một người duy trì đi ra khỏi con đường của mình để phá vỡ mọi thứ bằng instantiating 'std :: vector' với một cấp phát cung cấp một loại kích thước ngớ ngẩn, tất cả những gì mà người đi bộ làm là tăng cơ hội người đó thành công trong việc làm hỏng mọi thứ. Như tôi thấy. –

+0

@ Cheersandhth.-Alf Có lẽ bạn đã đúng. Theo như tôi có thể nói mặc dù tiêu chuẩn nói 'std :: vector :: size_type' phải được unsigned, nhưng tôi nghĩ rằng nó có thể là một kích thước khác nhau để' std :: size_t'. Mặc dù tôi tưởng tượng hầu hết các triển khai sẽ làm cho 'size_type' giống như' size_t'. – Galik

Trả lời

6

Nó được xác định rõ để sử dụng số nguyên unsigned (và size_t là unsigned) Bằng cách này, với wraparound: Hành vi đó được đảm bảo theo tiêu chuẩn, như trái ngược với với số nguyên ký kết, nơi nó không được đảm bảo theo tiêu chuẩn.

Tuy nhiên, không cần thiết thông minh.

Như một quy tắc chung, để tránh các sự cố do quảng cáo gói ẩn để không được ký, hãy sử dụng số nguyên không dấu cho công cụ cấp bit, sử dụng số nguyên đã ký cho số. Nơi bạn cần số nguyên đã ký tương ứng với size_tptrdiff_t cho bạn. Xác định hàm n_items với kết quả đã ký, ví dụ:

using Size = ptrdiff_t; 

template< class Container > 
auto n_items(Container const& c) 
    -> Size 
{ return end(c) - begin(c); } 

và bạn đã sẵn sàng để tiếp tục, không còn sự ngớ ngẩn từ trình biên dịch nữa.


Thay vì mã cho quá thông minh

vector<int> v { 1,2,3,4,5 };  
bool rev = true; 

size_t start, end, di; 
if (rev) { 
    start = v.size()-1; 
    end = -1; 
    di = -1; 
} 
else { 
    start = 0; 
    end = v.size(); 
    di = 1; 
} 

for (auto i=start; i!=end; i+=di) { 
    cout << v[i] << endl; 

làm ví dụ

const vector<int> v { 1,2,3,4,5 };  
const bool reverse = true; // whatever 

for(int i = 0; i < n_items(v); ++i) 
{ 
    const int j = (reverse? n_items(v) - i - 1 : i); 
    cout << v[j] << endl; 
} 
0

Có an toàn để sử dụng số nguyên âm với size_t?

Không, rất nguy hiểm. Tràn ra.

size_t a = -1; 
std::cout << a << "\n"; 

Output:

4294967295 // depends on the system, largest value possible here 
+1

Đây không phải là tràn nguy hiểm, nhưng được xác định rõ ràng xung quanh. Việc đưa các số âm vào size_t là một cách phổ biến để trả về các lỗi từ các hàm thường trả về một kích thước; bản thân thư viện chuẩn C thực hiện điều này ví dụ như trong các hàm như 'mbrtoc32()'. – dpi

1

tôi không thể nói chuyện với cách an toàn mã đó là nhưng tôi nghĩ rằng đó là một phong cách khá nghèo. Một cách tốt hơn là sử dụng các trình vòng lặp có hỗ trợ lặp lại hoặc đảo ngược.

Ví dụ:

std::vector<int> v = { 1, 2, 3, 4, 5 }; 
bool rev = true; 

if (rev) 
{ 
    for (auto itr = v.rbegin(); itr != v.rend(); ++itr) 
    { 
     std::cout << *itr << "\n"; 
    } 
} 
else 
{ 
    for (auto itr = v.begin(); itr != v.end(); ++itr) 
    { 
     std::cout << *itr << "\n"; 
    } 
} 
+0

Bằng cách nào đó mã không biên dịch với g ++ 4.8.2. Bạn có thể nghĩ ra lý do nào không? –

+1

Tôi không chắc chắn (nó có thể phải làm với thực tế là * rend() * và * bắt đầu() * sẽ không đánh giá cùng loại). Đó là một câu trả lời khái niệm; Tôi đã không kiểm tra nó. Tôi sẽ cập nhật với thứ gì đó thực sự biên dịch. –

0

Bất cứ khi nào tôi cần phải đối phó với các loại ký, tôi luôn luôn sử dụng:

typedef std::make_signed<std::size_t>::type ssize_t; // Since C++11 

... như một sự thay thế ký hợp đồng với std :: size_t.

Tôi đánh giá cao câu hỏi này là một vài năm tuổi, nhưng tôi hy vọng rằng sẽ giúp đỡ người khác. Ghi có vào số moodycamel::ConcurrentQueue.

Các vấn đề liên quan