Khi nào con trỏ nên được kiểm tra NULL trong C?


18

Tóm tắt :

Có nên kiểm tra một hàm trong C để đảm bảo rằng nó không bị hủy bỏ NULLcon trỏ không? Nếu không khi nào thì thích hợp để bỏ qua các kiểm tra này?

Chi tiết :

Tôi đã đọc một số cuốn sách về các cuộc phỏng vấn lập trình và tôi tự hỏi mức độ xác nhận đầu vào thích hợp cho các đối số chức năng trong C là gì? Rõ ràng bất kỳ chức năng nào nhận đầu vào từ người dùng cần thực hiện xác nhận, bao gồm kiểm tra một NULLcon trỏ trước khi hủy bỏ chức năng này. Nhưng trong trường hợp của một chức năng trong cùng một tệp mà bạn không mong đợi sẽ hiển thị thông qua API thì sao?

Ví dụ: phần sau xuất hiện trong mã nguồn của git:

static unsigned short graph_get_current_column_color(const struct git_graph *graph)
{
    if (!want_color(graph->revs->diffopt.use_color))
        return column_colors_max;
    return graph->default_column_color;
}

Nếu *graphNULLsau đó một con trỏ null sẽ được dereferenced, có lẽ làm hỏng chương trình, nhưng có thể dẫn đến một số hành vi không thể đoán trước kia. Mặt khác, chức năng là staticvà vì vậy có thể lập trình viên đã xác nhận đầu vào. Tôi không biết, tôi chỉ chọn nó một cách ngẫu nhiên vì đó là một ví dụ ngắn trong một chương trình ứng dụng được viết bằng C. Tôi đã thấy nhiều nơi khác sử dụng con trỏ mà không kiểm tra NULL. Câu hỏi của tôi là chung chung không cụ thể cho phân khúc mã này.

Tôi thấy một câu hỏi tương tự được hỏi trong bối cảnh bàn giao ngoại lệ . Tuy nhiên, đối với một ngôn ngữ không an toàn như C hoặc C ++, không có sự lan truyền lỗi tự động của các ngoại lệ chưa được xử lý.

Mặt khác, tôi đã thấy rất nhiều mã trong các dự án nguồn mở (như ví dụ ở trên) không thực hiện bất kỳ kiểm tra nào về con trỏ trước khi sử dụng chúng. Tôi tự hỏi liệu có ai có suy nghĩ về các hướng dẫn khi nào nên đặt kiểm tra trong một hàm so với giả sử rằng hàm đó được gọi với các đối số chính xác.

Tôi quan tâm đến câu hỏi này nói chung để viết mã sản xuất. Nhưng tôi cũng quan tâm đến bối cảnh phỏng vấn lập trình. Ví dụ, nhiều sách giáo khoa thuật toán (như CLR) có xu hướng trình bày các thuật toán trong mã giả mà không có bất kỳ kiểm tra lỗi nào. Tuy nhiên, trong khi điều này tốt cho việc hiểu cốt lõi của thuật toán thì rõ ràng nó không phải là một thực hành lập trình tốt. Vì vậy, tôi sẽ không muốn nói với một người phỏng vấn rằng tôi đã bỏ qua việc kiểm tra lỗi để đơn giản hóa các ví dụ mã của tôi (như một cuốn sách giáo khoa có thể). Nhưng tôi cũng không muốn xuất hiện để tạo mã không hiệu quả với việc kiểm tra lỗi quá mức. Ví dụ, graph_get_current_column_colorcó thể đã được sửa đổi để kiểm tra *graphnull nhưng không rõ nó sẽ làm gì nếu *graphlà null, ngoài ra nó không nên hủy đăng ký.


7
Nếu bạn đang viết một hàm cho API nơi người gọi không cần phải hiểu các bộ phận bên trong, thì đây là một trong những nơi mà tài liệu quan trọng. Nếu bạn chứng minh rằng một đối số phải là một con trỏ không hợp lệ, không phải là NULL, việc kiểm tra nó sẽ trở thành trách nhiệm của người gọi.
Blrfl


Với tầm nhìn của năm 2017, hãy ghi nhớ câu hỏi và hầu hết các câu trả lời đã được viết vào năm 2013, có câu trả lời nào đề cập đến vấn đề hành vi không xác định du hành thời gian do tối ưu hóa trình biên dịch không?
rwong

Trong trường hợp các lệnh gọi API mong đợi các đối số con trỏ hợp lệ, tôi tự hỏi giá trị của việc kiểm tra chỉ NULL là gì? Bất kỳ con trỏ không hợp lệ nào bị hủy đăng ký cũng sẽ tệ như NULL và segfault đều giống nhau.
PaulHK

Câu trả lời:


15

Con trỏ null không hợp lệ có thể do lỗi lập trình viên hoặc do lỗi thời gian chạy. Lỗi thời gian chạy là thứ mà lập trình viên không thể sửa, như malloclỗi do bộ nhớ thấp hoặc mạng bị rớt gói hoặc người dùng nhập vào thứ gì đó ngu ngốc. Lỗi lập trình được gây ra bởi một lập trình viên sử dụng chức năng không chính xác.

Nguyên tắc chung mà tôi đã thấy là luôn luôn phải kiểm tra lỗi thời gian chạy, nhưng lỗi lập trình viên không phải kiểm tra mỗi lần. Hãy nói rằng một số lập trình viên ngốc gọi trực tiếp graph_get_current_column_color(0). Nó sẽ segfault lần đầu tiên được gọi, nhưng một khi bạn sửa nó, bản sửa lỗi sẽ được biên dịch vĩnh viễn. Không cần phải kiểm tra mỗi lần nó chạy.

Đôi khi, đặc biệt là trong các thư viện của bên thứ ba, bạn sẽ thấy một lỗi assertđể kiểm tra lỗi lập trình viên thay vì một ifcâu lệnh. Điều đó cho phép bạn biên dịch các kiểm tra trong quá trình phát triển và bỏ chúng trong mã sản xuất. Thỉnh thoảng tôi cũng thấy các kiểm tra vô cớ trong đó nguồn gốc của lỗi lập trình viên tiềm năng đã bị loại bỏ khỏi triệu chứng.

Rõ ràng, bạn luôn có thể tìm thấy ai đó có tính mô phạm hơn, nhưng hầu hết các lập trình viên C mà tôi biết đều ưu tiên mã ít lộn xộn hơn mã an toàn hơn một chút. Và "an toàn hơn" là một thuật ngữ chủ quan. Một segfault trắng trợn trong quá trình phát triển được ưu tiên hơn một lỗi tham nhũng tinh vi trong lĩnh vực này.


Câu hỏi có phần chủ quan nhưng dường như đây là câu trả lời tốt nhất. Cảm ơn tất cả những người đã cho những suy nghĩ của họ về câu hỏi này.
Gabriel Nam

1
Trong iOS, malloc sẽ không bao giờ trả lại NULL. Nếu nó không tìm thấy bộ nhớ thì nó sẽ yêu cầu ứng dụng của bạn giải phóng bộ nhớ trước, sau đó nó sẽ yêu cầu hệ điều hành (sẽ yêu cầu các ứng dụng khác giải phóng bộ nhớ và có thể giết chúng), và nếu vẫn không có bộ nhớ, nó sẽ giết ứng dụng của bạn . Không cần kiểm tra.
gnasher729

11

Kernighan & Plauger, trong "Công cụ phần mềm", đã viết rằng họ sẽ kiểm tra mọi thứ và, với điều kiện mà họ tin rằng trên thực tế không bao giờ xảy ra, họ sẽ hủy bỏ thông báo lỗi "Không thể xảy ra".

Họ báo cáo đã nhanh chóng bị hạ thấp bởi số lần họ thấy "Không thể xảy ra" xuất hiện trên thiết bị đầu cuối của họ.

Bạn nên LUÔN kiểm tra con trỏ cho NULL trước khi bạn (cố gắng) hủy đăng ký nó. LUÔN LUÔN . Số lượng mã bạn nhân đôi kiểm tra các NULL không xảy ra và bộ xử lý sẽ xử lý "lãng phí" của bạn, sẽ được trả nhiều hơn cho số lượng sự cố mà bạn không phải gỡ lỗi từ không có gì hơn là kết xuất sự cố - nếu bạn may mắn

Nếu con trỏ bất biến bên trong một vòng lặp, nó đủ để kiểm tra nó bên ngoài vòng lặp, nhưng sau đó bạn nên "sao chép" nó vào một biến cục bộ giới hạn phạm vi, để sử dụng bởi vòng lặp, có thêm các trang trí const thích hợp. Trong trường hợp này, bạn PHẢI đảm bảo rằng mọi chức năng được gọi từ thân vòng lặp bao gồm các trang trí const cần thiết trên các nguyên mẫu, TẤT CẢ CÁC CÁCH XUỐNG. Nếu bạn không, hoặc có thể không (vì ví dụ như một nhà cung cấp gói hoặc một đồng nghiệp bướng bỉnh), sau đó bạn phải kiểm tra xem nó cho NULL mỗi lần nó thể được sửa đổi , bởi vì chắc chắn như COL Murphy là một người lạc quan vô phương cứu chữa, ai đó IS đi để hạ gục nó khi bạn không tìm kiếm.

Nếu bạn đang ở trong một hàm và con trỏ được cho là không phải là NULL, bạn nên xác minh nó.

Nếu bạn đang nhận được nó từ một chức năng và nó được cho là không phải NULL sắp ra mắt, bạn nên xác minh nó. malloc () đặc biệt nổi tiếng về việc này. (Nortel Networks, hiện không còn tồn tại, đã có một tiêu chuẩn mã hóa bằng văn bản khó và nhanh về điều này. Tôi đã gỡ lỗi một sự cố tại một thời điểm, rằng tôi đã quay trở lại malloc () trả về một con trỏ NULL và bộ mã hóa ngu ngốc không bận tâm kiểm tra nó trước khi anh ấy viết cho nó, bởi vì anh ấy chỉ BIẾT anh ấy có rất nhiều bộ nhớ ... Tôi đã nói một số điều rất khó chịu khi cuối cùng tôi đã tìm thấy nó.)


8
Nếu bạn đang ở trong một hàm yêu cầu con trỏ không phải là NULL, nhưng dù sao bạn cũng kiểm tra và đó là NULL ... tiếp theo là gì?
gièm pha

1
@detly hoặc dừng những gì bạn đang làm và trả lại mã lỗi hoặc thực hiện một xác nhận
James

1
@James - không nghĩ đến assert, chắc chắn. Tôi không thích ý tưởng mã lỗi nếu bạn đang nói về việc thay đổi mã hiện có để bao gồm NULLkiểm tra.
gièm pha

10
@detly bạn sẽ không đi xa như một nhà phát triển C nếu bạn không thích mã lỗi
James

5
@ JohnR.Strohm - đây là C, đó là khẳng định hoặc không có gì: P
gièm pha

5

Bạn có thể bỏ qua kiểm tra khi bạn có thể thuyết phục bản thân bằng cách nào đó rằng con trỏ có thể không có giá trị.

Thông thường, kiểm tra con trỏ null được triển khai theo mã trong đó null dự kiến ​​sẽ xuất hiện dưới dạng chỉ báo cho thấy một đối tượng hiện không có sẵn. Null được sử dụng như một giá trị sentinel, ví dụ để chấm dứt các danh sách được liên kết hoặc thậm chí là các mảng của con trỏ. Các argvvector của chuỗi truyền vào mainlà cần thiết để được null-chấm dứt bởi một con trỏ, tương tự như cách một chuỗi bị chấm dứt bởi một ký tự null: argv[argc]là một con trỏ null, và bạn có thể dựa vào điều này khi phân tích cú pháp dòng lệnh.

while (*argv) {
   /* process argument string *argv */
   argv++; /* increment to next one */
}

Vì vậy, các tình huống để kiểm tra null là những tình huống trong đó nó là một giá trị mong đợi. Kiểm tra null thực hiện ý nghĩa của con trỏ null, chẳng hạn như dừng tìm kiếm danh sách được liên kết. Chúng ngăn chặn mã từ bỏ hội thảo con trỏ.

Trong một tình huống mà giá trị con trỏ null không được thiết kế mong đợi, không có điểm nào trong việc kiểm tra nó. Nếu một giá trị con trỏ không hợp lệ phát sinh, rất có thể nó sẽ xuất hiện không null, không thể phân biệt được với các giá trị hợp lệ theo bất kỳ cách di động nào. Chẳng hạn, một giá trị con trỏ thu được từ việc đọc bộ nhớ chưa được khởi tạo được hiểu là một loại con trỏ, một con trỏ thu được thông qua một số chuyển đổi mờ ám hoặc một con trỏ tăng dần ra khỏi giới hạn.

Về một loại dữ liệu, chẳng hạn như graph *: điều này có thể được thiết kế sao cho giá trị null là một biểu đồ hợp lệ: một cái gì đó không có cạnh và không có nút. Trong trường hợp này, tất cả các hàm lấy một graph *con trỏ sẽ phải xử lý giá trị đó, vì đó là một giá trị miền chính xác trong biểu diễn đồ thị. Mặt khác, một graph *con trỏ có thể là một con trỏ tới một đối tượng giống như container không bao giờ là null nếu chúng ta giữ một biểu đồ; một con trỏ null sau đó có thể cho chúng ta biết rằng "đối tượng biểu đồ không có mặt; chúng ta chưa phân bổ nó hoặc chúng ta đã giải phóng nó hoặc điều này hiện không có biểu đồ liên quan". Việc sử dụng con trỏ sau này là một boolean / vệ tinh kết hợp: con trỏ không phải là null cho biết "Tôi có đối tượng chị em này" nó cung cấp đối tượng đó.

Chúng ta có thể đặt một con trỏ thành null ngay cả khi chúng ta không giải phóng một đối tượng, chỉ đơn giản là phân tách một đối tượng khỏi đối tượng khác:

tty_driver->tty = NULL; /* detach low level driver from the tty device */

Đối số thuyết phục nhất mà tôi biết rằng một con trỏ không thể rỗng tại một điểm nhất định là bọc điểm đó trong "if (ptr! = NULL) {" và "}" tương ứng. Ngoài ra, bạn đang ở trong lãnh thổ xác minh chính thức.
John R. Strohm

4

Hãy để tôi thêm một giọng nói vào fugue.

Giống như nhiều câu trả lời khác, tôi nói - đừng bận tâm kiểm tra vào thời điểm này; đó là trách nhiệm của người gọi. Nhưng tôi có một nền tảng để xây dựng chứ không phải là sự nhanh nhẹn đơn giản (và sự kiêu ngạo lập trình C).

Tôi cố gắng tuân theo hiệu trưởng của Donald Knuth về việc làm cho các chương trình trở nên mong manh nhất có thể. Nếu bất cứ điều gì sai, hãy để nó sụp đổ lớn và tham chiếu một con trỏ null thường là một cách tốt để làm điều đó. Ý tưởng chung là một vụ tai nạn hoặc một vòng lặp vô hạn là xa tốt hơn so với việc tạo ra dữ liệu sai. Và nó được các lập trình viên chú ý!

Nhưng tham chiếu con trỏ null (đặc biệt đối với các cấu trúc dữ liệu lớn) không phải lúc nào cũng gây ra sự cố. Thở dài. Đúng. Và đó là nơi mà Asserts rơi vào. Chúng đơn giản, có thể ngay lập tức làm hỏng chương trình của bạn (câu trả lời cho câu hỏi, "Phương pháp nên làm gì nếu gặp null?") ​​Và có thể bật / tắt cho nhiều tình huống khác nhau (tôi khuyên bạn nên KHÔNG tắt chúng đi, vì sẽ tốt hơn nếu khách hàng gặp sự cố và thấy một thông điệp khó hiểu hơn là có dữ liệu xấu).

Đó là hai xu của tôi.


1

Tôi thường chỉ kiểm tra khi một con trỏ được gán, thường là lần duy nhất tôi thực sự có thể làm gì đó về nó và có thể phục hồi nếu nó không hợp lệ.

Ví dụ, nếu tôi xử lý một cửa sổ, tôi sẽ kiểm tra xem nó có đúng không và sau đó và làm gì đó với điều kiện null, nhưng tôi sẽ không kiểm tra xem nó có bị rỗng không Tôi sử dụng con trỏ, trong mỗi chức năng, con trỏ được truyền tới, nếu không tôi sẽ có hàng núi mã xử lý lỗi trùng lặp.

Các hàm như graph_get_current_column_colorcó lẽ hoàn toàn không thể làm bất cứ điều gì hữu ích cho tình huống của bạn nếu nó gặp phải một con trỏ xấu, vì vậy tôi sẽ bỏ qua việc kiểm tra NULL cho người gọi của nó.


1

Tôi muốn nói rằng nó phụ thuộc vào những điều sau đây:

  1. Việc sử dụng CPU có quan trọng không? Mỗi kiểm tra cho NULL mất một lượng thời gian.
  2. Tỷ lệ cược mà con trỏ là NULL là gì? Có phải nó chỉ được sử dụng trong một chức năng trước đó. Có thể giá trị của con trỏ đã được thay đổi.
  3. Là hệ thống phòng ngừa? Có nghĩa là một sự thay đổi nhiệm vụ có thể xảy ra và thay đổi giá trị? ISR có thể đến và thay đổi giá trị không?
  4. Làm thế nào chặt chẽ là mã?
  5. Có một số loại cơ chế tự động sẽ tự động kiểm tra con trỏ NULL không?

Con trỏ sử dụng CPU / tỷ lệ cược là NULL Mỗi khi bạn kiểm tra NULL, sẽ mất thời gian. Vì lý do này, tôi cố gắng giới hạn kiểm tra của mình ở nơi con trỏ có thể đã thay đổi giá trị của nó.

Hệ thống ưu tiên Nếu mã của bạn đang chạy và một tác vụ khác có thể làm gián đoạn nó và có khả năng thay đổi giá trị mà một kiểm tra sẽ rất tốt để có.

Các mô-đun được ghép chặt chẽ Nếu hệ thống được liên kết chặt chẽ thì sẽ có nghĩa là bạn có nhiều kiểm tra hơn. Ý tôi là điều này là nếu có các cấu trúc dữ liệu được chia sẻ giữa nhiều mô-đun, một mô-đun có thể thay đổi một cái gì đó từ bên dưới mô-đun khác. Trong những tình huống này có ý nghĩa để kiểm tra thường xuyên hơn.

Kiểm tra tự động / Hỗ trợ phần cứng Điều cuối cùng cần tính đến là nếu phần cứng mà bạn đang chạy có một số loại cơ chế có thể kiểm tra NULL. Cụ thể tôi đang đề cập đến phát hiện lỗi trang. Nếu hệ thống của bạn phát hiện lỗi trang, CPU có thể kiểm tra truy cập NULL. Cá nhân tôi thấy đây là cơ chế tốt nhất vì nó luôn chạy và không dựa vào lập trình viên để đưa vào kiểm tra rõ ràng. Nó cũng có lợi ích của thực tế không chi phí. Nếu điều này là có sẵn, tôi khuyên bạn nên gỡ lỗi, khó hơn một chút nhưng không quá.

Để kiểm tra nếu nó có sẵn, tạo một chương trình với một con trỏ. Đặt con trỏ về 0 và sau đó thử đọc / ghi nó.


Tôi không biết liệu mình có thể phân loại segfault là thực hiện kiểm tra NULL tự động hay không. Tôi đồng ý rằng việc bảo vệ bộ nhớ CPU sẽ giúp ích cho một quá trình không thể gây ra nhiều thiệt hại cho phần còn lại của hệ thống nhưng tôi sẽ không gọi đó là bảo vệ tự động.
Gabriel Nam

1

Theo tôi, xác nhận các đầu vào (trước / sau điều kiện, nghĩa là) là một điều tốt để phát hiện lỗi lập trình, nhưng chỉ khi nó dẫn đến một lỗi lớn và đáng ghét, dừng hiển thị của một loại không thể bỏ qua. assertthường có tác dụng đó.

Bất cứ điều gì thiếu điều này có thể biến thành một cơn ác mộng mà không có các đội phối hợp rất cẩn thận. Và tất nhiên, lý tưởng là tất cả các đội được phối hợp rất cẩn thận và thống nhất theo các tiêu chuẩn chặt chẽ, nhưng hầu hết các môi trường mà tôi đã làm việc đều thua xa điều đó.

Chỉ là một ví dụ, tôi đã làm việc với một số đồng nghiệp tin rằng người ta nên kiểm tra sự hiện diện của con trỏ null một cách tôn giáo, vì vậy họ đã rắc rất nhiều mã như thế này:

void vertex_move(Vertex* v)
{
     if (!v)
          return;
     ...
}

... và đôi khi chỉ như vậy mà không trả lại / đặt mã lỗi. Và đây là một cơ sở mã đã tồn tại vài thập kỷ với nhiều plugin bên thứ ba có được. Nó cũng là một codebase bị vấy bẩn bởi nhiều lỗi và thường là những lỗi rất khó tìm ra nguyên nhân gốc do chúng có xu hướng bị sập ở các trang web cách xa nguồn gốc của vấn đề.

Và thực tế này là một trong những lý do tại sao. Đó là một sự vi phạm một điều kiện tiên quyết đã được thiết lập của move_vertexhàm trên để truyền một đỉnh null cho nó, nhưng một hàm như vậy chỉ âm thầm chấp nhận nó và không làm gì để đáp lại. Vì vậy, điều có xu hướng xảy ra là một plugin có thể có lỗi lập trình viên khiến nó chuyển null thành chức năng đã nói, chỉ để không phát hiện ra nó, chỉ làm nhiều việc sau đó, và cuối cùng hệ thống sẽ bắt đầu bong ra hoặc gặp sự cố.

Nhưng vấn đề thực sự ở đây là không có khả năng dễ dàng phát hiện vấn đề này. Vì vậy, tôi đã từng cố gắng xem điều gì sẽ xảy ra nếu tôi chuyển mã tương tự ở trên thành một assert, như vậy:

void vertex_move(Vertex* v)
{
     assert(v && "Vertex should never be null!");
     ...
}

... Và với sự kinh hoàng của tôi, tôi thấy rằng sự khẳng định thất bại trái và phải ngay cả khi khởi động ứng dụng. Sau khi tôi sửa một vài trang web cuộc gọi đầu tiên, tôi đã làm thêm một số điều và sau đó nhận được thêm một lần thất bại khẳng định. Tôi đã tiếp tục cho đến khi tôi đã sửa đổi rất nhiều mã mà cuối cùng tôi đã hoàn nguyên các thay đổi của mình vì chúng đã trở nên quá xâm phạm và giữ nguyên kiểm tra con trỏ null, thay vào đó là tài liệu cho phép hàm chấp nhận một đỉnh null.

Nhưng đó là mối nguy hiểm, mặc dù trong trường hợp xấu nhất, không thể vi phạm các điều kiện trước / sau điều kiện dễ dàng phát hiện. Sau đó, bạn có thể, trong nhiều năm, âm thầm tích lũy một thuyền mã vi phạm các điều kiện trước / sau như vậy trong khi bay dưới radar thử nghiệm. Theo ý kiến ​​của tôi, con trỏ null như vậy kiểm tra bên ngoài một sự thất bại khẳng định trắng trợn và đáng ghét thực sự có thể gây hại rất nhiều, xa hơn là tốt.

Đối với câu hỏi quan trọng là khi nào bạn nên kiểm tra con trỏ null, tôi tin vào việc khẳng định một cách tự do nếu nó được thiết kế để phát hiện lỗi lập trình viên và không để điều đó im lặng và khó phát hiện. Nếu đó không phải là lỗi lập trình và một cái gì đó nằm ngoài sự kiểm soát của lập trình viên, như lỗi hết bộ nhớ, thì việc kiểm tra null và sử dụng xử lý lỗi là điều hợp lý. Ngoài ra, đó là một câu hỏi thiết kế và dựa trên những gì các chức năng của bạn coi là điều kiện trước / sau hợp lệ.


0

Một thực tế là luôn luôn thực hiện kiểm tra null trừ khi bạn đã kiểm tra nó; vì vậy nếu đầu vào đang được truyền từ hàm A () đến B () và A () đã xác thực con trỏ bạn chắc chắn B () không được gọi ở bất kỳ nơi nào khác, thì B () có thể tin tưởng A () vệ sinh dữ liệu.


1
... Cho đến 6 tháng sau, một người nào đó xuất hiện và thêm một số mã gọi B () (có thể giả sử rằng bất cứ ai đã viết B () chắc chắn đã kiểm tra NULL đúng cách). Sau đó, bạn đang say sưa, phải không? Quy tắc cơ bản - nếu một điều kiện không hợp lệ tồn tại cho đầu vào của một chức năng, thì hãy kiểm tra nó, bởi vì đầu vào nằm ngoài sự kiểm soát của chức năng.
Maximus Minimus

@ mh01 Nếu bạn chỉ cần phá vỡ mã ngẫu nhiên (nghĩa là đưa ra các giả định và không đọc tài liệu), thì tôi không nghĩ rằng NULLviệc kiểm tra thêm sẽ có tác dụng nhiều. Hãy suy nghĩ về nó: bây giờ B()kiểm tra NULLvà ... làm gì? Trả lại -1? Nếu người gọi không kiểm tra NULL, bạn có thể tin tưởng rằng họ sẽ xử lý -1trường hợp giá trị trả lại nào?
gièm pha

1
Đó là trách nhiệm của người gọi. Bạn tự giải quyết trách nhiệm của mình, bao gồm việc không tin tưởng bất kỳ đầu vào tùy ý / không thể biết / có khả năng chưa được xác minh nào mà bạn đưa ra. Nếu không thì bạn đang ở trong thành phố cop-out. Nếu người gọi không kiểm tra thì người gọi đã bắt vít; bạn đã kiểm tra, mông của bạn được bảo hiểm, bạn có thể nói với bất cứ ai đã viết người gọi rằng ít nhất bạn đã làm đúng.
Maximus Minimus
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.