2011-02-01 31 views
17

Tôi luôn tự hỏi: không phải là ptrdiff_t được cho là có thể giữ sự khác biệt của bất kỳ hai con trỏ nào theo định nghĩa? Làm thế nào đến nó không thành công khi hai con trỏ quá xa? (Tôi không chỉ vào bất kỳ ngôn ngữ cụ thể nào ... Tôi đang đề cập đến tất cả các ngôn ngữ có loại này.)ptrdiff_t quá nhỏ?

(ví dụ: trừ con trỏ có địa chỉ 1 từ con trỏ byte có địa chỉ 0xFFFFFFFF khi bạn có 32- bit con trỏ, và nó tràn bit dấu ...)

Trả lời

28

Không, nó không phải là.

$ 5,7 [expr.add] (từ n3225 - C++ 0x FCD)
Khi hai con trỏ đến các yếu tố của đối tượng cùng một mảng được trừ, kết quả là sự khác biệt của các chỉ số của hai các phần tử mảng. Loại kết quả là một loại tích phân có chữ ký thực hiện được xác định; loại này phải là cùng loại được định nghĩa là std::ptrdiff_t trong tiêu đề <cstddef> (18.2). Như với bất kỳ tràn số học khác, nếu kết quả không phù hợp trong không gian được cung cấp, hành vi là không xác định. Nói cách khác, nếu các biểu thức PQ điểm đến, tương ứng, i -thứ và j yếu tố -thứ của một đối tượng mảng, khái niệm (P)-(Q) có giá trị i − j cung cấp các giá trị phù hợp trong một đối tượng kiểu std::ptrdiff_t. Hơn nữa, nếu biểu thức P trỏ đến phần tử của đối tượng mảng hoặc một phần tử cuối cùng của đối tượng mảng và biểu thức Q trỏ đến phần tử cuối cùng của cùng đối tượng mảng, biểu thức ((Q)+1)-(P) có cùng giá trị với ((Q)-(P))+1 và dưới dạng -((P)-((Q)+1)) và có giá trị bằng 0 nếu biểu thức P điểm vượt qua phần tử cuối cùng của đối tượng mảng, mặc dù biểu thức (Q)+1 không trỏ đến phần tử của đối tượng mảng. Trừ khi cả hai con trỏ trỏ tới các phần tử của cùng một đối tượng mảng hoặc một phần tử cuối cùng của đối tượng mảng, hành vi này là không xác định.

Lưu ý số lần undefined xuất hiện trong đoạn văn. Cũng lưu ý rằng bạn chỉ có thể trừ con trỏ nếu chúng trỏ vào cùng một đối tượng.

+0

+1 Đánh bại tôi bằng một vài giây. Đẹp đào! – templatetypedef

+0

+1 Wow, tôi (rõ ràng) không bao giờ biết điều này không xác định-ness là một phần của tiêu chuẩn. Nhưng, trong trường hợp này, không phải là 'ptrdiff_t' chỉ là một 'ký hiệu size_t' được tôn vinh? – Mehrdad

+2

@templatetypedef: Tôi vui mừng, một lần, không thức dậy sau khi trận chiến đã được chiến đấu :) @Mehrdad: Tôi sẽ nói nó là :) Mặt khác, không có gì cấm việc thực hiện thư viện chuẩn mà bạn sử dụng để sử dụng 64 -bất số nguyên ngay cả trên nền tảng 32 bit. Tiêu chuẩn chỉ nói rằng không bắt buộc họ phải nỗ lực. Phần còn lại là vấn đề Chất lượng thực hiện. –

8

Không, vì không có sự khác biệt nào giữa sự khác biệt giữa "bất kỳ hai con trỏ" nào. Bạn chỉ có thể trừ con trỏ đến các phần tử của cùng một mảng (hoặc con trỏ đến vị trí vừa qua cuối mảng).

+1

Nhưng nếu bạn * có * một mảng thực sự lớn (có lẽ vì bạn * là * hạt nhân)? – Mehrdad

+2

@Mehrdad: Hạt nhân được phép tạo nhiều giả định không thể nhập. Ví dụ, mã hạt nhân Linux giả định rằng nó sẽ được biên dịch với GCC và rằng "dài" có chiều rộng giống như một con trỏ. Trên cả hai hệ thống 32 bit và 64 bit, sự khác biệt giữa hai địa chỉ sẽ phù hợp với 64 bit (vì một số bit cao trong địa chỉ 64 bit không được sử dụng). –

+0

Chờ đã, cái gì ?! Họ giả sử 'long' là kích thước của một con trỏ ?? o__o Tại sao họ không sử dụng 'ptrdiff_t' (mà tôi muốn nói là đặt cược an toàn hơn) hoặc tạo một typedef tùy chỉnh thay thế? – Mehrdad

1

Hoàn toàn có thể chấp nhận đối với ptrdiff_t có cùng kích thước với kiểu con trỏ, miễn là ngữ nghĩa tràn được xác định bởi trình biên dịch sao cho bất kỳ sự khác biệt nào vẫn còn thể hiện được. Không có gì đảm bảo rằng một ptrdiff_t phủ định có nghĩa là con trỏ thứ hai sống ở một địa chỉ thấp hơn trong bộ nhớ so với đầu tiên, hoặc ptrdiff_t đó được ký ở tất cả.

+2

Hm ... bạn có chắc chắn về "hoặc ptrdiff_t được ký ở tất cả" một phần không? – Mehrdad

+0

Không hoàn toàn, nhưng tôi không có bản sao của tiêu chuẩn trong tay, và tôi sẽ không dựa vào điều này cho bất cứ điều gì (tức là tôi sẽ sử dụng 'a

+0

@Simon: Ah được rồi, +1 dù sao, câu trả lời hay. :) – Mehrdad

2

Để thêm một trích dẫn tiêu chuẩn rõ ràng hơn, ISO 9899:1999 §J.2/1 trạng thái:

Các hành vi là undefined trong các trường hợp sau đây:

[...]

- Kết quả của việc trừ hai con trỏ không thể biểu diễn trong một đối tượng thuộc loại ptrdiff_t (6.5.6).

-1

Trên/underflow được về mặt toán học cũng xác định cho kích thước cố định nguyên số học:

(1 - 0xFFFFFFFF) % (1<<32) = 
(1 + -0xFFFFFFFF) % (1<<32) = 
1 + (-0xFFFFFFFF % (1<<32)) = 2 

này kết quả chính xác!

Cụ thể, kết quả sau khi quá/tràn là bí danh của số nguyên chính xác. Trong thực tế, mọi số nguyên không thể đại diện được đặt bí danh (không phân biệt được) với một số nguyên thể hiện được - được tính là vô cùng trong số nguyên có kích thước cố định và bạn sẽ lặp lại chính mình, tròn và tròn như mặt đồng hồ tương tự.

Số nguyên N bit đại diện cho bất kỳ số nguyên thực nào modulo 2^N. Trong C, modulo 2^N được viết dưới dạng% (1 < < 32).

Tôi tin rằng C guarrantees tính chính xác toán học của over/underflow, nhưng chỉ cho số nguyên unsigned. Ký dưới/tràn được giả định là không bao giờ xảy ra (vì lợi ích tối ưu hóa). Trong thực tế, các số nguyên đã ký là bổ sung của hai, mà không có sự khác biệt về cộng hoặc trừ, do đó, hành vi dưới/tràn chính xác là guarranteed cho các số nguyên đã ký (mặc dù không phải bằng C).

+1

* "Trong thực tế, các số nguyên đã ký là bổ sung của hai, mà không tạo ra sự khác biệt nào về phép cộng hay trừ" * ... không có nghĩa gì cả. Không xác định là không xác định - * bất cứ điều gì * có thể xảy ra. Có nghĩa là bạn không thể dựa vào bất cứ điều gì cụ thể xảy ra. – Mehrdad

+1

Câu trả lời này hoàn toàn sai. Hầu hết thời gian Tính toán sẽ được thực hiện như đã nêu nhưng trình biên dịch cũng có thể làm bất cứ điều gì nó muốn: Đây là hành vi không xác định. [Xem này] (http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html). – Arnaud

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