2017-09-06 25 views
18

Các chương trình sau đây khiêu khích lỗi segmentation có hệ thống do hành vi undefined (cố gắng để sửa đổi một chuỗi chữ):Tại sao không trình biên dịch C cảnh báo về các loại không tương thích với chuỗi ký tự?

int main() { 
    char *s = "immutable"; 
    s[0] = 'a'; 
    return 0; 
} 

Tuy nhiên, có vẻ là hoàn toàn không có cách nào để nói GCC/Clang phát ra thậm chí cảnh báo nhỏ về nó (-Wall -Wextra -pedantic -std=c11 không làm gì cả).

Đặc biệt đối với người mới bắt đầu, loại tình huống này sẽ hữu ích khi thông báo. Ngay cả đối với người mới bắt đầu không, trong một số tình huống hơi ít rõ ràng nó có thể hữu ích:

void f(char *s) { 
    s[0] = '0'; 
} 

int main() { 
    char *s = "immutable"; 
    f("literal"); // oops 
    f(s); // oops 
    return 0; 
} 

Bên cạnh đó, điều này sẽ giúp thực thi một số const -culture trong lập trình C.

Tại sao các trường hợp này được bỏ qua cố ý? Liệu tiêu chuẩn này có chủ động nghiêm cấm việc chẩn đoán được phát ra trong các trường hợp như vậy hay chủ yếu là khả năng tương thích ngược (cố gắng thực thi chúng ngay bây giờ sẽ tạo ra quá nhiều cảnh báo)?

+0

Nó không được bật theo mặc định bởi vì thật đáng buồn vẫn còn rất nhiều mã cũ được viết theo cách không đúng. Một số thậm chí trước khi thêm 'const' vào C. – StoryTeller

+1

Bật cảnh báo như vậy theo mặc định sẽ dẫn đến báo động mệt mỏi. Nó sẽ được tốt đẹp để có trình biên dịch cảnh báo cho điều này, nhưng có quá nhiều mã cũ mà sẽ đi qua này trong khi vẫn đang được mã chính xác. – Art

Trả lời

16

TL; DR Trình biên dịch C không cảnh báo, vì chúng không "thấy" sự cố ở đó. Theo định nghĩa, các chuỗi ký tự C không được kết thúc bằng các mảng char. Chỉ tuyên bố rằng,

[...] Nếu chương trình tìm cách sửa đổi một mảng như vậy, hành vi là không xác định.

Vì vậy, trong quá trình biên soạn, nó không được biết đến trình biên dịch rằng một mảng char nên cư xử như một chuỗi chữ hoặc chuỗi. Chỉ nỗ lực sửa đổi mới là bị cấm.

liên quan đọc: Đối với bất cứ ai quan tâm, xem Why are C string literals read-only?

Điều đó nói rằng, tôi không phải là rất chắc chắn cho dù đây là một tốt tùy chọn , nhưng gcc-Wwrite-strings tùy chọn.

Trích dẫn các online manual,

-Wwrite-strings

Khi biên dịch C, cung cấp cho chuỗi hằng số kiểu const char[length] để sao chép địa chỉ của một thành const char * con trỏ không tạo ra một cảnh báo. Các cảnh báo này giúp bạn tìm thấy tại mã thời gian biên dịch có thể cố gắng viết vào một hằng số chuỗi, nhưng chỉ khi bạn đã rất cẩn thận về việc sử dụng const trong khai báo và nguyên mẫu. Nếu không, nó chỉ là một mối phiền toái. Đây là lý do tại sao chúng tôi không thực hiện yêu cầu -Wall các cảnh báo này.

Vì vậy, nó tạo cảnh báo bằng cách sử dụng cách backdoor.

Theo định nghĩa, C chuỗi ký tự (tức là, ký tự chuỗi ký tự) là char mảng có trình kết thúc bằng không. Tiêu chuẩn không yêu cầu họ phải là const đủ điều kiện.

Ref: C11, chương

Trong giai đoạn dịch 7, một byte hoặc mã có giá trị không được gắn cho mỗi nhân vật chuỗi multibyte là kết quả của một chuỗi chữ hoặc literals. Chuỗi ký tự multibyte sau đó được sử dụng để khởi tạo một mảng thời lượng lưu trữ tĩnh và độ dài chỉ đủ để chứa chuỗi. Đối với các chuỗi ký tự chuỗi ký tự, các phần tử mảng có loại char và được khởi tạo với các byte riêng lẻ của chuỗi ký tự multibyte . [....]

Sử dụng tùy chọn nêu trên làm cho chuỗi literalsconst đủ tiêu chuẩn để sử dụng một chuỗi chữ như RHS của nhiệm vụ để một con trỏ kiểu không const gây nên một cảnh báo.

này được thực hiện với tham chiếu đến C11, chương §6.7.3

Nếu một nỗ lực được thực hiện để sửa đổi một đối tượng được xác định với một kiểu const trình độ thông qua sử dụng của một giá trị trái với phi const- loại đủ điều kiện, hành vi không xác định. [...]

Vì vậy, tại đây trình biên dịch đưa ra cảnh báo cho việc gán loại const đủ điều kiện thành loại không phải loại const.

liên quan đến lý do tại sao sử dụng -Wall -Wextra -pedantic -std=c11 không tạo ra cảnh báo này, nghĩa là, trích dẫn báo giá một lần nữa

[...] Những cảnh báo giúp bạn tìm vào mã thời gian biên dịch có thể cố gắng để viết thành một chuỗi liên tục, nhưng chỉ khi bạn đã rất cẩn thận về việc sử dụng const trong khai báo và nguyên mẫu. Nếu không, nó chỉ là một mối phiền toái. Đây là lý do tại sao chúng tôi không thực hiện yêu cầu -Wall các cảnh báo này.

+0

* "Nếu không, nó chỉ là một mối phiền toái." * Có vẻ như bất cứ ai đã viết hướng dẫn này đang cố gắng bào chữa cho mã riêng của mình: P. – user694733

+0

umm ... có thể không? const-to non-const không được phép hoặc không ngầm và bảo đảm chẩn đoán. làm cho một bên mạnh mẽ const đủ điều kiện có thể phá vỡ một số mã hợp lệ, bạn biết không? –

+1

Có, bạn không thể sử dụng tùy chọn này với mã cũ. Nhưng bất kỳ mã mới nào cũng phải cố gắng trở thành chính xác. Tôi chỉ tìm thấy nó buồn cười mà hướng dẫn sử dụng âm thanh quá phòng thủ. – user694733

14

Có một tùy chọn cho điều này: -Wwrite-strings. Nó hoạt động bằng cách thay đổi loại chuỗi ký tự từ char[N] thành const char[N]. Thay đổi này không tương thích với chuẩn C và sẽ khiến mã hợp lệ bị từ chối và trong một số ít trường hợp mã không hợp lệ được chấp nhận âm thầm. Nó không được kích hoạt theo mặc định.

Thật không may, do cách thức chuỗi ký tự được xác định trong C, cung cấp cảnh báo tốt cho việc này mà không cần thay đổi ngôn ngữ là rất khó.

+0

* "và trong một số ít trường hợp mã không hợp lệ được chấp nhận âm thầm." * Điều đó sẽ xảy ra như thế nào? Nếu '-Wwrite-strings' làm cho việc kiểm tra lỗi nghiêm ngặt hơn, điều này không thể thực hiện được. – user694733

+4

@ user694733 Mã gán địa chỉ của chuỗi ký tự cho biến 'const char (*) []' sẽ được chấp nhận âm thầm (trừ khi nó được thay đổi kể từ lần cuối tôi kiểm tra), nhưng trong tiêu chuẩn C, không có chuyển đổi ngầm từ 'char (*) []' đến 'const char (*) []', do đó, điều này đòi hỏi một chẩn đoán. – hvd

+0

Không chắc chắn tại sao bất cứ ai sẽ lấy địa chỉ của chuỗi chữ, nhưng tôi đoán bạn là chính xác. Tôi vẫn nghĩ rằng cảnh báo về trường hợp bình thường 'char * a =" x ";', outweights rằng vấn đề có thể. – user694733

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