2015-06-21 15 views
21

Giả sử unsigned int không có biểu diễn bẫy, làm một hoặc cả hai câu lệnh được đánh dấu (A) và (B) bên dưới kích hoạt hành vi không xác định, tại sao hoặc tại sao không , và (đặc biệt nếu bạn nghĩ rằng một trong số chúng được xác định rõ ràng nhưng khác không phải là), bạn có cho rằng một khiếm khuyết trong tiêu chuẩn? Tôi chủ yếu quan tâm đến phiên bản hiện tại của tiêu chuẩn C (nghĩa là C2011), nhưng nếu điều này khác với các phiên bản cũ của tiêu chuẩn, hoặc trong C++, tôi cũng muốn biết về điều đó.Hành vi không xác định đối tượng đọc sử dụng loại không phải ký tự khi được viết lần cuối bằng cách sử dụng loại ký tự

(_Alignas được sử dụng trong chương trình này để loại bỏ bất kỳ câu hỏi của UB do sự liên kết không phù hợp. Các quy tắc tôi thảo luận trong việc giải thích của tôi, tuy nhiên, nói gì về sự liên kết.)

#include <stdlib.h> 
#include <string.h> 

int main(void) 
{ 
    unsigned int v1, v2; 
    unsigned char _Alignas(unsigned int) b1[sizeof(unsigned int)]; 
    unsigned char *b2 = malloc(sizeof(unsigned int)); 

    if (!b2) return 1; 

    memset(b1, 0x55, sizeof(unsigned int)); 
    memset(b2, 0x55, sizeof(unsigned int)); 

    v1 = *(unsigned int *)b1; /* (A) */ 
    v2 = *(unsigned int *)b2; /* (B) */ 

    return !(v1 == v2); 
} 

giải thích của tôi về C2011 là rằng (A) khiêu khích hành vi undefined nhưng (B) được xác định rõ (để lưu trữ một giá trị không xác định vào v2), bởi vì:

  • memset được định nghĩa (§7.24.6.1) để viết thư cho đối số đầu tiên của nó như là -tôi f thông qua một lvalue với loại ký tự, được cho phép cho cả hai b1b2 cho mỗi trường hợp đặc biệt ở dưới cùng của §6.5p7.

  • Đối tượng b1 có loại được khai báo, unsigned char[n]. Do đó, loại truy cập hiệu quả của nó cũng là unsigned char[n] mỗi 6,5p6. Bản tuyên bố (A) đọc b1 thông qua một biểu thức lvalue có loại là unsigned int, đó không phải là loại hiệu quả của b1 cũng không phải bất kỳ ngoại lệ nào khác trong 6,5p7, do đó hành vi không xác định.

  • Đối tượng được trỏ đến bởi b2 không có loại được khai báo. Giá trị được lưu trữ trong nó (bởi memset) là (as-if) thông qua một lvalue với loại ký tự, vì vậy trường hợp thứ hai của 6,5p6 không áp dụng. Giá trị không phải là được sao chép từ mọi nơi, vì vậy trường hợp thứ ba 6,5p6 cũng không áp dụng. Do đó, loại hiệu quả của đối tượng là loại giá trị được sử dụng cho truy cập, là unsigned int và các quy tắc của 6.5p7 đều hài lòng.

  • Cuối cùng, mỗi 6.2.6.1, giả sử unsigned int không có cơ quan đại diện bẫy, hoạt động memset đã tạo ra các đại diện của một số unsigned int giá trị không xác định trong mỗi b1b2. Do đó, nếu không (A) cũng như (B) gây ra hành vi không xác định, thì các giá trị thực tế trong v1v2 không xác định nhưng chúng bằng nhau.

Bình luận:

Các không đối xứng của các quy tắc "loại dựa trên răng cưa" (có nghĩa là, 6.5p7), cho phép một đối tượng với bất kỳ loại hiệu quả để được truy cập bởi một giá trị trái với kiểu nhân vật, nhưng không phải ngược lại, là một nguồn gây nhầm lẫn liên tục. Trường hợp thứ hai của 6.5p6 dường như đã được thêm vào đặc biệt để ngăn chặn hành vi không xác định của nó để đọc một giá trị được khởi tạo bởi memset (hoặc, cho rằng vấn đề, calloc) nhưng, bởi vì nó chỉ áp dụng cho các đối tượng không có loại khai báo, nguồn gây nhầm lẫn bổ sung.

+0

'A' rõ ràng là vi phạm bí danh; nhưng tiêu chuẩn dường như không nói rõ những gì hiệu ứng 'memset' có hiệu lực loại –

+1

Tôi đã bắt đầu [một câu hỏi khác] (http://stackoverflow.com/questions/30970251/what-is-the-effective-type- of-an-object-by-memset) đặc biệt để bao gồm loại dữ liệu có hiệu quả được ghi bởi 'memset' –

Trả lời

0

Trên một kiểm tra hời hợt, tôi đồng ý với đánh giá của bạn (A là UB, B là tốt), và có thể đưa ra một lý do cụ thể cho lý do tại sao mà nên quá (trước khi chỉnh sửa để bao gồm _Alignas()): Alignment.

char[] trên ngăn xếp có thể bắt đầu tại bất kỳ địa chỉ nào, cho dù đó là căn chỉnh hợp lệ cho số unsigned int hay không. Ngược lại, malloc() là bắt buộc để trả về bộ nhớ đáp ứng các yêu cầu căn chỉnh chặt chẽ nhất của bất kỳ loại gốc nào trên nền tảng được đề cập.

Tiêu chuẩn rõ ràng không muốn áp đặt các yêu cầu căn chỉnh trên char[] ngoài các yêu cầu của char, vì vậy nó phải để quyền truy cập loại hình phạt là có thể không được xác định.

+3

Trong C11, điều này cho biết thêm các điều khiển căn chỉnh rõ ràng, từ ngữ trong 6.5 không thay đổi - truy cập vào' unsigned char _Alignas (unsigned int) b1 [sizeof (unsigned int)] 'thông qua một lvalue với kiểu' unsigned int' vẫn chưa được xác định, khi tôi đọc tiêu chuẩn. Điều này * có thể * là một sự giám sát, hoặc nó có thể có nghĩa là sự liên kết không phải là lý do duy nhất cho tiêu chuẩn được diễn đạt như vậy. – zwol

+0

Đó là một điểm tốt. Có thể có những lý do khác, hoặc điều này có thể được để lại như một sự giám sát. Tôi chắc chắn sẽ không khẳng định rằng sự liên kết là lý do duy nhất sự khác biệt này nên tồn tại. – Novelocrat

+3

Căn chỉnh không liên quan đến răng cưa –

2

Các tác giả của tiêu chuẩn thừa nhận trong lý do rằng sẽ có thể cho việc triển khai phù hợp nhưng vô dụng. Bởi vì họ hy vọng rằng những người triển khai sẽ cố gắng làm cho việc triển khai của họ hữu ích, họ không nghĩ cần thiết phải ủy thác mọi hành vi có thể cần thiết để thực hiện triển khai phù hợp với bất kỳ mục đích cụ thể nào.

Tiêu chuẩn không áp đặt yêu cầu về hành vi của mã truy cập đối tượng được liên kết của loại mảng ký tự như một loại khác. Điều đó không có nghĩa là họ dự định rằng việc triển khai nên làm điều gì đó khác hơn là xử lý mảng là không lưu trữ trong trường hợp mã lấy địa chỉ của mảng một lần nhưng không bao giờ truy cập nó trực tiếp. Bản chất cơ bản của răng cưa là nó yêu cầu một mục được truy cập theo hai cách khác nhau; nếu một đối tượng chỉ được truy cập một cách, thì có nghĩa là không có răng cưa. Bất kỳ việc triển khai chất lượng nào được cho là phù hợp với lập trình cấp thấp sẽ hoạt động theo cách hữu ích trong trường hợp char[] chỉ được sử dụng làm bộ nhớ không được phân loại, cho dù tiêu chuẩn yêu cầu hay không và khó tưởng tượng bất kỳ mục đích hữu ích nào sẽ bị cản trở bởi việc điều trị như vậy. Mục đích duy nhất sẽ được phục vụ bằng cách có nhiệm vụ tiêu chuẩn như vậy sẽ ngăn chặn các nhà biên dịch xử lý việc thiếu một ủy nhiệm như là - trong và của chính nó - một lý do không xử lý mã đó theo cách rõ ràng hữu ích.

+0

Có, đây là ý nghĩa thông thường. Nhưng cảm giác thông thường thường thất bại khi nói đến UB và các trình biên dịch thực. Đó là lý do tại sao các lập trình viên _do_ muốn tiêu chuẩn để ủy thác những thứ thông thường như vậy. – Ruslan

+0

@Ruslan: Tiêu chuẩn nên tránh cố gắng giả vờ ủy thác tất cả mọi thứ thông thường trừ khi hoặc cho đến khi được sửa chữa hoàn toàn để loại bỏ tất cả sự phụ thuộc theo ý nghĩa thông thường. Thay vào đó, nó nên * công khai tuyên bố * sự phụ thuộc của nó theo nghĩa thông thường với một cái gì đó tương tự như sửa đổi thứ chín cho Hiến pháp, và cũng cho các quan sát liên quan rằng nó presupposes một người đạo đức và đạo đức, và hoàn toàn không phù hợp cho bất kỳ loại nào khác. – supercat

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