Có an toàn khi sử dụng -1 để đặt tất cả các bit thành đúng không?


132

Tôi đã thấy mô hình này được sử dụng rất nhiều trong C & C ++.

unsigned int flags = -1;  // all bits are true

Đây có phải là một cách di động tốt để thực hiện điều này? Hoặc là sử dụng 0xffffffffhoặc ~0tốt hơn?


1
Tôi không hiểu, bạn có thể giải thích?
corazza

8
Tôi nghĩ liệu ý nghĩa của mã có rõ ràng hay không là câu hỏi quan trọng nhất. Mặc dù -1sẽ luôn hoạt động, thực tế là một bình luận là cần thiết sau khi nó cho thấy rằng nó không rõ ràng về mã. Nếu biến có nghĩa là một tập hợp các cờ, tại sao lại gán cho nó một số nguyên? Kiểu của nó có thể là một số nguyên, nhưng chắc chắn nó không phải là một số nguyên. Bạn sẽ không bao giờ tăng nó hoặc nhân nó. Vì vậy, tôi sẽ sử dụng 0xffffffffkhông phải cho tính di động hoặc tính chính xác, nhưng cho sự rõ ràng.
Cam Jackson

@CamJackson nhận xét không phải và bất kỳ ai viết mã C đều có thể quen với cách các giá trị được biểu diễn.
Tuyến Miles

Câu hỏi ban đầu được gắn thẻ đúng là C và C ++. Các ngôn ngữ có thể được chuyển hướng trong đó C ++ có một đề xuất yêu cầu bổ sung hai. Điều đó nói rằng, nó không thay đổi thực tế -1vẫn là một giải pháp tương thích di động và lạc hậu cho cả hai ngôn ngữ, nhưng nó có thể ảnh hưởng đến một số lý do trong các câu trả lời khác.
Adrian McCarthy

Câu trả lời:


154

Tôi khuyên bạn nên làm điều đó chính xác như bạn đã thể hiện, vì đó là cách đơn giản nhất. Khởi tạo để luôn-1 hoạt động , độc lập với biểu diễn dấu hiệu thực tế, trong khi đôi khi sẽ có hành vi đáng ngạc nhiên vì bạn sẽ phải có loại toán hạng phù hợp. Chỉ sau đó bạn sẽ nhận được giá trị cao nhất của một loại.~unsigned

Để biết ví dụ về một bất ngờ có thể xảy ra, hãy xem xét điều này:

unsigned long a = ~0u;

Nó sẽ không nhất thiết lưu trữ một mẫu có tất cả các bit 1 vào a. Nhưng trước tiên, nó sẽ tạo một mẫu có tất cả các bit 1 trong một unsigned int, sau đó gán nó cho a. Điều gì xảy ra khi unsigned longcó nhiều bit hơn là không phải tất cả trong số đó là 1.

Và xem xét cái này, cái này sẽ thất bại trên đại diện bổ sung không phải của hai:

unsigned int a = ~0; // Should have done ~0u !

Lý do cho điều đó là ~0phải đảo ngược tất cả các bit. Đảo ngược sẽ mang lại -1trên máy bổ sung hai (đó là giá trị chúng ta cần!), Nhưng sẽ không mang lại -1đại diện khác. Trên máy bổ sung của một người, nó mang lại số không. Do đó, trên máy bổ trợ của một người, ở trên sẽ khởi tạo avề không.

Điều bạn nên hiểu là tất cả về giá trị - không phải bit. Biến được khởi tạo với một giá trị . Nếu trong trình khởi tạo, bạn sửa đổi các bit của biến được sử dụng để khởi tạo, giá trị sẽ được tạo theo các bit đó. Giá trị bạn cần, để khởi tạoa giá trị cao nhất có thể, là -1hoặc UINT_MAX. Thứ hai sẽ phụ thuộc vào loại a- bạn sẽ cần sử dụng ULONG_MAXcho một unsigned long. Tuy nhiên, cách đầu tiên sẽ không phụ thuộc vào loại của nó và đó là một cách hay để nhận được giá trị cao nhất.

Chúng tôi không nói về việc -1có tất cả các bit một hay không (không phải lúc nào cũng có). Và chúng ta không nói về việc ~0có tất cả các bit một hay không (tất nhiên là có).

Nhưng những gì chúng ta đang nói là kết quả của flagsbiến khởi tạo là gì. Và cho nó, chỉ-1 sẽ làm việc với mọi loại và máy.


9
Tại sao -1 được đảm bảo để được chuyển đổi cho tất cả những người? Điều đó có được đảm bảo bởi tiêu chuẩn không?
jalf

9
việc chuyển đổi xảy ra là nó liên tục thêm một lần nữa so với ULONG_MAX cho đến khi nó nằm trong phạm vi (6.3.1.3 trong dự thảo C TC2). Trong C ++ cũng vậy, chỉ cần sử dụng một cách khác để chính thức hóa nó (modulo 2 ^ n). Tất cả đều liên quan đến toán học.
Julian Schaub - litb

6
@litb: truyền -1 chắc chắn là một cách hay để nhận các giá trị không dấu tối đa, nhưng nó không thực sự mô tả; đó là lý do tại sao các hằng số _MAX tồn tại (SIZE_MAX đã được thêm vào C99); được cho phép, phiên bản C ++ numeric_limits<size_t>::max()hơi dài dòng, nhưng diễn viên cũng vậy ...
Christoph

11
"Chúng tôi không nói về việc -1 có tất cả các bit một hay không (không phải lúc nào cũng có). Và chúng tôi không nói về việc ~ 0 có tất cả các bit một hay không (tất nhiên là có)." - ai ??? Tôi nghĩ rằng toàn bộ vấn đề đặt tất cả các bit thành 1. Đó là cách các cờ hoạt động..không ?? Bạn nhìn vào các bit . Ai quan tâm đến giá trị?
mở

14
@Mark người hỏi quan tâm. Anh ta hỏi "Có an toàn không khi sử dụng -1 để đặt tất cả các bit thành đúng". Điều này không hỏi về những gì bit -1được đại diện bởi, và cũng không hỏi những gì bit ~0có. Chúng tôi có thể không quan tâm đến các giá trị, nhưng trình biên dịch thì có. Chúng ta không thể bỏ qua thực tế là các hoạt động làm việc với và theo các giá trị. Các giá trị của ~0có thể không -1, nhưng đây là giá trị mà bạn cần. Xem câu trả lời của tôi và tóm tắt của @ Dingo.
Julian Schaub - litb

49
  • unsigned int flags = -1; là hàng xách tay.
  • unsigned int flags = ~0; không thể mang theo được vì nó phụ thuộc vào đại diện bổ sung của hai.
  • unsigned int flags = 0xffffffff; không khả dụng vì nó giả sử ints 32 bit.

Nếu bạn muốn đặt tất cả các bit theo cách được đảm bảo theo tiêu chuẩn C, hãy sử dụng bit đầu tiên.


10
Làm thế nào để ~ 0 (tức là toán tử bổ sung của một người) dựa vào biểu diễn bổ sung của hai?
Hội trường Drew

11
Bạn có điều này ngược. Cờ cài đặt của nó thành -1 dựa trên biểu diễn bổ sung twos. Trong một dấu hiệu + biểu diễn cường độ trừ đi, người ta chỉ có hai bit được đặt: bit dấu và bit có ý nghĩa nhỏ nhất của độ lớn.
Stephen C. Steel

15
Tiêu chuẩn C yêu cầu giá trị int bằng 0 có bit dấu và tất cả các bit giá trị bằng 0. Sau phần bổ sung của một người, tất cả các bit đó là một. Các giá trị của một int với tất cả các bit được đặt là: Ký hiệu và cường độ: INT_MIN Phần bù của một: -0 Phần bù hai: -1 Vì vậy, câu lệnh "unsign int flags = ~ 0;" sẽ chỉ định giá trị nào ở trên khớp với biểu diễn số nguyên của nền tảng. Nhưng '-1' của phần bù hai là phần duy nhất sẽ đặt tất cả các bit cờ thành một.
Dingo

9
@Stephen: Đồng ý về việc đại diện. Nhưng khi một giá trị int được gán cho một số nguyên không dấu, thì số không dấu không nhận được giá trị của nó bằng cách áp dụng biểu diễn bên trong của giá trị int (ngoại trừ trong hai hệ thống bổ sung có hoạt động chung). Tất cả các giá trị được gán cho int unsign là modulo (UINT_MAX + 1), vì vậy việc gán -1 hoạt động bất kể biểu diễn bên trong.
Dingo

20
@Mark: bạn đang nhầm lẫn hai thao tác. ~0tất nhiên mang lại một intgiá trị với tất cả các bit được đặt. Nhưng việc gán một intcho unsigned intkhông nhất thiết dẫn đến int không dấu có cùng mẫu bit với mẫu bit đã ký. Chỉ với đại diện bổ sung của 2 là luôn luôn như vậy. Trên biểu diễn bổ sung hoặc cường độ ký hiệu của 1s, gán intgiá trị âm cho unsigned intkết quả theo mẫu bit khác. Điều này là do tiêu chuẩn C ++ xác định chuyển đổi đã ký -> không dấu là giá trị bằng modulo, không phải là giá trị có cùng bit.
Steve Jessop

25

Thành thật mà nói tôi nghĩ rằng tất cả các fff là dễ đọc hơn. Đối với nhận xét rằng đó là một antipotype, nếu bạn thực sự quan tâm rằng tất cả các bit được thiết lập / xóa, tôi sẽ tranh luận rằng bạn có thể đang ở trong một tình huống mà bạn quan tâm đến kích thước của biến, dù sao cũng sẽ gọi một cái gì đó như boost :: uint16_t, v.v.


Có một số trường hợp bạn không quan tâm lắm, nhưng chúng rất hiếm. Ví dụ: các thuật toán hoạt động trên các tập dữ liệu của N bit bằng cách chia nó thành các khối có kích thước (không dấu) * mỗi bit CHAR_BIT.
MSalters

2
+1. Ngay cả khi kích thước của kiểu dữ liệu lớn hơn số của F (nghĩa là bạn không hoàn toàn đặt tất cả các bit thành đúng), vì bạn rõ ràng đặt giá trị, ít nhất bạn cũng biết rằng bit nào là "an toàn để sử dụng "..
mpen

17

Một cách để tránh các vấn đề được đề cập là chỉ cần làm:

unsigned int flags = 0;
flags = ~flags;

Di động và đến điểm.


2
Nhưng sau đó bạn mất khả năng khai báo flagsconst.
David Stone

1
@DavidStoneunsigned int const flags = ~0u;

@Zoidberg '- Điều đó không hoạt động trên các hệ thống khác ngoài hai phần bù. Chẳng hạn, trên một hệ thống cường độ ký hiệu, ~0là một số nguyên có tất cả các bit được đặt thành 1, nhưng khi bạn gán nó intcho unsignedbiến đó flags, bạn thực hiện chuyển đổi giá trị từ -2**31(giả sử là 32 bit int) thành (-2**31 % 2**32) == 2**31số nguyên với tất cả các bit nhưng lần đầu tiên được đặt thành 1.
David Stone

Đó cũng là một lý do tại sao câu trả lời này là nguy hiểm. Có vẻ như câu trả lời của @Zoidberg sẽ giống hệt nhau, nhưng thực tế không phải vậy. Tuy nhiên, là một người đọc mã, tôi sẽ phải suy nghĩ về nó để hiểu lý do tại sao bạn thực hiện hai bước để khởi tạo nó và có thể bị cám dỗ thay đổi điều đó thành một bước duy nhất.
David Stone

2
À vâng, tôi không nhận thấy uhậu tố trong câu trả lời của bạn. Điều đó tất nhiên sẽ hoạt động, nhưng vẫn có vấn đề chỉ định loại dữ liệu bạn sử dụng ( unsignedvà không lớn hơn) hai lần, điều này có thể dẫn đến lỗi. Tuy nhiên, lỗi rất có thể xuất hiện nếu việc gán và khai báo biến ban đầu cách xa nhau hơn.
David Stone

13

Tôi không chắc chắn sử dụng một int unsign cho cờ là một ý tưởng tốt ở vị trí đầu tiên trong C ++. Những gì về bitet và tương tự?

std::numeric_limit<unsigned int>::max()là tốt hơn vì 0xffffffffgiả sử int unsign là số nguyên 32 bit.


Tôi thích điều này vì tiêu chuẩn của nó, nhưng nó quá dài dòng và nó khiến bạn phát biểu kiểu hai lần. Sử dụng ~ 0 có thể an toàn hơn vì 0 có thể là bất kỳ loại số nguyên nào. (Mặc dù tôi biết rằng nó có mùi quá nhiều của C.)
Macke

Thực tế nó là dài dòng có thể được coi là một lợi thế. Nhưng tôi cũng thích ~ 0.
Edouard A.

2
Bạn có thể giảm thiểu độ dài bằng macro UINT_MAX tiêu chuẩn, vì dù sao bạn cũng đang mã hóa kiểu không dấu int.

2
@Macke Bạn có thể tránh nêu loại trong C ++ 11 với auto. auto const flags = std::numeric_limit<unsigned>::max().
David Stone

11
unsigned int flags = -1;  // all bits are true

"Đây có phải là một cách tốt [,] di động để thực hiện điều này?"

Di động? Đúng .

Tốt chứ Tranh cãi , bằng chứng là tất cả sự nhầm lẫn được hiển thị trên chủ đề này. Rõ ràng đủ để các lập trình viên đồng nghiệp của bạn có thể hiểu mã mà không nhầm lẫn phải là một trong những thứ nguyên mà chúng tôi đo lường cho mã tốt.

Ngoài ra, phương pháp này dễ bị cảnh báo trình biên dịch . Để bỏ qua cảnh báo mà không làm tê liệt trình biên dịch của bạn, bạn cần một diễn viên rõ ràng. Ví dụ,

unsigned int flags = static_cast<unsigned int>(-1);

Dàn diễn viên rõ ràng yêu cầu bạn chú ý đến loại mục tiêu. Nếu bạn chú ý đến loại mục tiêu, thì đương nhiên bạn sẽ tránh được những cạm bẫy của các phương pháp khác.

Lời khuyên của tôi sẽ là chú ý đến loại mục tiêu và đảm bảo không có chuyển đổi ngầm định. Ví dụ:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

Tất cả đều đúng và rõ ràng hơn đối với các lập trình viên của bạn.

Và với C ++ 11 : Chúng ta có thể sử dụng autođể làm cho bất kỳ thứ nào trong số này thậm chí còn đơn giản hơn:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

Tôi xem xét đúng và rõ ràng tốt hơn đơn giản là chính xác.


10

Chuyển đổi -1 thành bất kỳ loại không dấu nào được đảm bảo theo tiêu chuẩn để tạo ra tất cả các loại. Việc sử dụng ~0Unói chung là xấu vì 0có loại unsigned intvà sẽ không điền vào tất cả các bit của loại không dấu lớn hơn, trừ khi bạn viết rõ ràng một cái gì đó như ~0ULL. Trên các hệ thống lành mạnh, ~0nên giống hệt với-1 , nhưng vì tiêu chuẩn cho phép các biểu diễn bổ sung và ký hiệu / cường độ, nói đúng ra nó không thể mang theo được.

Tất nhiên sẽ luôn ổn khi viết ra 0xffffffffnếu bạn biết bạn cần chính xác 32 bit, nhưng -1 có lợi thế là nó sẽ hoạt động trong mọi ngữ cảnh ngay cả khi bạn không biết kích thước của loại, chẳng hạn như macro hoạt động trên nhiều loại hoặc nếu kích thước của loại thay đổi theo cách thực hiện. Nếu bạn không biết loại, một cách an toàn để có được tất cả-những người là các macro hạn UINT_MAX, ULONG_MAX, ULLONG_MAXvv

Cá nhân tôi luôn sử dụng -1. Nó luôn hoạt động và bạn không cần phải suy nghĩ về nó.


FWIW, nếu tôi muốn nói là tất cả 1 bit bit tôi sử dụng ~(type)0(tốt, tất nhiên là điền vào bên phải type). Việc truyền số 0 vẫn cho kết quả bằng 0, vì vậy điều đó rõ ràng và việc phủ định tất cả các bit trong loại mục tiêu được xác định khá rõ ràng. Nó không thường xuyên mà tôi thực sự muốn hoạt động đó mặc dù; YMMV.
Donal Fellows

6
@Donal: bạn đơn giản là sai. C chỉ định rằng, khi chuyển đổi một giá trị không phù hợp với loại không dấu, các giá trị bị giảm modulo 2 ^ n trong đó n là số bit trong loại đích. Điều này áp dụng cả cho các giá trị đã ký và các loại không dấu lớn hơn. Nó không có gì để làm với bổ sung twos.
R .. GitHub DỪNG GIÚP ICE

2
Họ làm thực hiện nó như nhau, đó là tầm thường; bạn chỉ cần sử dụng một opcode trừ không dấu thay vì một ký tên hoặc một neghướng dẫn. Các máy có hành vi số học không có chữ ký có các opcode số học được ký / không dấu riêng biệt. Tất nhiên, một trình biên dịch thực sự tốt sẽ luôn luôn bỏ qua các opcodes đã ký ngay cả đối với các giá trị đã ký và do đó được bổ sung twos miễn phí.
R .. GitHub DỪNG GIÚP ICE

1
@R.: var = ~(0*var)Trường hợp sẽ thất bại vì varlà một loại không dấu hẹp hơn int. Có lẽ var = ~(0U*var)? (cá nhân tôi vẫn thích -1, mặc dù).
phê

1
Câu trả lời này rõ ràng hơn nhiều so với của Julian Schaub. Tuy nhiên, việc gán một số nguyên bằng chữ âm cho một loại không dấu mà không cần truyền thường dẫn đến cảnh báo trình biên dịch. Việc loại bỏ cảnh báo đòi hỏi phải có diễn viên, điều đó có nghĩa là phải chú ý đến loại mục tiêu, vì vậy bạn cũng có thể sử dụng UINT_MAX hoặc ULONG_MAX và rõ ràng thay vì dựa vào một chi tiết nhỏ trong tiêu chuẩn, điều này làm cho nhiều lập trình viên của bạn bối rối .
Adrian McCarthy

5

Miễn là bạn có #include <limits.h>một trong những bao gồm của bạn, bạn chỉ nên sử dụng

unsigned int flags = UINT_MAX;

Nếu bạn muốn bit có giá trị dài, bạn có thể sử dụng

unsigned long flags = ULONG_MAX;

Các giá trị này được đảm bảo có tất cả các bit giá trị của kết quả được đặt thành 1, bất kể số nguyên đã ký được thực hiện như thế nào.


1
các hằng số mà bạn đề xuất thực sự được xác định trong giới hạn.h - stdint.h chứa các giới hạn cho các loại số nguyên bổ sung (số nguyên có kích thước cố định, intptr_t, ...)
Christoph

5

Đúng. Như đã đề cập trong các câu trả lời khác,-1 là di động nhất; tuy nhiên, nó không phải là rất ngữ nghĩa và kích hoạt cảnh báo trình biên dịch.

Để giải quyết những vấn đề này, hãy thử trợ giúp đơn giản này:

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

Sử dụng:

unsigned a = ALL_BITS_TRUE;
uint8_t  b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;

3
Là một lập trình viên C, mã như thế này mang lại cho tôi những giấc mơ tồi tệ vào ban đêm.
jforberg

Và điều gì xảy ra khi bạn sử dụng điều này trong một bối cảnh như ALL_BITS_TRUE ^ anơi alà một số nguyên ký? Kiểu vẫn giữ nguyên số nguyên và mẫu bit (biểu diễn đối tượng) tùy thuộc vào mục tiêu có phải là phần bù 2 hay không.
Peter Cordes

Không, ALL_BITS_TRUE ^ ađưa ra một lỗi biên dịch vì ALL_BITS_TRUEmơ hồ. Nó có thể được sử dụng như thế uint32_t(ALL_BITS_TRUE) ^ a, tuy nhiên. Bạn có thể thử nó cho mình trên cpp.sh :) Ngày nay tôi muốn thêm một static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");trong operatorđể chắc chắn người dùng không cố gắng sử dụng int(ALL_BITS_TRUE). Tôi sẽ cập nhật câu trả lời.
Kim cương Python

3

Tôi sẽ không làm điều -1. Nó khá là không trực quan (ít nhất là với tôi). Việc gán dữ liệu đã ký cho một biến không dấu dường như là vi phạm trật tự tự nhiên của mọi thứ.

Trong tình huống của bạn, tôi luôn luôn sử dụng 0xFFFF . (Sử dụng đúng số lượng F cho kích thước biến của khóa học.)

[BTW, tôi rất hiếm khi thấy thủ thuật -1 được thực hiện trong mã thế giới thực.]

Thêm vào đó, nếu bạn thực sự quan tâm đến các bit riêng lẻ trong một vairable, nó sẽ là ý tưởng tốt để bắt đầu sử dụng chiều rộng cố định uint8_t, uint16_t, uint32_tloại.


2

Trên bộ xử lý IA-32 của Intel, bạn có thể ghi 0xFFFFFFFF vào thanh ghi 64 bit và nhận được kết quả mong đợi. Điều này là do IA32e (phần mở rộng 64 bit thành IA32) chỉ hỗ trợ tức thời 32 bit. Trong hướng dẫn 64 bit, tức thời 32 bit được mở rộng đăng nhập thành 64 bit.

Sau đây là bất hợp pháp:

mov rax, 0ffffffffffffffffh

Sau đây đặt 64 1 giây trong RAX:

mov rax, 0ffffffffh

Để hoàn thiện, phần sau đây đặt 32 1s vào phần dưới của RAX (còn gọi là EAX):

mov eax, 0ffffffffh

Và trên thực tế, tôi đã có các chương trình thất bại khi tôi muốn viết 0xffffffff thành biến 64 bit và thay vào đó tôi đã nhận được 0xffffffffffffffffffff. Trong C này sẽ là:

uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);

kết quả là:

x is 0xffffffffffffffff

Tôi nghĩ sẽ đăng bài này như một bình luận cho tất cả các câu trả lời nói rằng 0xFFFFFFFF giả định 32 bit, nhưng rất nhiều người đã trả lời tôi nghĩ rằng tôi đã thêm nó như một câu trả lời riêng biệt.


1
Xin chúc mừng, bạn đã tìm thấy một lỗi biên dịch!
tc.

Điều này đã được ghi nhận ở bất cứ đâu là một lỗi?
Nathan Fellman

1
Giả sử UINT64_C(0xffffffff)mở rộng đến một cái gì đó như 0xffffffffuLL, nó chắc chắn là một lỗi biên dịch. Tiêu chuẩn C chủ yếu thảo luận về các giá trị , giá trị được biểu thị 0xfffffffflà 4294967295 (không phải 36893488147419103231) và không có bất kỳ chuyển đổi nào đối với các loại số nguyên đã ký.
tc.

2

Xem câu trả lời của litb để được giải thích rất rõ ràng về các vấn đề.

Sự không đồng ý của tôi là, nói rất nghiêm túc, không có gì đảm bảo cho cả hai trường hợp. Tôi không biết bất kỳ kiến ​​trúc nào không đại diện cho giá trị không dấu 'một ít hơn hai so với sức mạnh của số bit' như tất cả các bit được đặt, nhưng đây là những gì Tiêu chuẩn thực sự nói (3.9.1 / 7 cộng lưu ý 44):

Các biểu diễn của các kiểu tích phân sẽ xác định các giá trị bằng cách sử dụng hệ thống số nhị phân thuần túy. [Lưu ý 44:] Biểu diễn vị trí cho các số nguyên sử dụng các chữ số nhị phân 0 và 1, trong đó các giá trị được biểu thị bằng các bit liên tiếp là phụ gia, bắt đầu bằng 1 và được nhân với công suất tích phân liên tiếp là 2, ngoại trừ bit với vị trí cao nhất.

Điều đó để lại khả năng cho một trong các bit là bất cứ điều gì cả.


Nói chung, chúng tôi không thể chắc chắn về giá trị của các bit đệm. Và nếu chúng ta muốn, thì chúng ta có thể gặp nguy hiểm vì chúng ta có thể tạo ra một đại diện bẫy cho chúng (và nó có thể tăng tín hiệu). Tuy nhiên, std yêu cầu char không dấu không có bit đệm và trong 4.7 / 2 trong tiêu chuẩn c ++ nói rằng việc chuyển đổi một số nguyên thành một loại không dấu, giá trị của biến không dấu kết quả là giá trị nhỏ nhất phù hợp với giá trị số nguyên nguồn, (modulo 2 ^ n, n == số bit trong loại không dấu). thì (-1) == ((2 ^ n) -1) (mod 2 ^ n). 2 ^ n-1 có tất cả các bit được đặt trong một hệ thống đánh số nhị phân thuần túy.
Julian Schaub - litb

Nếu chúng ta thực sự muốn có tất cả các bit 1 trong biểu diễn đối tượng của một loại không dấu, chúng ta sẽ cần bộ nhớ. Nhưng chúng ta có thể tạo ra một đại diện bẫy do đó :( Dù sao, việc triển khai có thể không có lý do gì để loại bỏ một chút số nguyên không dấu của chúng để nó sẽ sử dụng nó để lưu trữ giá trị của nó. Nhưng bạn có một điểm rất tốt - không có gì dừng lại một cách giải thích từ việc có một vài bit vô nghĩa ngớ ngẩn, tôi nghĩ (ngoài char / ký char / unsign char, không cần phải có). Tất nhiên là +1 :)
Julian Schaub - litb

Cuối cùng, tôi nghĩ rằng tiêu chuẩn có thể rõ ràng hơn về đại diện mà nó đề cập đến trong 4.7 / 2. Nếu nó đề cập đến biểu diễn đối tượng, thì không còn chỗ để đệm bit nữa (tôi đã thấy mọi người tranh luận như thế mà tôi không thấy có gì sai cả). Nhưng tôi nghĩ rằng nó nói về biểu diễn giá trị (bởi vì mọi thứ trong 4.7 / 2 dù sao cũng là về giá trị - và sau đó các bit đệm có thể nằm bên cạnh các bit giá trị.
Johannes Schaub - litb

1
Tiêu chuẩn dường như khá rõ ràng có các đại diện '2, bổ sung 1 và cường độ đã ký' trong tâm trí, nhưng không muốn loại trừ bất cứ điều gì. Điểm thú vị về bẫy đại diện quá. Theo như tôi có thể nói, bit tôi đã trích dẫn là định nghĩa của 'hệ thống số nhị phân thuần túy' theo như tiêu chuẩn có liên quan - bit 'ngoại trừ' ở cuối thực sự là nghi ngờ duy nhất của tôi về việc truyền -1 có được đảm bảo hay không công việc.
James Hopkin

2

Mặc dù 0xFFFF(hoặc0xFFFFFFFF , v.v.) có thể dễ đọc hơn, nhưng nó có thể phá vỡ tính di động trong mã mà nếu không thì có thể mang theo được. Ví dụ, hãy xem xét một thói quen thư viện để đếm xem có bao nhiêu mục trong cấu trúc dữ liệu có các bit nhất định được đặt (các bit chính xác được chỉ định bởi người gọi). Thường trình có thể hoàn toàn không tin vào những gì các bit đại diện, nhưng vẫn cần phải có hằng số "tất cả các bit". Trong trường hợp như vậy, -1 sẽ tốt hơn rất nhiều so với hằng số hex vì nó sẽ hoạt động với bất kỳ kích thước bit nào.

Khả năng khác, nếu một typedefgiá trị được sử dụng cho bitmask, sẽ là sử dụng ~ (bitMaskType) 0; nếu bitmask chỉ xảy ra là loại 16 bit, biểu thức đó sẽ chỉ có 16 bit được đặt (ngay cả khi 'int' sẽ là 32 bit) nhưng vì 16 bit sẽ là tất cả những gì được yêu cầu, mọi thứ sẽ ổn với điều kiện là một thực sự sử dụng loại thích hợp trong typecast.

Ngẫu nhiên, các biểu thức của biểu mẫu longvar &= ~[hex_constant]có một hình ảnh khó chịu nếu hằng số hex quá lớn để phù hợp với một int, nhưng sẽ phù hợp với một unsigned int. Nếu an intlà 16 bit thì longvar &= ~0x4000;hoặc longvar &= ~0x10000; sẽ xóa một bit của longvar, nhưng longvar &= ~0x8000;sẽ xóa bit 15 và tất cả các bit ở trên đó. Các giá trị phù hợp intsẽ có toán tử bổ sung được áp dụng cho một loại int, nhưng kết quả sẽ được ký hiệu mở rộng long, thiết lập các bit trên. Các giá trị quá lớn unsigned intsẽ áp dụng toán tử bổ sung cho kiểu long. Tuy nhiên, các giá trị nằm giữa các kích thước đó sẽ áp dụng toán tử bổ sung cho kiểu unsigned int, sau đó sẽ được chuyển đổi thành kiểu longmà không có phần mở rộng dấu.


1

Thực tế: Có

Về mặt lý thuyết: Không.

-1 = 0xFFFFFFFF (hoặc bất kỳ kích thước nào int trên nền tảng của bạn) chỉ đúng với số học bổ sung của hai. Trong thực tế, nó sẽ hoạt động, nhưng có những máy kế thừa ngoài kia (máy tính lớn của IBM, v.v.) nơi bạn đã có một bit ký hiệu thực tế thay vì biểu diễn bổ sung của hai. Giải pháp đề xuất ~ 0 của bạn nên hoạt động ở mọi nơi.


6
Tôi cũng nói vậy Nhưng sau đó tôi nhận ra mình đã sai, vì -1 đã ký luôn chuyển đổi thành max_value không dấu theo quy tắc chuyển đổi, bất kể các biểu diễn giá trị. Ít nhất, nó có trong C ++, tôi không có tiêu chuẩn C để xử lý.
Steve Jessop

6
có một sự mơ hồ. -1 không phải là 0xFFFFFF. Nhưng -1 là 0xFFFFFFFF nếu được chuyển đổi thành int không dấu (có 32 bit). Đó là những gì làm cho cuộc thảo luận này rất khó khăn tôi nghĩ. Nhiều người có những điều rất khác nhau trong tâm trí khi họ nói về những chuỗi bit đó.
Julian Schaub - litb

1

Như những người khác đã đề cập, -1 là cách chính xác để tạo một số nguyên sẽ chuyển đổi thành loại không dấu với tất cả các bit được đặt thành 1. Tuy nhiên, điều quan trọng nhất trong C ++ là sử dụng đúng loại. Do đó, câu trả lời chính xác cho vấn đề của bạn (bao gồm câu trả lời cho câu hỏi bạn đã hỏi) là đây:

std::bitset<32> const flags(-1);

Điều này sẽ luôn chứa số lượng bit chính xác mà bạn cần. Nó xây dựng một std::bitsetvới tất cả các bit được đặt thành 1 vì cùng lý do được đề cập trong các câu trả lời khác.


0

Nó chắc chắn an toàn, vì -1 sẽ luôn có tất cả các bit có sẵn, nhưng tôi thích ~ 0 hơn. -1 chỉ không có ý nghĩa nhiều cho một unsigned int. 0xFF... không tốt vì nó phụ thuộc vào độ rộng của loại.


4
"0xFF ... là không tốt bởi vì nó phụ thuộc vào độ rộng của loại" Đó là cách duy nhất để đi, tôi nghĩ vậy. Bạn phải xác định rõ ràng mỗi cờ / bit có nghĩa là gì trong chương trình của bạn. Vì vậy, nếu bạn xác định rằng bạn đang sử dụng 32 bit thấp nhất để lưu trữ cờ, bạn nên hạn chế sử dụng 32 bit đó, cho dù kích thước thực tế của int là 32 hay 64.
Juan Pablo Califano

0

Tôi nói:

int x;
memset(&x, 0xFF, sizeof(int));

Điều này sẽ luôn cung cấp cho bạn kết quả mong muốn.


4
Không phải trên các hệ thống có ký tự 9 bit!
tc.

0

Tận dụng thực tế là việc gán tất cả các bit cho một loại cho loại không dấu tương đương với việc lấy giá trị tối đa có thể cho loại đã cho
và mở rộng phạm vi câu hỏi cho tất cả loại số nguyên không dấu :

Chỉ định -1 hoạt động cho bất kỳ dấu nào loại số nguyên (int unsign int, uint8_t, uint16_t, v.v.) cho cả C và C ++.

Để thay thế, đối với C ++, bạn có thể:

  1. Bao gồm <limits> và sử dụngstd::numeric_limits< your_type >::max()
  2. Viết hàm templated tùy chỉnh (Điều này cũng sẽ cho phép kiểm tra độ tỉnh táo, nghĩa là nếu loại đích thực sự là một loại không dấu)

Mục đích có thể được thêm rõ ràng hơn, vì việc gán -1sẽ luôn cần một số nhận xét giải thích.


0

Một cách để làm cho ý nghĩa rõ ràng hơn một chút và để tránh lặp lại kiểu:

const auto flags = static_cast<unsigned int>(-1);

-6

vâng, biểu diễn được hiển thị rất chính xác như thể chúng ta làm theo cách khác, bạn sẽ yêu cầu một toán tử đảo ngược tất cả các bit nhưng trong trường hợp này logic khá đơn giản nếu chúng ta xem xét kích thước của các số nguyên trong máy

ví dụ trong hầu hết các máy, số nguyên là 2 byte = 16 bit giá trị tối đa có thể giữ là 2 ^ 16-1 = 65535 2 ^ 16 = 65536

0% 65536 = 0 -1% 65536 = 65535 mà corressponds thành 1111 ............. 1 và tất cả các bit được đặt thành 1 (nếu chúng ta xem xét các lớp dư lượng mod 65536) do đó nó rất nhiều thẳng về phía trước.

tôi đoán

không nếu bạn xem xét khái niệm này thì nó hoàn toàn phù hợp với số nguyên không dấu và nó thực sự có tác dụng

chỉ cần kiểm tra đoạn chương trình sau

int chính () {

unsigned int a=2;

cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

}

câu trả lời cho b = 4294967295 whcih là -1% 2 ^ 32 trên số nguyên 4 byte

do đó, nó hoàn toàn hợp lệ cho các số nguyên không dấu

trong trường hợp có bất kỳ báo cáo plzz khác biệt


9
Hai nhận xét: đầu tiên, bạn đã sai về kích thước của các số nguyên trên các máy của hầu hết các máy. Thứ hai, ur txt iz rất khó đọc 2 do chúng tôi sử dụng loại ngôn ngữ seckrit. Xin vui lòng cho chúng tôi tiếng Anh đơn giản .
Konrad Rudolph
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.