2011-12-23 41 views
17

Ý nghĩa của việc chuyển đổi giá trị số nguyên thành một số void* hoặc ngược lại từ một điểm bộ nhớ? Sự hiểu biết của tôi là void* là một địa chỉ cho một khối bộ nhớ có độ dài không xác định.
Điều này có vẻ giống như so sánh táo với cam.ý nghĩa của việc chuyển đổi int thành void * hoặc ngược lại là gì?

int myval = 5; 
void* ptr = (void*)myval; 
printf("%d",(int)ptr); 

Tôi nhận thấy rằng tôi nên đưa ra ngữ cảnh chính xác nơi sử dụng điều này.

int main(int argc, char* argv[]) { 
long  thread; /* Use long in case of a 64-bit system */ 
pthread_t* thread_handles; 

/* Get number of threads from command line */ 
if (argc != 2) Usage(argv[0]); 
thread_count = strtol(argv[1], NULL, 10); 
if (thread_count <= 0 || thread_count > MAX_THREADS) Usage(argv[0]); 

thread_handles = malloc (thread_count*sizeof(pthread_t)); 

for (thread = 0; thread < thread_count; thread++) 
    pthread_create(&thread_handles[thread], NULL, Hello, (void*) thread); 

printf("Hello from the main thread\n"); 

for (thread = 0; thread < thread_count; thread++) 
    pthread_join(thread_handles[thread], NULL); 

free(thread_handles); 
return 0; 
} /* main */ 

/*-------------------------------------------------------------------*/ 
void *Hello(void* rank) { 
long my_rank = (long) rank; /* Use long in case of 64-bit system */ 

printf("Hello from thread %ld of %d\n", my_rank, thread_count); 

return NULL; 
} /* Hello */ 

Mã này là từ sách của Peter Pachecho về lập trình song song.

+0

Bản sao có thể có của: http://stackoverflow.com/questions/3568069/is-it-safe-to-cast-an-int-to-void-pointer-and-back-to-int-again –

+1

Đó là thậm chí tệ hơn .. - ít nhất là so sánh táo với khoai tây – alk

Trả lời

17

Đúc int đến void * là khá vô nghĩa và không nên được thực hiện vì bạn sẽ cố gắng truyền không trỏ đến con trỏ. Trích dẫn số C99 standard, mục 6.3.2.3 mục 5:

Một số nguyên có thể được chuyển đổi thành bất kỳ loại con trỏ nào. Ngoại trừ như trước đây được chỉ định, kết quả được xác định thực hiện, có thể không được căn chỉnh chính xác, có thể không trỏ đến một thực thể thuộc loại được tham chiếu và có thể là đại diện bẫy.

Bạn thể cast int *-void * (bất kỳ con trỏ là mui trần để void * mà bạn có thể nghĩ đến như "kiểu cơ sở" của tất cả các con trỏ).

Đúc void * để int không phải là di động và có thể hoàn toàn sai tùy thuộc vào nền tảng mà bạn sử dụng (ví dụ void * có lẽ 64 bit rộng và int có thể chỉ có 32 bit). Trích dẫn lại tiêu chuẩn C99, phần 6.3.2.3 mục 6:

Bất kỳ loại con trỏ nào cũng có thể được chuyển thành kiểu số nguyên. Ngoại trừ được chỉ định trước đó, kết quả được xác định thực hiện. Nếu kết quả không thể được thể hiện trong loại số nguyên, hành vi là không xác định. Kết quả không cần nằm trong phạm vi giá trị của bất kỳ loại số nguyên nào.

Để giải quyết vấn đề này, một số nền tảng cung cấp uintptr_t cho phép bạn coi con trỏ là giá trị số có chiều rộng thích hợp.

3

Cả hai con trỏ void* (hoặc bất kỳ con trỏ nào cho vấn đề đó) và int, gần như là số. Chúng có thể có các bit khác nhau, nhưng không chắc rằng con trỏ nhỏ hơn int, do đó làm cho hoạt động có thể đảo ngược. Tất nhiên, nó là bất hợp pháp và bạn không bao giờ nên dereference con trỏ mà không có vị trí hợp lệ để trỏ đến.

2

Điều này khá giống với việc so sánh táo và cam. Cách duy nhất mà mã này hoạt động là bởi vì bạn đang chuyển nó qua lại một cách rõ ràng.

Trình biên dịch C chỉ sao chép các byte từ int vào không gian cho con trỏ và ngược lại, giống như cách bạn có thể giữ một char trong một int.

Điều này thậm chí có thể khiến số của bạn bị sai lầm nếu vì lý do nào đó 'int' dài hơn 'void *' trên nền tảng của bạn.

Nếu bạn thực sự muốn có số chuyển đổi từ số nguyên thành con trỏ và ngược lại, hãy xem intptr_t. Loại đó thực sự được thiết kế để lưu trữ cả số nguyên và con trỏ.

1

Rất hữu ích khi có thể truyền con trỏ tới loại số nguyên cho số học con trỏ. Ví dụ: macro offsetof(), tính toán độ lệch của thành viên trong cấu trúc, cần loại chuyển đổi này. Tuy nhiên, phải đảm bảo rằng loại nguyên thủy được sử dụng cho điều này có thể xử lý một con trỏ: ở đây, ví dụ 64 Linux, khi sử dụng gcc, đây không phải là trường hợp: void * có kích thước 8, một int có kích thước 4.

vĩ mô offsetof được định nghĩa như vậy:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 

Nó được sử dụng nổi bật trong thực hiện danh sách gấp đôi liên kết hạt nhân của Linux, rất đơn giản định nghĩa là:

struct list_head { struct list_head *prev, *next; } 

struct này được nhúng vào trong các cấu trúc hạt nhân, như trong:

struct something { 
    //... 
    struct list_head list; 
    //... 
} 

Khi đi bộ một danh sách, mã cần phải lấy một con trỏ đến cấu trúc mã hóa.Này được thực hiện như thế này (định nghĩa giản thể):

#define container_of(ptr, type, member) \ 
    (type *)((char *)ptr - offsetoff(type, member)) 
#define list_entry(list, type, member) \ 
    container_of(list, type, member) 

Và trong các mã, bạn sẽ rất thường thấy:

struct something *s; 
list_for_each(listptr, somehead) { 
    s = list_entry(listptr, struct something, list); 
    //... 
} 

mà sẽ chỉ đơn giản là không thể thực hiện được mà không loại số học vĩ mô và con trỏ. Nhưng nó rất phụ thuộc vào toolchain.

+0

Điều này có liên quan gì đến 'offsetof'. Ngoài ra, 'offsetof' là nền tảng được định nghĩa, việc thực hiện trình biên dịch là miễn phí để thực hiện nó theo cách nào nó phù hợp nhất. Ví dụ: gcc thực hiện điều này bằng nội tại. –

1

Nó giống như so sánh táo với cam nếu có bất kỳ nỗ lực nào được thực hiện để so sánh. Nhưng không có. Về cơ bản, trong phần lớn các kiến ​​trúc trên mạng, một int có thể được đúc thành một con trỏ rỗng mà không mất bất kỳ thông tin nào và một con trỏ void cũng có thể được đưa trở lại một int, một lần nữa mà không mất bất kỳ thông tin nào. Tất nhiên, bạn không nên thử dereferencing con trỏ đó, bởi vì nó không trỏ đến bất kỳ vị trí bộ nhớ có ý nghĩa nào: nó chỉ là một biến chứa cùng mẫu bit như mẫu bit mà số nguyên được sử dụng để chứa trước diễn viên.

+0

Không đúng là không mất thông tin. Tiêu chuẩn C99 nói, "Bất kỳ loại con trỏ nào cũng có thể được chuyển đổi thành một kiểu số nguyên. Trừ khi được xác định trước đó, kết quả được xác định thực hiện. Nếu kết quả không thể được biểu diễn trong kiểu số nguyên, hành vi không xác định. trong phạm vi giá trị của bất kỳ loại số nguyên nào. " – bobbymcr

+0

Xin lỗi, tôi đã chỉnh sửa câu trả lời của mình và sửa lỗi đó. (Tôi đã chỉnh sửa một chút về C99, và từ 'rộng lớn' trong thuật ngữ 'đại đa số'.) –

+0

"Đa số lớn" có lẽ thậm chí là sai ngày hôm nay, và sẽ ít có khả năng hơn trong tương lai. Kiến trúc hiện đại chủ yếu là với 64 con trỏ và 32 bit 'int'. –

3

Chuẩn C xác định rằng nó phải có khả năng chuyển đổi một con trỏ void thành một kiểu tích phân sao cho việc chuyển đổi kiểu tích phân trở lại một con trỏ void sẽ mang lại cùng một con trỏ. Tôi không chắc chắn nếu nó đòi hỏi rằng chuyển đổi một con trỏ null để một số nguyên mang lại giá trị bằng không, hoặc nếu chỉ số 0 chữ số được công nhận là đặc biệt. Trên nhiều triển khai, giá trị số nguyên đại diện cho địa chỉ phần cứng, nhưng tiêu chuẩn không đảm bảo như vậy. Nó sẽ hoàn toàn có thể là trên phần cứng bao gồm một cái bẫy đặc biệt cho địa chỉ phần cứng 0x12345678, chuyển đổi con trỏ thành số nguyên sẽ trừ 0x12345678 từ địa chỉ phần cứng và chuyển đổi số nguyên thành con trỏ sẽ thêm 0x12345678 vào (do đó giá trị nguyên của số không sẽ đại diện cho một con trỏ null).

Trong nhiều trường hợp, đặc biệt khi phát triển cho bộ điều khiển nhúng, nhà cung cấp trình biên dịch sẽ chỉ định rõ địa chỉ phần cứng nào sẽ được truy cập khi chuyển đổi giá trị số nguyên cụ thể thành loại con trỏ. Trên các bộ xử lý có một không gian địa chỉ tuyến tính đơn lẻ, việc chuyển đổi một giá trị số nguyên 0x12345678 thành một con trỏ thường sẽ sinh ra một con trỏ trỏ đến địa chỉ 0x12345678; tài liệu tham khảo phần cứng sẽ chỉ ra nếu có bất cứ điều gì đặc biệt về địa điểm đó. Trên các bộ xử lý có nhiều không gian địa chỉ 'thú vị', có thể cần phải sử dụng một thứ khác với địa chỉ phần cứng làm con trỏ. Ví dụ, trên các máy tính PC IBM cổ, bộ đệm hiển thị được ánh xạ tại địa chỉ phần cứng 0xB8000, nhưng trong hầu hết các trình biên dịch, địa chỉ sẽ được biểu diễn dưới dạng (ngắn xa *) 0xB8000000.

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