2014-11-07 15 views
12

Các dòng sau (tinh khiết c) biên dịch sạch trên cửa sổ (win7 64 bit + CodeBlocks 13 + mingw32) và debian (khò khè 32 bit + CodeBlocks 10 + gcc) nhưng tăng cảnh báo về kali (64 bit + codeblocks + gcc). Có ý kiến ​​gì không? Tôi có nghĩa là, tại sao tôi nhận được cảnh báo này, mặc dù cùng một dòng biên dịch w/o bất kỳ cảnh báo trên windows & debian?Tại sao tôi nhận được lỗi "truyền từ con trỏ đến số nguyên có kích thước khác"?

void* foo(void *dst, ...) { 
    // some code 
    unsigned int blkLen = sizeof(int); // this line ok. 
    unsigned int offset = (unsigned int) dst % blkLen; // warning here! 
    // some code cont... 
} 

Thông điệp trong CodeBlocks là: "lỗi: cast từ con trỏ đến số nguyên kích thước khác nhau [-Werror = con trỏ-to-int-cast]"

lưu ý: tùy chọn trình biên dịch của tôi là -std=c99 -Werror -save-temps (tương tự trên cả ba hệ thống).

chỉnh sửa 2: Mặc dù tôi đã quản lý để biên soạn cảnh báo bằng cách sử dụng các dòng tiền xử lý bên dưới, @Keith Thompson (xem bên dưới) có một điểm quan trọng về vấn đề này. Vì vậy, quyết định cuối cùng của tôi là sử dụng uintptr_t sẽ là một lựa chọn tốt hơn.

chỉnh sửa 1: Cảm ơn mọi người đã trả lời. Như tất cả các lưu ý trả lời, vấn đề là một vấn đề 32 bit vs 64 bit. Tôi đã chèn dòng tiền xử lý sau đây:

#if __linux__ // or #if __GNUC__ 
    #if __x86_64__ || __ppc64__ 
     #define ENVIRONMENT64 
    #else 
     #define ENVIRONMENT32 
    #endif 
#else 
    #if _WIN32 
     #define ENVIRONMENT32 
    #else 
     #define ENVIRONMENT64 
    #endif 
#endif // __linux__ 

#ifdef ENVIRONMENT64 
    #define MAX_BLOCK_SIZE unsigned long long int 
#else 
    #define MAX_BLOCK_SIZE unsigned long int 
#endif // ENVIRONMENT64 

và sau đó thay thế dòng vấn đề như:

unsigned int offset = (MAX_BLOCK_SIZE) dst % blkLen; 

Bây giờ, tất cả mọi thứ có vẻ OK.

+0

trên một số hệ thống/biên dịch, kích thước của một int là khác biệt so với kích thước của một con trỏ. Tuy nhiên, đối với một hệ thống điển hình, giá trị bù trừ sẽ là 0 ... 3. do đó, một chút đúc kết quả của modulo để unsigned int sẽ khắc phục vấn đề. – user3629249

Trả lời

17

Lý do cảnh báo là trình biên dịch nghi ngờ bạn có thể đang cố gắng làm tròn một con trỏ qua int và ngược lại. Đây là thực tế phổ biến trước sự ra đời của các máy 64 bit và nó không an toàn hoặc hợp lý. Tất nhiên ở đây trình biên dịch rõ ràng có thể thấy rằng bạn không làm điều này, và nó sẽ là tốt đẹp nếu nó đủ thông minh để tránh cảnh báo trong các trường hợp như thế này, nhưng nó không phải.

Một thay thế sạch mà tránh được cảnh báo, và một vấn đề nastier nhiều kết quả sai khi giá trị chuyển đổi là tiêu cực, là:

unsigned int offset = (uintptr_t) dst % blkLen; 

Bạn sẽ cần phải bao gồm stdint.h hoặc inttypes.huintptr_t sẵn.

+0

Cùng một vấn đề: http://stackoverflow.com/a/21323738/1959808 –

2

Có thể vì trên kiến ​​trúc 64 bit, con trỏ dài 64 bit và chỉ có một bit dài 32 bit?

Bạn nên cố gắng

void* foo(void *dst, ...) { 
    // some code 
    unsigned int blkLen = sizeof(int); // this line ok. 
    uintptr_t offset = (uintptr_t) dst % blkLen; // warning here! 
    // some code cont... 
} 
1

Tôi nghĩ rằng bạn nhận được cảnh báo vì kích thước của int phụ thuộc vào implemetations ví dụ int có thể dài 2 byte hoặc 4 byte dài. Đây có thể là lý do cho cảnh báo (Vui lòng sửa tôi nếu tôi sai). Nhưng anyways tại sao bạn đang cố gắng để làm một modulo trên một con trỏ.

+1

modulo trên một con trỏ làm cho tinh thần để kiểm tra sự liên kết. –

+0

Trên nhiều ** nhưng không phải tất cả ** hệ thống. Xem ví dụ Cray trong câu trả lời của Keith, nơi nó sẽ không hoạt động. –

3

Vì việc đúc void * đến unsigned int chính xác là những gì cảnh báo được dự định bắt vì không an toàn. Con trỏ có thể là 64 bit và int có thể là 32 bit. Đối với bất kỳ nền tảng cụ thể nào, sizeof(unsigned int) không được đảm bảo là sizeof(void *).Thay vào đó, bạn nên sử dụng uintptr_t.

9

Sự cố là việc chuyển đổi con trỏ void* thành unsigned int vốn không thể di chuyển được.

Sự khác biệt có thể về kích thước chỉ là một phần của sự cố. Phần đó của vấn đề có thể được giải quyết bằng cách sử dụng uintptr_t, một loại được xác định trong <stdint.h><inttypes.h>. uintptr_t được đảm bảo đủ rộng để chuyển đổi void* thành uintptr_t và ngược lại sẽ mang lại giá trị con trỏ ban đầu (hoặc ít nhất giá trị con trỏ so với giá trị ban đầu). Ngoài ra còn có một loại intptr_t, được ký; thường là các loại unsigned có ý nghĩa hơn cho loại điều này. uintptr_tintptr_t không được bảo đảm để tồn tại, nhưng chúng sẽ tồn tại trên bất kỳ triển khai (C99 hoặc mới hơn) nào có các loại số nguyên thích hợp. Tuy nhiên, ngay cả khi bạn có một loại số nguyên đủ lớn để giữ con trỏ được chuyển đổi, kết quả không nhất thiết có ý nghĩa đối với bất kỳ điều gì khác ngoài việc chuyển đổi trở lại con trỏ.

Tiêu chuẩn C nói, trong một chú thích không quy chuẩn, rằng:

The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment.

mà không phải là hữu ích nếu bạn tình cờ biết rằng cấu trúc giải quyết được.

Có vẻ như bạn đang cố xác định mức chênh lệch của đối số void* liên quan đến bội số thấp tiếp theo của blkLen; nói cách khác, bạn đang cố gắng xác định cách giá trị con trỏ là được căn chỉnh đối với blkLen khối bộ nhớ được kích thước.

Nếu bạn tình cờ biết rằng đó là điều hợp lý để thực hiện trên hệ thống bạn đang sử dụng, điều đó là tốt. Nhưng bạn nên lưu ý rằng các phép tính số học trên các số nguyên do các chuyển đổi con trỏ vẫn vốn không di động.

Ví dụ cụ thể: Tôi đã làm việc trên các hệ thống (máy vectơ màu vàng) trong đó con trỏ void* là địa chỉ máy 64 bit (trỏ đến từ 64 bit), với bù đắp byte 3 bit được chèn bởi phần mềm vào một cách khác không sử dụng đơn đặt hàng cao 3 bit. Chuyển đổi một con trỏ thành một số nguyên chỉ cần sao chép biểu diễn. Bất kỳ số học số nguyên trên một số nguyên như vậy có khả năng mang lại kết quả vô nghĩa trừ khi nó có đại diện này (thừa nhận kỳ lạ) vào tài khoản.

Kết luận:

  1. Bạn chắc chắn nên sử dụng uintptr_t chứ không phải là chơi thủ đoạn tiền xử lý để xác định số nguyên loại mà bạn có thể sử dụng. Người triển khai trình biên dịch của bạn đã thực hiện công việc xác định một kiểu số nguyên có thể giữ một giá trị con trỏ được chuyển đổi một cách an toàn. Không cần phải tái tạo lại cái bánh xe đặc biệt đó. (Caveat: <stdint.h> đã được thêm vào C theo tiêu chuẩn ISO 1999. Nếu bạn đang mắc kẹt bằng cách sử dụng một trình biên dịch cổ đại mà không thực hiện nó, bạn vẫn có thể cần phải sử dụng một số loại hacks #ifdef.Nhưng tôi vẫn đề nghị sử dụng uintptr_t nếu có, bạn có thể kiểm tra __STDC_VERSION__ >= 199901L để kiểm tra sự phù hợp của C99 - mặc dù một số trình biên dịch có thể hỗ trợ <stdint.h> mà không hỗ trợ đầy đủ C99.)

  2. Bạn cần phải nhận thức rằng chuyển đổi một con trỏ đến một số nguyên và chơi với giá trị của nó là không cầm tay. Đó không phải là để nói rằng bạn không nên làm điều đó; một trong những điểm mạnh lớn nhất của C là khả năng hỗ trợ mã không di động khi đó là những gì bạn cần.

+0

Đây có thể là ** nhận xét có giá trị nhất ** liên quan đến vấn đề. Cảm ơn rất nhiều. Tôi sẽ tính đến điểm của bạn và chỉnh sửa lại câu hỏi, cho tất cả những người theo dõi để sử dụng nó. – ssd

0

Bạn đã tạo macro nhưng bạn không nghĩ rằng nó vẫn sai. Bởi vì con trỏ của bạn sẽ được chuyển đổi sang unsigned dài int dài hoặc unsigned dài int mà sẽ là 32 bit và 64 bit trong 86x và 64x OS nhưng biến bù đắp của bạn là unsigned int đó là 32bit trong 64x và 86x OS. Vì vậy, tôi nghĩ bạn nên chuyển đổi offset cũng thành macro tương ứng.

Hoặc chỉ đơn giản là bạn có thể chuyển đổi con trỏ thành dài (tức là không ký int thành dài) và bù đắp thành dài (tức là không ký int đến dài).

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