Tại sao trình tối ưu hóa GCC 6 nâng cao phá vỡ mã C ++ thực tế?


148

GCC 6 có một tính năng tối ưu hóa mới : Nó giả định rằng thisnó luôn không rỗng và tối ưu hóa dựa trên điều đó.

Giờ đây, việc truyền phạm vi giá trị giả định rằng con trỏ của các hàm thành viên C ++ là không rỗng. Điều này giúp loại bỏ kiểm tra con trỏ null phổ biến nhưng cũng phá vỡ một số cơ sở mã không tuân thủ (như Qt-5, Chromium, KDevelop) . Như một công việc tạm thời xung quanh -fno-xóa-null-con trỏ-kiểm tra có thể được sử dụng. Mã sai có thể được xác định bằng cách sử dụng -fsanitize = không xác định.

Tài liệu thay đổi rõ ràng gọi điều này là nguy hiểm vì nó phá vỡ một lượng đáng kinh ngạc của mã thường được sử dụng.

Tại sao giả định mới này sẽ phá vỡ mã C ++ thực tế? Có những mô hình cụ thể mà các lập trình viên bất cẩn hoặc không hiểu biết dựa vào hành vi không xác định cụ thể này? Tôi không thể tưởng tượng bất cứ ai viết if (this == NULL)bởi vì điều đó là không tự nhiên.


21
@Ben Hy vọng bạn có nghĩa là một cách tốt. Mã với UB nên được viết lại để không gọi UB. Nó đơn giản như vậy. Heck, thường có những câu hỏi thường gặp cho bạn biết làm thế nào để đạt được nó. Vì vậy, không phải là một vấn đề thực sự IMHO. Tất cả đều tốt.
Phục hồi lại

49
Tôi ngạc nhiên khi thấy mọi người bảo vệ con trỏ dereferences null trong mã. Đơn giản là tuyệt vời.
SergeyA

19
@Ben, khám phá hành vi không xác định đã là chiến thuật tối ưu hóa rất hiệu quả trong một thời gian rất dài. Tôi thích nó, bởi vì tôi thích tối ưu hóa làm cho mã của tôi chạy nhanh hơn.
SergeyA

17
Tôi đồng ý với SergeyA. Toàn bộ brouhaha bắt đầu bởi vì mọi người dường như tập trung vào thực tế thisđược truyền dưới dạng một tham số ngầm, vì vậy họ bắt đầu sử dụng nó như thể đó là một tham số rõ ràng. Nó không thể. Khi bạn hủy đăng ký null, bạn sẽ gọi UB giống như khi bạn hủy đăng ký bất kỳ con trỏ null nào khác. Đó là tất cả để có nó. Nếu bạn muốn truyền nullptrs xung quanh, hãy sử dụng tham số rõ ràng, DUH . Nó sẽ không chậm hơn, nó sẽ không trở nên khó hiểu hơn và mã có API như vậy dù sao cũng nằm sâu trong nội bộ, nên có phạm vi rất hạn chế. Kết thúc câu chuyện tôi nghĩ.
Phục hồi lại

41
Kudos đến GCC để phá vỡ chu trình mã xấu -> trình biên dịch không hiệu quả để hỗ trợ mã xấu -> mã xấu hơn -> biên dịch kém hiệu quả hơn -> ...
MM

Câu trả lời:


87

Tôi đoán câu hỏi cần được trả lời tại sao những người có thiện chí sẽ viết séc ngay từ đầu.

Trường hợp phổ biến nhất có lẽ là nếu bạn có một lớp là một phần của cuộc gọi đệ quy xảy ra tự nhiên.

Nếu bạn có:

struct Node
{
    Node* left;
    Node* right;
};

trong C, bạn có thể viết:

void traverse_in_order(Node* n) {
    if(!n) return;
    traverse_in_order(n->left);
    process(n);
    traverse_in_order(n->right);
}

Trong C ++, thật tuyệt khi biến điều này thành một hàm thành viên:

void Node::traverse_in_order() {
    // <--- What check should be put here?
    left->traverse_in_order();
    process();
    right->traverse_in_order();
}

Trong những ngày đầu của C ++ (trước khi tiêu chuẩn hóa), người ta đã nhấn mạnh rằng các hàm thành viên là đường cú pháp cho một hàm trong đó thistham số ẩn. Mã được viết bằng C ++, được chuyển đổi thành C tương đương và được biên dịch. Thậm chí có những ví dụ rõ ràng rằng so sánh thisvới null là có ý nghĩa và trình biên dịch Cfront ban đầu cũng tận dụng điều này. Vì vậy, đến từ một nền C, sự lựa chọn rõ ràng cho việc kiểm tra là:

if(this == nullptr) return;      

Lưu ý: Bjarne Stroustrup thậm chí còn đề cập rằng các quy tắc thisđã thay đổi qua nhiều năm ở đây

Và điều này đã làm việc trên nhiều trình biên dịch trong nhiều năm. Khi tiêu chuẩn hóa xảy ra, điều này đã thay đổi. Và gần đây hơn, các trình biên dịch bắt đầu lợi dụng việc gọi một hàm thành viên trong đó thiskhông phải nullptrlà hành vi không xác định, điều đó có nghĩa là điều kiện này luôn luôn falsevà trình biên dịch có thể tự do bỏ qua nó.

Điều đó có nghĩa là để thực hiện bất kỳ giao dịch nào của cây này, bạn cần phải:

  • Làm tất cả các kiểm tra trước khi gọi traverse_in_order

    void Node::traverse_in_order() {
        if(left) left->traverse_in_order();
        process();
        if(right) right->traverse_in_order();
    }

    Điều này cũng có nghĩa là kiểm tra tại MỌI trang web cuộc gọi nếu bạn có thể có quyền root.

  • Không sử dụng chức năng thành viên

    Điều này có nghĩa là bạn đang viết mã kiểu C cũ (có thể là một phương thức tĩnh) và gọi nó với đối tượng một cách rõ ràng là một tham số. ví dụ. bạn đang quay lại viết Node::traverse_in_order(node);hơn là node->traverse_in_order();tại trang web cuộc gọi.

  • Tôi tin rằng cách dễ nhất / gọn gàng nhất để khắc phục ví dụ cụ thể này theo cách tuân thủ tiêu chuẩn là thực sự sử dụng nút sentinel chứ không phải a nullptr.

    // static class, or global variable
    Node sentinel;
    
    void Node::traverse_in_order() {
        if(this == &sentinel) return;
        ...
    }

Cả hai tùy chọn đầu tiên đều có vẻ hấp dẫn và trong khi mã có thể thoát khỏi nó, họ đã viết mã xấu với this == nullptr thay vì sử dụng một bản sửa lỗi thích hợp.

Tôi đoán đó là cách mà một số trong các cơ sở mã này đã phát triển để có this == nullptrkiểm tra trong đó.


6
Làm thế nào có thể 1 == 0được xác định hành vi? Nó đơn giản falsethôi.
Julian Schaub - litb

11
Việc kiểm tra chính nó không phải là một hành vi không xác định. Nó chỉ luôn luôn sai, và do đó bị loại bỏ bởi trình biên dịch.
SergeyA

15
Hmm .. this == nullptrthành ngữ là hành vi không xác định vì bạn đã gọi hàm thành viên trên đối tượng nullptr trước đó, không xác định. Và trình biên dịch có thể bỏ qua kiểm tra
jtlim

6
@Joshua, tiêu chuẩn đầu tiên được xuất bản vào năm 1998. Bất cứ điều gì xảy ra trước đó là bất cứ điều gì mọi triển khai đều muốn. Thời kỳ đen tối.
SergeyA

26
Heh, wow, tôi không thể tin bất cứ ai đã từng viết mã dựa trên việc gọi các hàm cá thể ... mà không có một thể hiện . Theo bản năng, tôi đã sử dụng đoạn trích được đánh dấu "Thực hiện tất cả các kiểm tra trước khi gọi traverse_in_order", mà thậm chí không nghĩ đến việc thiscó thể bị vô hiệu hóa. Tôi đoán có lẽ đây là lợi ích của việc học C ++ trong thời đại mà SO tồn tại để cố thủ những hiểm họa của UB trong não tôi và ngăn cản tôi thực hiện những vụ hack kỳ quái như thế này.
gạch dưới

65

Nó làm như vậy bởi vì mã "thực tế" đã bị hỏng và liên quan đến hành vi không xác định để bắt đầu. Không có lý do để sử dụng null this, ngoài việc tối ưu hóa vi mô, thường là rất sớm.

Đó là một thực tế nguy hiểm, vì việc điều chỉnh các con trỏ do truyền tải phân cấp lớp có thể biến null thisthành không null. Vì vậy, ít nhất, lớp có các phương thức được cho là hoạt động với null thisphải là lớp cuối cùng không có lớp cơ sở: nó không thể xuất phát từ bất cứ thứ gì và nó không thể xuất phát từ đó. Chúng tôi đang nhanh chóng khởi hành từ thực tế đến xấu-hack-đất .

Trong thực tế, mã không phải là xấu xí:

struct Node
{
  Node* left;
  Node* right;
  void process();
  void traverse_in_order() {
    traverse_in_order_impl(this);
  }
private:
  static void traverse_in_order_impl(Node * n)
    if (!n) return;
    traverse_in_order_impl(n->left);
    n->process();
    traverse_in_order_impl(n->right);
  }
};

Nếu bạn có một cây trống (ví dụ: root là nullptr), giải pháp này vẫn dựa vào hành vi không xác định bằng cách gọi traverse_in_order với nullptr.

Nếu cây trống, hay còn gọi là null Node* root, bạn không cần phải gọi bất kỳ phương thức không tĩnh nào trên nó. Giai đoạn = Stage. Hoàn toàn ổn khi có mã cây giống như C lấy một con trỏ cá thể bằng một tham số rõ ràng.

Đối số ở đây dường như sôi lên bằng cách nào đó cần phải viết các phương thức không tĩnh trên các đối tượng có thể được gọi từ một con trỏ cá thể null. Không có nhu cầu như vậy. Cách viết mã C-with-object như vậy vẫn đẹp hơn trong thế giới C ++, vì ít nhất nó có thể là loại an toàn. Về cơ bản, null thislà một tối ưu hóa vi mô như vậy, với phạm vi sử dụng hẹp như vậy, không cho phép nó là IMHO hoàn toàn tốt. Không có API công khai nào nên phụ thuộc vào null this.


18
@Ben, Bất cứ ai viết mã này đều sai ngay từ đầu. Thật buồn cười khi bạn đặt tên cho các dự án bị phá vỡ khủng khiếp như MFC, Qt và Chromium. Tốt câu đố với họ.
SergeyA

19
@Ben, phong cách mã hóa khủng khiếp trong Google được biết đến với tôi. Mã Google (ít nhất là có sẵn công khai) thường được viết xấu, mặc dù nhiều người tin rằng mã Google là ví dụ sáng chói. Có thể điều này sẽ khiến họ phải xem lại phong cách mã hóa của họ (và hướng dẫn trong khi họ đang ở trên đó).
SergeyA

18
@Ben Không ai thay thế hồi tố Chromium trên các thiết bị này bằng Chromium được biên dịch bằng gcc 6. Trước khi Chromium được biên dịch bằng gcc 6 và các trình biên dịch hiện đại khác, nó sẽ cần phải được sửa. Nó cũng không phải là một nhiệm vụ lớn; các thiskiểm tra được chọn ra bởi các máy phân tích mã tĩnh khác nhau, vì vậy không phải ai cũng phải tự tìm kiếm chúng. Bản vá có thể là một vài trăm thay đổi nhỏ.
Phục hồi Monica

8
@Ben Trong thực tế, một sự vô hiệu hóa thislà một sự cố ngay lập tức. Những vấn đề này sẽ được phát hiện rất nhanh ngay cả khi không ai quan tâm chạy bộ phân tích tĩnh qua mã. C / C ++ tuân theo câu thần chú "chỉ trả tiền cho các tính năng bạn sử dụng". Nếu bạn muốn kiểm tra, bạn phải rõ ràng về chúng và điều đó có nghĩa là không thực hiện chúng this, khi quá muộn, vì trình biên dịch giả định thiskhông phải là null. Nếu không, nó sẽ phải kiểm tra thisvà đối với 99.9999% mã ngoài đó, việc kiểm tra như vậy là lãng phí thời gian.
Phục hồi lại

10
Lời khuyên của tôi cho bất cứ ai nghĩ rằng tiêu chuẩn bị phá vỡ: sử dụng một ngôn ngữ khác. Không thiếu C ++ - như các ngôn ngữ không có khả năng hành vi không xác định.
MM

35

Tài liệu thay đổi rõ ràng gọi điều này là nguy hiểm vì nó phá vỡ một lượng đáng kinh ngạc của mã thường được sử dụng.

Tài liệu không gọi nó là nguy hiểm. Nó cũng không tuyên bố rằng nó phá vỡ một số lượng mã đáng ngạc nhiên . Nó chỉ đơn giản chỉ ra một vài cơ sở mã phổ biến mà nó tuyên bố là được biết là dựa vào hành vi không xác định này và sẽ bị phá vỡ do thay đổi trừ khi sử dụng tùy chọn giải pháp thay thế.

Tại sao giả định mới này sẽ phá vỡ mã C ++ thực tế?

Nếu mã c ++ thực tế dựa trên hành vi không xác định, thì những thay đổi đối với hành vi không xác định đó có thể phá vỡ nó. Đây là lý do tại sao UB phải tránh, ngay cả khi một chương trình dựa vào nó dường như hoạt động như dự định.

Có những mô hình cụ thể mà các lập trình viên bất cẩn hoặc không hiểu biết dựa vào hành vi không xác định cụ thể này?

Tôi không biết liệu nó có chống lan truyền rộng không , nhưng một lập trình viên không hiểu biết có thể nghĩ rằng họ có thể khắc phục chương trình của họ khỏi sự cố bằng cách thực hiện:

if (this)
    member_variable = 42;

Khi lỗi thực tế là hủy bỏ một con trỏ null ở một nơi khác.

Tôi chắc chắn rằng nếu lập trình viên không đủ hiểu biết, họ sẽ có thể đưa ra các mô hình (chống) tiên tiến hơn dựa trên UB này.

Tôi không thể tưởng tượng bất cứ ai viết if (this == NULL)bởi vì điều đó là không tự nhiên.

Tôi có thể.


11
"Nếu mã c ++ thực tế phụ thuộc vào hành vi không xác định, thì những thay đổi đối với hành vi không xác định đó có thể phá vỡ nó. Đây là lý do tại sao UB phải tránh" này * 1000
underscore_d

if(this == null) PrintSomeHelpfulDebugInformationAboutHowWeGotHere(); Chẳng hạn như một bản ghi dễ đọc dễ đọc của một chuỗi các sự kiện mà trình gỡ lỗi không thể dễ dàng cho bạn biết. Hãy vui vẻ gỡ lỗi này ngay bây giờ mà không mất hàng giờ để kiểm tra ở mọi nơi khi có một giá trị ngẫu nhiên bất ngờ trong một tập dữ liệu lớn, trong mã bạn chưa viết ... Và quy tắc UB về điều này đã được thực hiện sau đó, sau khi C ++ được tạo. Nó được sử dụng là hợp lệ.
Stephane Hockenhull

@StephaneHockenhull Đó là những gì -fsanitize=nulldành cho.
eerorika

@ user2079303 Các vấn đề: Điều đó có làm chậm mã sản xuất đến mức bạn không thể rời khỏi kiểm tra trong khi chạy, khiến công ty tốn rất nhiều tiền không? Điều đó sẽ tăng kích thước và không phù hợp với đèn flash? Điều đó có hoạt động trên tất cả các nền tảng mục tiêu bao gồm Atmel không? Có thể -fsanitize=nullghi lại các lỗi vào thẻ SD / MMC trên các chân # 5,6,10,11 bằng SPI không? Đó không phải là một giải pháp phổ quát. Một số người lập luận rằng nó đi ngược lại các nguyên tắc hướng đối tượng để truy cập vào một đối tượng null nhưng một số ngôn ngữ OOP có một đối tượng null có thể được vận hành để nó không phải là quy tắc chung của OOP. 1/2
Stephane Hockenhull

1
... một biểu thức chính quy phù hợp với các tập tin như vậy? Nói rằng, ví dụ, nếu một giá trị được truy cập hai lần, trình biên dịch có thể hợp nhất các truy cập trừ khi mã giữa chúng thực hiện bất kỳ điều cụ thể nào sẽ dễ dàng hơn nhiều so với cố gắng xác định các tình huống chính xác trong đó mã được phép truy cập vào bộ lưu trữ.
supercat

25

Một số mã "thực tế" (cách hài hước để đánh vần "lỗi") đã bị hỏng trông như thế này:

void foo(X* p) {
  p->bar()->baz();
}

và nó đã quên tính đến thực tế là p->bar()đôi khi trả về một con trỏ null, điều đó có nghĩa là việc hủy bỏ cuộc gọi mà nó gọi baz()là không xác định.

Không phải tất cả các mã đã bị hỏng có chứa rõ ràng if (this == nullptr)hoặc if (!p) return;kiểm tra. Một số trường hợp chỉ đơn giản là các hàm không truy cập bất kỳ biến thành viên nào và do đó đã xuất hiện hoạt động tốt. Ví dụ:

struct DummyImpl {
  bool valid() const { return false; }
  int m_data;
};
struct RealImpl {
  bool valid() const { return m_valid; }
  bool m_valid;
  int m_data;
};

template<typename T>
void do_something_else(T* p) {
  if (p) {
    use(p->m_data);
  }
}

template<typename T>
void func(T* p) {
  if (p->valid())
    do_something(p);
  else 
    do_something_else(p);
}

Trong mã này khi bạn gọi func<DummyImpl*>(DummyImpl*)bằng một con trỏ null, có một sự quy định "khái niệm" của con trỏ để gọi p->DummyImpl::valid(), nhưng thực tế là hàm thành viên chỉ trả về falsemà không truy cập *this. Điều đó return falsecó thể được nội tuyến và vì vậy trong thực tế, con trỏ không cần phải được truy cập. Vì vậy, với một số trình biên dịch có vẻ như nó hoạt động tốt: không có segfault cho dereferences null, p->valid()là sai, vì vậy các cuộc gọi mã do_something_else(p), kiểm tra các con trỏ null, và do đó không có gì. Không có sự cố hoặc hành vi bất ngờ được quan sát.

Với GCC 6, bạn vẫn nhận được cuộc gọi đến p->valid(), nhưng trình biên dịch bây giờ xâm nhập vào biểu thức đó pphải là null (nếu không p->valid()sẽ là hành vi không xác định) và ghi chú thông tin đó. Thông tin được suy luận đó được sử dụng bởi trình tối ưu hóa để nếu cuộc gọi do_something_else(p)được nội tuyến, if (p)kiểm tra hiện được coi là dư thừa, bởi vì trình biên dịch nhớ rằng nó không phải là null và do đó, nội tuyến mã sẽ:

template<typename T>
void func(T* p) {
  if (p->valid())
    do_something(p);
  else {
    // inlined body of do_something_else(p) with value propagation
    // optimization performed to remove null check.
    use(p->m_data);
  }
}

Điều này bây giờ thực sự không có con trỏ null, và do đó mã mà trước đây xuất hiện để làm việc ngừng hoạt động.

Trong ví dụ này, lỗi đã xảy ra func, trước tiên nên kiểm tra null (hoặc người gọi không bao giờ nên gọi nó bằng null):

template<typename T>
void func(T* p) {
  if (p && p->valid())
    do_something(p);
  else 
    do_something_else(p);
}

Một điểm quan trọng cần nhớ là hầu hết các tối ưu hóa như thế này không phải là trường hợp trình biên dịch nói rằng "ah, lập trình viên đã kiểm tra con trỏ này với null, tôi sẽ loại bỏ nó chỉ để gây phiền nhiễu". Điều gì xảy ra là các tối ưu hóa khác nhau như truyền nội tuyến và lan truyền phạm vi giá trị kết hợp với nhau để làm cho các kiểm tra đó trở nên dư thừa, bởi vì chúng xuất hiện sau một kiểm tra trước đó, hoặc một quy định. Nếu trình biên dịch biết rằng một con trỏ không rỗng tại điểm A trong một hàm và con trỏ không thay đổi trước một điểm B sau đó trong cùng một hàm, thì nó biết rằng nó cũng không phải là null tại B. Khi xảy ra nội tuyến các điểm A và B thực sự có thể là các đoạn mã ban đầu ở các hàm riêng biệt, nhưng giờ được kết hợp thành một đoạn mã và trình biên dịch có thể áp dụng kiến ​​thức của mình rằng con trỏ không rỗng ở nhiều nơi hơn.


Có thể sử dụng GCC 6 để đưa ra các cảnh báo về thời gian biên dịch khi nó gặp các ứng dụng như vậy thiskhông?
jotik


3
@jotik, ^^ ^ những gì TC nói. Điều đó là có thể, nhưng bạn sẽ nhận được cảnh báo đó CHO TẤT CẢ MÃ, TẤT CẢ THỜI GIAN . Lan truyền phạm vi giá trị là một trong những tối ưu hóa phổ biến nhất, ảnh hưởng đến hầu hết tất cả các mã, ở mọi nơi. Các trình tối ưu hóa chỉ cần xem mã, có thể được đơn giản hóa. Họ không thấy "một đoạn mã được viết bởi một tên ngốc muốn được cảnh báo nếu UB ngu ngốc của họ được tối ưu hóa". Trình biên dịch không dễ để phân biệt sự khác nhau giữa "kiểm tra dự phòng mà lập trình viên muốn được tối ưu hóa" và "kiểm tra dự phòng mà lập trình viên nghĩ sẽ giúp, nhưng là dự phòng".
Jonathan Wakely

1
Nếu bạn muốn sử dụng mã của mình để đưa ra lỗi thời gian chạy cho các loại UB khác nhau, bao gồm cả việc sử dụng không hợp lệ this, thì chỉ cần sử dụng-fsanitize=undefined
Jonathan Wakely


-25

Tiêu chuẩn C ++ bị phá vỡ theo những cách quan trọng. Thật không may, thay vì bảo vệ người dùng khỏi những vấn đề này, các nhà phát triển GCC đã chọn sử dụng hành vi không xác định làm lý do để thực hiện tối ưu hóa cận biên, ngay cả khi nó đã được giải thích rõ ràng cho họ về tác hại của nó.

Ở đây một người thông minh hơn tôi giải thích rất chi tiết. (Anh ấy đang nói về C nhưng tình hình vẫn như vậy).

Tại sao nó có hại?

Chỉ cần biên dịch lại mã hoạt động trước đó, mã bảo mật với phiên bản mới hơn của trình biên dịch có thể đưa ra các lỗ hổng bảo mật . Mặc dù hành vi mới có thể bị vô hiệu hóa với một cờ, rõ ràng các tệp tạo tệp hiện tại không được đặt cờ đó. Và vì không có cảnh báo nào được tạo ra, nên nhà phát triển không rõ ràng rằng hành vi hợp lý trước đây đã thay đổi.

Trong ví dụ này, nhà phát triển đã bao gồm một kiểm tra cho tràn số nguyên, sử dụng assert, sẽ chấm dứt chương trình nếu độ dài không hợp lệ được cung cấp. Nhóm GCC đã xóa kiểm tra trên cơ sở rằng tràn số nguyên không được xác định, do đó kiểm tra có thể được xóa. Điều này dẫn đến các trường hợp thực tế trong cơ sở mã hóa này bị tái tạo dễ bị tổn thương sau khi sự cố đã được khắc phục.

Đọc toàn bộ. Nó đủ để làm bạn khóc.

OK, nhưng cái này thì sao?

Quay trở lại khi, có một thành ngữ khá phổ biến đã diễn ra như thế này:

 OPAQUEHANDLE ObjectType::GetHandle(){
    if(this==NULL)return DEFAULTHANDLE;
    return mHandle;

 }

 void DoThing(ObjectType* pObj){
     osfunction(pObj->GetHandle(), "BLAH");
 }

Vì vậy, thành ngữ là: Nếu pObjkhông phải là null, bạn sử dụng tay cầm mà nó chứa, nếu không thì bạn sử dụng tay cầm mặc định. Điều này được gói gọn trong GetHandlehàm.

Mẹo ở đây là gọi một hàm không ảo thực sự không sử dụng this con trỏ, do đó không có vi phạm truy cập.

Tôi vẫn không hiểu

Rất nhiều mã tồn tại được viết như thế. Nếu ai đó chỉ đơn giản biên dịch lại nó, mà không thay đổi một dòng, mỗi cuộc gọi đến DoThing(NULL)là một lỗi sự cố - nếu bạn may mắn.

Nếu bạn không may mắn, các cuộc gọi đến các lỗi gặp sự cố sẽ trở thành lỗ hổng thực thi từ xa.

Điều này có thể xảy ra thậm chí tự động. Bạn đã có một hệ thống xây dựng tự động, phải không? Nâng cấp nó lên trình biên dịch mới nhất là vô hại, phải không? Nhưng bây giờ thì không - không phải nếu trình biên dịch của bạn là GCC.

OK để nói với họ!

Họ đã được nói. Họ đang làm điều này trong kiến ​​thức đầy đủ về hậu quả.

nhưng tại sao?

Ai có thể nói? Có lẽ:

  • Họ đánh giá độ tinh khiết lý tưởng của ngôn ngữ C ++ so với mã thực tế
  • Họ tin rằng mọi người nên bị trừng phạt vì không tuân theo tiêu chuẩn
  • Họ không hiểu gì về thực tế của thế giới
  • Họ đang ... giới thiệu các lỗi về mục đích. Có lẽ đối với một chính phủ nước ngoài. Bạn sống ở đâu? Tất cả các chính phủ đều xa lạ với hầu hết thế giới, và hầu hết đều thù địch với một số người trên thế giới.

Hoặc có lẽ một cái gì đó khác. Ai có thể nói?


32
Không đồng ý với mỗi và một dòng của câu trả lời. Những bình luận tương tự đã được đưa ra để tối ưu hóa răng cưa nghiêm ngặt, và những người hy vọng sẽ bị loại bỏ ngay bây giờ. Giải pháp là giáo dục các nhà phát triển, không ngăn chặn tối ưu hóa dựa trên thói quen phát triển xấu.
SergeyA

30
Tôi đã đi và đọc toàn bộ những gì bạn nói, và thực sự tôi đã khóc, nhưng chủ yếu là ở sự ngu ngốc của Felix mà tôi không nghĩ là những gì bạn đang cố gắng vượt qua ...
Mike Vine

33
Downvote cho các rant vô dụng. "Họ đang ... giới thiệu các lỗi về mục đích. Có lẽ đối với một chính phủ nước ngoài." Có thật không? Đây không phải là / r / âm mưu.
isanae

31
Các lập trình viên quá cố lặp đi lặp lại lặp đi lặp lại câu thần chú không viện dẫn hành vi không xác định , tuy nhiên những người không phải này đã đi trước và thực hiện nó bằng mọi cách. Và nhìn những gì đã xảy ra. Tôi không có cảm tình gì. Đây là lỗi của nhà phát triển, đơn giản như vậy. Họ cần chịu trách nhiệm. Nhớ lấy? Trách nhiệm cá nhân? Mọi người dựa vào câu thần chú của bạn "nhưng thực tế thì sao !" chính xác là làm thế nào tình huống này phát sinh ở nơi đầu tiên. Tránh những điều vô nghĩa như thế này chính là lý do tại sao các tiêu chuẩn tồn tại ở nơi đầu tiên. Mã theo tiêu chuẩn, và bạn sẽ không có vấn đề. Giai đoạn = Stage.
Các cuộc đua nhẹ nhàng trong quỹ đạo

18
"Đơn giản là biên dịch lại mã hoạt động trước đó, mã bảo mật với phiên bản mới hơn của trình biên dịch có thể đưa ra các lỗ hổng bảo mật" - điều đó luôn xảy ra . Trừ khi bạn muốn bắt buộc một phiên bản của một trình biên dịch là trình biên dịch duy nhất sẽ được phép cho phần còn lại của sự vĩnh cửu. Bạn có nhớ lại khi kernel linux chỉ có thể được biên dịch với chính xác gcc 2.7.2.1 không? Dự án gcc thậm chí đã bị rẽ nhánh vì mọi người đã chán ngấy với bullcrap. Phải mất một thời gian dài để vượt qua điều đó.
MM
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.