2009-08-20 8 views
8

Đây là những gì tôi cung cấp tại một cuộc phỏng vấn ngày hôm nay.Điều gì là sai với chức năng C này để tìm sự kết thúc của một máy trong thời gian chạy?

int is_little_endian(void) 
{ 
    union { 
     long l; 
     char c; 
    } u; 

    u.l = 1; 

    return u.c == 1; 
} 

phỏng vấn của tôi nhấn mạnh rằng cl đang không được bảo đảm để bắt đầu tại cùng một địa chỉ và do đó, sự kết hợp nên được thay đổi để nói char c[sizeof(long)] và giá trị trả về nên được thay đổi để u.c[0] == 1.

Có đúng là các thành viên của công đoàn có thể không bắt đầu tại cùng một địa chỉ không?

Trả lời

6

Bạn đúng trong đó "các thành viên của liên minh có thể bắt đầu ở cùng một địa chỉ". Phần có liên quan của số Standard là (6.7.2.1 đoạn 13):

Kích thước của công đoàn đủ để chứa thành viên lớn nhất. Giá trị của nhiều nhất một trong các thành viên có thể được lưu trữ trong một đối tượng công đoàn bất cứ lúc nào. Một con trỏ đến một đối tượng công đoàn, được chuyển đổi phù hợp, trỏ tới từng thành viên của nó (hoặc nếu một thành viên là một trường bit, sau đó đến đơn vị mà nó cư trú) và ngược lại.

Về cơ bản, địa chỉ xuất phát của công đoàn được đảm bảo giống như địa chỉ xuất phát của từng thành viên. Tôi tin (vẫn đang tìm kiếm tài liệu tham khảo) rằng long được đảm bảo lớn hơn char. Nếu bạn giả định điều này, thì giải pháp của bạn nên * hợp lệ.

* Tôi vẫn còn một chút không chắc chắn do một số từ ngữ thú vị xung quanh biểu diễn số nguyên và, đặc biệt là các loại số nguyên đã ký. Đọc kỹ 6.2.6.2 điều khoản 1 & 2.

8

Tôi không chắc chắn về các thành viên của công đoàn, nhưng SO came to the rescue.

Vui lòng cung có thể được viết tốt hơn như:

int is_bigendian(void) { 
    const int i = 1; 
    return (*(unsigned char*)&i) == 0; 
} 

Ngẫu nhiên, C FAQ cho thấy cả hai phương pháp: How can I determine whether a machine's byte order is big-endian or little-endian?

+0

Tôi tin rằng việc truyền con trỏ lông là hành vi không xác định về mặt kỹ thuật, nhưng tôi không thể trích dẫn bất cứ điều gì, và nó chắc chắn sẽ hoạt động trên hầu hết các máy. –

+2

Tôi sẽ ngạc nhiên nếu nó không được xác định; nếu không làm thế nào memcpy và mã serialization nhất làm việc? – Crashworks

+2

@ Chris Tôi tin rằng bạn đã đảo ngược.Chuyển đổi từ một 'char *' thành 'int *' có thể gây ra hành vi không xác định. Tôi có một bản sao của dự thảo WG14/N1124 và nếu mọi thứ không thay đổi kể từ đó: * Khi một con trỏ tới một đối tượng được chuyển đổi thành con trỏ thành kiểu ký tự, kết quả sẽ trỏ đến byte địa chỉ thấp nhất là đối tượng. * (p.47, http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf) –

1

Tiêu chuẩn nói offsets cho mỗi mục trong một liên minh đang thực hiện xác định.

Khi một giá trị được lưu trữ trong một thành viên của một đối tượng kiểu công đoàn, các byte của các đại diện đối tượng mà không tương ứng với thành viên đó nhưng tương ứng với các thành viên khác có giá trị ed fi unspeci. ISO/IEC 9899:1999 Representation of Types 6.5.6.2, para 7 (pdf file)

Do đó, tùy thuộc vào trình biên dịch để lựa chọn nơi để đặt các char tương đối so với dài trong union- họ không được đảm bảo để có cùng địa chỉ.

+4

Có một ngoại lệ ở đây. Xa hơn một chút (6.7.2.1 para 13): "Kích thước của một union là đủ để chứa lớn nhất của các thành viên của nó. Giá trị của nhiều nhất một trong các thành viên có thể được lưu trữ trong một đối tượng công đoàn bất cứ lúc nào. đối tượng công đoàn, được chuyển đổi phù hợp, trỏ đến từng thành viên của nó_ (hoặc nếu một thành viên là một trường bit, sau đó đến đơn vị mà nó cư trú) và ngược lại. " Về cơ bản, địa chỉ bắt đầu của liên minh được đảm bảo giống như địa chỉ bắt đầu của mỗi thành viên. –

+0

Điểm tốt, tôi sẽ ngừng can thiệp với câu hỏi của fbrereton. Bây giờ tôi đang bối rối, bởi vì nếu bạn đúng, thì mã trong câu hỏi sẽ hoạt động. –

+0

Mã OP là tốt: Xem http://stackoverflow.com/questions/891471/union-element-alignment –

3

Khi mã của bạn có thể hoạt động trong nhiều trình biên dịch, người phỏng vấn là đúng - cách căn chỉnh các trường trong một liên kết hoặc cấu trúc hoàn toàn tùy thuộc vào trình biên dịch và trong trường hợp này, char có thể được đặt ở đầu hoặc kết thúc". Mã của người phỏng vấn không còn chỗ cho nghi ngờ và được đảm bảo để làm việc.

0

Tôi có một câu hỏi về vấn đề này ...

thế nào là

uc [0] == gì

hợp lệ đưa ra:

union { 
    long l; 
    char c; 
} u; 

như thế nào [0] Công việc trên một char?

Dường như với tôi, nó sẽ tương đương với: (* u.c + 0) == bất cứ điều gì, mà sẽ là, tốt, crap, xem xét giá trị của u.c, được coi là một con trỏ, sẽ là crap.

(Trừ khi có lẽ, khi nó xảy ra với tôi bây giờ, một số mã html tào lao ăn một dấu trong câu hỏi ban đầu ...)

+1

Người phỏng vấn nói rằng 'char c;' nên là 'char c [sizeof (long)];', do đó 'u.c [0]' sẽ hợp lệ. –

+0

Ah, ok, điều đó có ý nghĩa. Chúa Giêsu quan sát hút. – smcameron

+0

Tôi đã làm điều đó: int x = 0x01020304; unsigned char * x = (char *) & x; return x [0] == 0x01; – smcameron

0

Trong khi người phỏng vấn là đúng và điều này không được bảo đảm để làm việc bởi spec, không ai trong số các câu trả lời khác được đảm bảo để làm việc, hoặc, như dereferencing một con trỏ sau khi đúc nó vào loại khác mang lại hành vi không xác định.Trong thực tế, điều này (và các câu trả lời khác) sẽ luôn hoạt động, vì tất cả các trình biên dịch cho phép đúc giữa con trỏ đến công đoàn và con trỏ thành viên liên minh một cách minh bạch - nhiều mã cổ sẽ không hoạt động nếu Họ đã không.

0

sửa lỗi nếu tôi sai nhưng biến cục bộ không được khởi tạo thành 0;

đây không phải là tốt hơn:

union { 
    long l; 
    char c; 
} u={0,}; 
0

Một điểm chưa được đề cập là tiêu chuẩn cho phép một cách rõ ràng cho khả năng rằng số nguyên đại diện có thể chứa các bit đệm. Cá nhân tôi muốn ủy ban tiêu chuẩn sẽ cho phép một cách dễ dàng tốt đẹp cho một chương trình để xác định hành vi mong đợi nhất định, và yêu cầu bất kỳ trình biên dịch phải tôn trọng các thông số kỹ thuật hoặc từ chối biên dịch; mã bắt đầu với thông số "số nguyên không được có bit đệm" sau đó sẽ có quyền giả định rằng đó là trường hợp.

Vì vậy, nó sẽ hoàn toàn hợp pháp (dù là lẻ) để triển khai lưu trữ giá trị 35 bit long dưới dạng bốn ký tự 9 bit ở định dạng lớn, nhưng sử dụng LSB của byte đầu tiên bit. Trong quá trình thực hiện như vậy, việc lưu trữ 1 vào một long có thể làm cho tính chẵn lẻ của từ tổng thể trở nên kỳ quặc, do đó hấp dẫn việc triển khai để lưu trữ một bit 1 vào bit chẵn lẻ. Để chắc chắn, hành vi đó sẽ là kỳ quặc, nhưng nếu kiến ​​trúc sử dụng đệm là đủ đáng chú ý để biện minh cho các quy định rõ ràng trong tiêu chuẩn, mã sẽ phá vỡ trên các kiến ​​trúc đó thực sự không thể được coi là "di động".

Mã sử ​​dụng union phải hoạt động chính xác trên tất cả các kiến ​​trúc có thể được mô tả đơn giản là "người lớn" hoặc "nhỏ" và không sử dụng các bit đệm. Nó sẽ là vô nghĩa đối với một số kiến ​​trúc khác (và thực sự là các thuật ngữ "big-endian" và "little-endian" cũng có thể vô nghĩa).