Kiểu dữ liệu uintptr_t là gì


Câu trả lời:


199

uintptr_tlà một kiểu số nguyên không dấu có khả năng lưu trữ một con trỏ dữ liệu. Điều đó thường có nghĩa là nó có cùng kích thước với một con trỏ.

Nó được định nghĩa tùy ý trong C ++ 11 và các tiêu chuẩn mới hơn.

Một lý do phổ biến để muốn một loại số nguyên có thể chứa loại con trỏ của kiến ​​trúc là để thực hiện các hoạt động cụ thể về số nguyên trên một con trỏ hoặc để che khuất loại của một con trỏ bằng cách cung cấp cho nó như là một "số nguyên".

Chỉnh sửa: Lưu ý rằng Steve Jessop có một số chi tiết bổ sung rất thú vị (mà tôi sẽ không ăn cắp) trong một câu trả lời khác ở đây cho bạn các kiểu mô phạm :)


53
Lưu ý rằng size_tchỉ cần đủ để giữ kích thước thứ của đối tượng lớn nhất và có thể nhỏ hơn con trỏ. Điều này sẽ được mong đợi trên các kiến ​​trúc được phân đoạn như 8086 (16 bit size_t, nhưng 32 bit void*)
MSalters

3
để đại diện cho sự khác biệt giữa hai con trỏ, bạn có ptrdiff_t. uintptr_tkhông có nghĩa cho điều đó.
jalf

3
@jalf: Để khác biệt, có, nhưng đối với khoảng cách , bạn sẽ muốn một loại không dấu.
Drew Dormann

Định nghĩa uintptr_t rõ ràng là không bắt buộc (vì vậy không chuẩn) ngay cả trong C ++ 11! cplusplus.com/reference/cstdint (Tôi đã nhận được gợi ý từ câu trả lời của Steve Jessop)
Antonio

2
@MarcusJ unsigned intthường không đủ lớn. Nhưng nó có thể đủ lớn. Loại này tồn tại đặc biệt để loại bỏ tất cả "giả định" .
Drew Dormann

239

Điều đầu tiên, tại thời điểm câu hỏi được hỏi, uintptr_tkhông có trong C ++. Nó ở C99, trong<stdint.h> , như một loại tùy chọn. Nhiều trình biên dịch C ++ 03 cung cấp tệp đó. Nó cũng có trong C ++ 11, trong <cstdint>đó một lần nữa là tùy chọn và đề cập đến C99 cho định nghĩa.

Trong C99, nó được định nghĩa là "một kiểu số nguyên không dấu với thuộc tính mà mọi con trỏ hợp lệ thành void có thể được chuyển đổi thành loại này, sau đó được chuyển đổi trở lại thành con trỏ thành void và kết quả sẽ so sánh bằng với con trỏ ban đầu".

Hãy hiểu điều này có nghĩa là những gì nó nói. Nó không nói bất cứ điều gì về kích thước.

uintptr_tcó thể có cùng kích thước với a void*. Nó có thể lớn hơn. Nó có thể hình dung nhỏ hơn, mặc dù cách triển khai C ++ như vậy là sai lầm. Ví dụ: trên một số nền tảng giả thuyết void*có 32 bit, nhưng chỉ có 24 bit không gian địa chỉ ảo được sử dụng, bạn có thể có 24 bit uintptr_tthỏa mãn yêu cầu. Tôi không biết tại sao một triển khai sẽ làm điều đó, nhưng tiêu chuẩn cho phép nó.


8
Cảm ơn vì "<stdint.h>". Ứng dụng của tôi không được biên dịch vì khai báo uintptr_t. Nhưng khi tôi đọc bình luận của bạn, tôi thêm "#include <stdint.h>" và vâng bây giờ nó hoạt động. Cảm ơn!
JavaRunner

2
Để phân bổ bộ nhớ liên kết , trong số các sử dụng khác, ví dụ?
huyền thoại2k

4
Một trường hợp sử dụng phổ biến khác của việc truyền con trỏ tới int là tạo một tay cầm mờ che giấu con trỏ. Điều này hữu ích để trả về một tham chiếu đến một đối tượng từ các API nơi bạn muốn giữ đối tượng ở chế độ riêng tư cho thư viện và ngăn các ứng dụng có quyền truy cập vào nó. Sau đó, ứng dụng buộc phải sử dụng API để thực hiện bất kỳ thao tác nào trên đối tượng
Joel Cickyham

3
@JoelCastyham: hoạt động nhưng không thực sự khác biệt so với sử dụng void*. Tuy nhiên, nó ảnh hưởng đến các hướng có thể có trong tương lai, đặc biệt là nếu bạn có thể muốn thay đổi để sử dụng thứ gì đó thực sự chỉ là một số nguyên, không phải là một con trỏ được chuyển đổi.
Steve Jessop

2
@CiroSantilli 烏坎 2016 六四 事件 Một trường hợp sử dụng phổ biến là chỉ chuyển một int cho một API có khoảng trống * cho dữ liệu chung. Tiết kiệm cho bạn các tổ hợp phím typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;. Thay vào đó : callback.dataPtr = (void*)val. Mặt khác, bạn tất nhiên có được void*và phải đưa nó trở lại int.
Francesco Dondi

19

Đó là một kiểu số nguyên không dấu chính xác kích thước của một con trỏ. Bất cứ khi nào bạn cần làm điều gì đó bất thường bằng một con trỏ - ví dụ như đảo ngược tất cả các bit (đừng hỏi tại sao) bạn chuyển nó sang uintptr_tvà thao tác nó như một số nguyên thông thường, sau đó bỏ lại.


6
Tất nhiên bạn có thể làm điều đó, nhưng tất nhiên đó sẽ là hành vi không xác định. Tôi tin rằng điều duy nhất bạn có thể làm với kết quả của việc chuyển sang uintptr_t là chuyển nó không thay đổi và bỏ lại - mọi thứ khác là UB.
sleske

Đôi khi bạn cần chơi với các bit và nó thường sẽ tạo ra các lỗi biên dịch. Một ví dụ phổ biến là thực thi bộ nhớ được căn chỉnh 16 byte cho một số ứng dụng quan trọng về hiệu suất và video. stackoverflow.com/questions/227897/ cường
Đám mây

3
@sleske điều đó không đúng. Trên các máy có kiểu tự liên kết, hai bit có ý nghĩa nhỏ nhất của con trỏ sẽ bằng 0 (vì địa chỉ là bội số của 4 hoặc 8). Tôi đã thấy các chương trình khai thác điều này để nén dữ liệu ..
saadtaame

3
@saadtaame: Tôi chỉ chỉ ra rằng đây là UB theo tiêu chuẩn C . Điều đó không có nghĩa là nó không thể được định nghĩa trên một số hệ thống - trình biên dịch và thời gian chạy được tự do xác định một hành vi cụ thể cho một cái gì đó là UB trong tiêu chuẩn C. Vì vậy, không có mâu thuẫn :-).
sleske

2
Nó không nhất thiết phải chính xác kích thước của một con trỏ. Tất cả các đảm bảo tiêu chuẩn là việc chuyển đổi một void*giá trị con trỏ thành uintptr_tvà trở lại một lần nữa mang lại một void*giá trị so sánh với con trỏ ban đầu. uintptr_tthường có cùng kích thước với void*, nhưng điều đó không được đảm bảo, cũng không có gì đảm bảo rằng các bit của giá trị được chuyển đổi có bất kỳ ý nghĩa cụ thể nào. Và không có gì đảm bảo rằng nó có thể giữ giá trị con trỏ chuyển đổi thành hàm mà không mất thông tin. Cuối cùng, nó không được đảm bảo để tồn tại.
Keith Thompson

15

Đã có nhiều câu trả lời hay cho phần "kiểu dữ liệu uintptr_t" là gì. Tôi sẽ cố gắng giải quyết "nó có thể được sử dụng để làm gì?" một phần trong bài này.

Chủ yếu cho các hoạt động bitwise trên con trỏ. Hãy nhớ rằng trong C ++, người ta không thể thực hiện các thao tác bitwise trên các con trỏ. Vì lý do xem Tại sao bạn không thể thực hiện các thao tác bitwise trên con trỏ trong C và có cách nào khác không?

Do đó, để thực hiện các thao tác bitwise trên các con trỏ, người ta sẽ cần truyền con trỏ để gõ unitpr_t và sau đó thực hiện các thao tác bitwise.

Dưới đây là ví dụ về một hàm mà tôi vừa viết để loại trừ bitwise hoặc 2 con trỏ để lưu trữ trong danh sách được liên kết XOR để chúng ta có thể di chuyển theo cả hai hướng như danh sách được liên kết đôi nhưng không bị phạt khi lưu 2 con trỏ trong mỗi nút .

 template <typename T>
 T* xor_ptrs(T* t1, T* t2)
 {
     return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
  }

1
ngoài số học bit, nó cũng tốt nếu bạn muốn có ngữ nghĩa dựa trên địa chỉ thay vì số lượng đối tượng.
Alex

4

Có nguy cơ nhận được một huy hiệu Necromancer khác, tôi muốn thêm một cách sử dụng rất tốt cho uintptr_t (hoặc thậm chí intptr_t) và đó là viết mã nhúng có thể kiểm tra được. Tôi viết phần lớn mã nhúng được nhắm mục tiêu vào các bộ xử lý khác nhau và hiện tại. Những cái này có chiều rộng xe buýt riêng khác nhau và tenilica thực sự là một kiến ​​trúc của Harvard với các bus dữ liệu và mã riêng biệt có thể có chiều rộng khác nhau. Tôi sử dụng một kiểu phát triển hướng thử nghiệm cho phần lớn mã của mình, điều đó có nghĩa là tôi thực hiện các thử nghiệm đơn vị cho tất cả các đơn vị mã tôi viết. Kiểm tra đơn vị trên phần cứng mục tiêu thực tế là một rắc rối vì vậy tôi thường viết mọi thứ trên PC dựa trên Intel trong Windows hoặc Linux bằng Ceedling và GCC. Điều đó đang được nói, rất nhiều mã nhúng liên quan đến thao tác xoay vòng và xử lý địa chỉ bit. Hầu hết các máy Intel của tôi là 64 bit. Vì vậy, nếu bạn định kiểm tra mã thao tác địa chỉ, bạn cần một đối tượng tổng quát để làm toán. Do đó, uintptr_t cung cấp cho bạn một cách gỡ lỗi mã độc lập của máy trước khi bạn thử triển khai vào phần cứng đích. Một vấn đề khác là đối với một số máy hoặc thậm chí các mô hình bộ nhớ trên một số trình biên dịch, con trỏ hàm và con trỏ dữ liệu có độ rộng khác nhau. Trên các máy đó, trình biên dịch thậm chí có thể không cho phép truyền giữa hai lớp, nhưng uintptr_t sẽ có thể giữ một trong hai.


Không chắc chắn nếu có liên quan ... Bạn đã thử "typedefs mờ" chưa? Vide: youtube.com/watch?v=jLdSjh8oqmE
shycha

@sleske Tôi ước rằng nó có sẵn trong C. Nhưng có stdint.h thì tốt hơn là không có gì. (Cũng muốn enum nơi đánh máy mạnh hơn nhưng hầu hết các trình gỡ lỗi làm tốt công việc tìm ra chúng bằng mọi cách)
bd2357
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.