Làm thế nào quan trọng là khởi tạo một biến


9

Nó quan trọng như thế nào để khởi tạo các biến?

Việc khởi tạo đúng cách có tránh rò rỉ bộ nhớ hoặc có lợi thế về hiệu suất không?


14
Nó phụ thuộc vào ngôn ngữ. Trong một số ngôn ngữ, việc ngăn chặn lỗi là khá quan trọng, trong phần còn lại, đó chỉ là một việc nên làm để giúp dễ đọc.
Telastyn

Cảm ơn Telastyn cho đầu vào của bạn. Bạn có thể đặt một trường hợp mà nó trở nên quan trọng tùy thuộc vào ngôn ngữ?
Vivek

4
C ++ là một trong những tai tiếng ở đây. Trong gỡ lỗi, các biến cục bộ được khởi tạo thành 0 (hoặc null) bởi các trình biên dịch chung, nhưng là rác ngẫu nhiên khi biên dịch để phát hành. (mặc dù kiến ​​thức về C ++ của tôi có từ ~ 10 năm trước, mọi thứ có thể đã thay đổi)
Telastyn

Đó là một trường hợp bị đốt cháy một lần hai lần. Vì tôi đã thấy / có lỗi do các biến chưa được khởi tạo, đặc biệt là các con trỏ, nó trở thành một thói quen. Đối với hiệu suất, nó thường không liên quan. Đối với rò rỉ bộ nhớ, không thực sự là một vấn đề.
Mike Dunlavey

1
@Telastyn còn tệ hơn thế nữa. Hành vi không xác định không giới hạn ở trạng thái rác, bất cứ điều gì cũng có thể xảy ra. Trình biên dịch có thể giả định rằng các đường dẫn đọc các biến chưa được khởi tạo là không thể truy cập được và loại bỏ các hiệu ứng "không liên quan" xảy ra trên đường đi.
Caleth

Câu trả lời:


7

Các biến chưa được khởi tạo làm cho một chương trình không xác định. Mỗi lần chương trình chạy, nó có thể hành xử khác nhau. Những thay đổi không liên quan đến môi trường hoạt động, thời gian trong ngày, giai đoạn của mặt trăng và hoán vị như vậy ảnh hưởng đến cách thức và thời điểm các daemon này biểu hiện. Chương trình có thể chạy một triệu lần trước khi lỗi xuất hiện, họ có thể làm điều đó mỗi lần hoặc chạy thêm một triệu lần nữa. Nhiều vấn đề được đưa xuống "trục trặc" và bỏ qua, hoặc báo cáo lỗi từ khách hàng đã đóng là "Không thể thực hiện được". Bạn có thường xuyên khởi động lại máy để 'khắc phục' sự cố không? Bạn có thường xuyên nói với khách hàng "Chưa bao giờ thấy điều đó xảy ra, hãy cho tôi biết nếu bạn gặp lại" - hy vọng (biết) đầy đủ họ sẽ không!

Vì việc tái tạo một khiếm khuyết có thể bên cạnh không thể trong môi trường thử nghiệm, bên cạnh không thể tìm thấy và sửa chữa.

Có thể mất nhiều năm để lỗi xuất hiện, thông thường trong mã được cho là đáng tin cậy và ổn định. Khiếm khuyết được cho là ở mã gần đây hơn - việc theo dõi nó có thể mất nhiều thời gian hơn. Một sự thay đổi trong trình biên dịch, một trình chuyển đổi trình biên dịch, thậm chí thêm một dòng mã có thể thay đổi hành vi.

Khởi tạo biến có lợi thế về hiệu suất rất lớn, không chỉ bởi vì một chương trình hoạt động chính xác nhanh hơn vô hạn, mà các nhà phát triển dành ít thời gian hơn để tìm và sửa các lỗi không nên có và mất nhiều thời gian hơn để thực hiện công việc "thực".

Ưu điểm đáng kể khác của các biến khởi tạo là tác giả ban đầu của mã phải quyết định khởi tạo chúng là gì. Đây không phải luôn luôn là một bài tập tầm thường, và khi không tầm thường, có thể là một dấu hiệu của một thiết kế kém.

Rò rỉ bộ nhớ là một vấn đề khác, nhưng khởi tạo đúng cách không chỉ có thể giúp ngăn ngừa chúng, mà còn có thể giúp phát hiện và tìm nguồn - phụ thuộc ngôn ngữ rất cao và đó thực sự là một câu hỏi riêng biệt đáng để tôi khám phá hơn trong câu trả lời này

Chỉnh sửa: Trong một số ngôn ngữ (ví dụ C #), không thể sử dụng các biến chưa được khởi tạo, vì chương trình sẽ không biên dịch hoặc báo cáo lỗi khi được thực thi, nếu được thực hiện. Tuy nhiên, nhiều ngôn ngữ có các đặc điểm này có giao diện với mã có khả năng không an toàn, do đó, cần thận trọng khi sử dụng các giao diện như vậy để giới thiệu các biến chưa được khởi tạo.


6
Nhiều ngôn ngữ lập trình tự động đặt biến của chúng thành một số giá trị được xác định trước, vì vậy phần lớn những gì bạn nói ở đây không thể áp dụng cho các ngôn ngữ đó.
Robert Harvey

2
Chỉ cần nhắc lại những gì @RobertHarvey đã nói, không điều nào trong số này được áp dụng cho C #. Không có lợi thế về hiệu suất để khởi tạo các biến của bạn khi bạn khai báo chúng và không thể sử dụng biến chưa được khởi tạo, vì vậy bạn không thể đổ lỗi cho các lỗi không thể khắc phục được. (Nó có thể sử dụng một trường lớp chưa được khởi tạo, nhưng nó được thiết lập để một giá trị mặc định và tạo ra một cảnh báo trong trường hợp đó)
Bobson

4
@mattnz - Vấn đề là đối với các ngôn ngữ hoạt động như C # (hoặc Java), một số lời khuyên này là sai lệch hoặc hoàn toàn sai. Như một câu hỏi bất khả tri ngôn ngữ, cần có một phản ứng bất khả tri ngôn ngữ, trong đó có nghĩa là giải quyết các ngôn ngữ mà làm tay cầm khởi tạo các biến một cách an toàn cũng như những người không làm.
Bobson

1
Tôi cũng nói thêm rằng vấn đề biến chưa được khởi tạo không khó tìm vì bất kỳ trình biên dịch / phân tích tĩnh nào cũng sẽ cảnh báo về chúng
jk.

1
Đối với Java (và C # có lẽ) việc khởi tạo sớm các địa phương là không cần thiết và có thể dẫn đến nhiều lỗi hơn. Ví dụ: đặt biến thành null trước khi gán nó một cách có điều kiện đánh bại khả năng của trình biên dịch để cho bạn biết rằng một trong các đường dẫn qua mã có thể không dẫn đến biến được gán.
JimmyJames

7

Khởi tạo một biến như Telastyn chỉ ra có thể ngăn ngừa lỗi. Nếu biến là một kiểu tham chiếu, việc khởi tạo nó có thể ngăn các lỗi tham chiếu null xuống dòng.

Một biến thuộc bất kỳ loại nào có mặc định không null sẽ chiếm một số bộ nhớ để lưu trữ giá trị mặc định.


6

Cố gắng sử dụng một biến chưa được khởi tạo luôn là một lỗi, vì vậy sẽ rất hợp lý để giảm thiểu khả năng xảy ra lỗi đó.

Có lẽ các ngôn ngữ lập trình tiếp cận phổ biến nhất được sử dụng để giảm thiểu vấn đề là tự động khởi tạo một giá trị mặc định, vì vậy ít nhất nếu bạn quên khởi tạo một biến, nó sẽ giống như 0thay vì một cái gì đó giống như 0x16615c4b.

Điều này giải quyết một tỷ lệ lớn các lỗi, nếu bạn tình cờ cần một biến được khởi tạo thành 0. Tuy nhiên, sử dụng một biến được khởi tạo thành một giá trị không chính xác cũng tệ như sử dụng một biến không được khởi tạo. Trong thực tế, đôi khi nó có thể còn tồi tệ hơn, bởi vì lỗi có thể tinh tế hơn và khó phát hiện hơn.

Các ngôn ngữ lập trình hàm giải quyết vấn đề này bằng cách không chỉ không cho phép các giá trị chưa được khởi tạo, mà bằng cách không cho phép gán lại hoàn toàn. Điều đó giúp loại bỏ vấn đề và hóa ra không phải là một hạn chế nghiêm trọng như bạn nghĩ. Ngay cả trong các ngôn ngữ phi chức năng, nếu bạn chờ khai báo một biến cho đến khi bạn có một giá trị chính xác để khởi tạo nó, mã của bạn có xu hướng mạnh mẽ hơn nhiều.

Về hiệu suất, nó có thể không đáng kể. Ở mức tồi tệ nhất với các biến chưa được khởi tạo, bạn có thêm một nhiệm vụ và gắn một số bộ nhớ lâu hơn mức cần thiết. Trình biên dịch tốt có thể tối ưu hóa sự khác biệt trong rất nhiều trường hợp.

Rò rỉ bộ nhớ hoàn toàn không liên quan, mặc dù các biến được khởi tạo đúng có xu hướng nằm trong phạm vi trong một khoảng thời gian ngắn hơn, và do đó có thể ít có khả năng một lập trình viên vô tình bị rò rỉ.


Luôn luôn? Bạn có nghĩa là "luôn luôn" như trong "Làm thế nào một thông báo Valgrind cố định hiển thị OpenSSL bên cạnh vô dụng" marc.info/?t=114651088900003&r=1&w=2 ? Hay bạn có nghĩa là người khác, "hầu như luôn luôn"?
JensG

1
Tôi có thể nghĩ về ba ngôn ngữ cho phép các biến chưa được khởi tạo mà không có lỗi, một trong số đó sử dụng như vậy cho mục đích ngôn ngữ.
DougM

Tôi sẽ quan tâm đến các chi tiết cụ thể. Tôi nghi ngờ trong những trường hợp đó, các biến không thực sự chưa được khởi tạo, nhưng được khởi tạo theo cách khác với trực tiếp bởi lập trình viên tại trang web khai báo. Hoặc chúng được chỉ định bởi một số phương tiện gián tiếp trước khi bị hủy đăng ký.
Karl Bielefeldt

5

Khởi tạo, ngụ ý rằng giá trị ban đầu quan trọng. Nếu giá trị ban đầu có vấn đề, thì có, rõ ràng bạn phải chắc chắn rằng nó được khởi tạo. Nếu nó không thành vấn đề, điều đó ngụ ý rằng nó sẽ được khởi tạo sau.

Khởi tạo không cần thiết gây ra chu kỳ CPU lãng phí. Mặc dù các chu trình lãng phí này có thể không quan trọng trong một số chương trình nhất định, nhưng trong các chương trình khác, mọi chu kỳ đơn lẻ đều quan trọng vì tốc độ là mối quan tâm chính. Vì vậy, điều rất quan trọng là phải hiểu mục tiêu hiệu suất của một người là gì và liệu các biến có cần được khởi tạo hay không.

Rò rỉ bộ nhớ là một vấn đề hoàn toàn khác, thường liên quan đến chức năng cấp phát bộ nhớ để phát hành và sau đó tái chế các khối bộ nhớ. Hãy nghĩ về một bưu điện. Bạn đi và yêu cầu một hộp thư. Họ cho bạn một cái. Bạn yêu cầu một cái khác. Họ cho bạn một cái khác. Quy tắc là khi bạn hoàn thành việc sử dụng hộp thư mà bạn cần trả lại. Nếu bạn quên trả lại, họ vẫn nghĩ bạn có nó và hộp không thể được sử dụng lại bởi bất kỳ ai khác. Vì vậy, có một đoạn bộ nhớ bị trói và không được sử dụng, và đây là những gì được gọi là rò rỉ bộ nhớ. Nếu bạn cứ hỏi hộp vào một lúc nào đó bạn sẽ hết bộ nhớ. Tôi đã quá đơn giản hóa điều này, nhưng đây là ý tưởng cơ bản.


-1 bạn đang xác định lại ý nghĩa khởi tạo trong ngữ cảnh này.
Pieter B

@Pieter B, tôi không hiểu bình luận của bạn. Xin vui lòng, nếu bạn sẽ, nói tôi là như thế nào, "xác định lại ý nghĩa khởi tạo trong ngữ cảnh này". Cảm ơn bạn
Chế độ xem hình elip

Đọc câu của riêng bạn, đó là lý do vòng tròn: "Đang khởi tạo, ngụ ý rằng giá trị ban đầu có vấn đề. Nếu giá trị ban đầu có vấn đề, thì có, rõ ràng bạn phải chắc chắn rằng nó được khởi tạo. Nếu không quan trọng, điều đó ngụ ý rằng nó sẽ nhận được khởi tạo sau. "
Pieter B

@Pieter B, Một số người khởi tạo như một quy tắc chung thay vì lý do lập trình, tức là họ khởi tạo cho dù giá trị ban đầu có quan trọng hay không. Đây không phải là trái tim của OQ: Việc khởi tạo một biến quan trọng đến mức nào? Dù sao, bạn đã được bỏ phiếu ở đây.
Chế độ xem hình elip

2

Như những người khác nói, nó phụ thuộc vào ngôn ngữ. Nhưng tôi sẽ trình bày các ý tưởng Java (và Java hiệu quả) của mình về việc khởi tạo các biến. Chúng nên được sử dụng cho nhiều ngôn ngữ cấp cao khác.

Các hằng số và biến lớp

Các biến lớp - được đánh dấu bằng staticJava - giống như các hằng số. Các biến này thường là cuối cùng và được khởi tạo trực tiếp sau khi định nghĩa bằng cách sử dụng =hoặc từ trong khối khởi tạo lớp static { // initialize here }.

Lĩnh vực

Vì ở nhiều cấp độ cao hơn và các ngôn ngữ kịch bản sẽ được tự động gán một giá trị mặc định. Đối với số và charđây sẽ là giá trị bằng không. Đối với String và các đối tượng khác, nó sẽ được null. Bây giờ nulllà nguy hiểm và nên được sử dụng một cách tiết kiệm. Vì vậy, các trường này nên được đặt thành một giá trị hợp lệ càng sớm càng tốt. Các constructor thường là một nơi hoàn hảo cho việc này. Để đảm bảo rằng các biến được đặt trong hàm tạo và không thay đổi sau đó, bạn có thể đánh dấu chúng bằng finaltừ khóa.

Hãy thử và chống lại sự thôi thúc sử dụng nullnhư một loại cờ hoặc giá trị đặc biệt. Tốt hơn là ví dụ bao gồm một trường cụ thể để giữ trạng thái. Một trường có tên statesử dụng các giá trị Stateliệt kê sẽ là một lựa chọn tốt.

Các tham số phương thức

Vì các thay đổi đối với các giá trị của tham số (có thể là tham chiếu đến các đối tượng hoặc các loại cơ bản như số nguyên, v.v.) sẽ không được người gọi nhìn thấy, các tham số nên được đánh dấu là final. Điều này có nghĩa là các giá trị của biến không thể thay đổi. Lưu ý rằng giá trị của các thể hiện đối tượng thể thay đổi có thể được thay đổi, tham chiếu không thể thay đổi để trỏ đến một đối tượng khác hoặc nullmặc dù.

Biến cục bộ

Biến cục bộ không được tự động khởi tạo; chúng cần được khởi tạo trước khi giá trị của chúng có thể được sử dụng. Một phương pháp để đảm bảo rằng biến của bạn được khởi tạo là khởi tạo chúng thành một loại giá trị mặc định trực tiếp. Tuy nhiên đây là điều bạn không nên làm. Hầu hết thời gian giá trị mặc định không phải là giá trị bạn mong đợi.

Sẽ tốt hơn nhiều khi chỉ xác định chính xác biến bạn cần biến. Nếu biến chỉ lấy một giá trị duy nhất (đúng với hầu hết các biến trong mã tốt) thì bạn có thể đánh dấu biến đó final. Điều này đảm bảo rằng biến cục bộ được gán chính xác một lần, không phải 0 lần hoặc hai lần. Một ví dụ:

public static doMethod(final int x) {
    final int y; // no assignment yet, it's final so it *must* be assigned
    if (x < 0) {
        y = 0;
    } else if (x > 0) {
        y = x;
    } else {
        // do nothing <- error, y not assigned if x = 0
        // throwing an exception here is acceptable though
    }
}

Lưu ý rằng nhiều ngôn ngữ sẽ cảnh báo bạn nếu một biến vẫn chưa được khởi tạo trước khi sử dụng. Kiểm tra thông số kỹ thuật ngôn ngữ và diễn đàn để xem bạn có cần lo lắng không.


1

Không có vấn đề với các biến uninitializing.

Vấn đề chỉ là khi bạn đọc một biến chưa được viết.

Tùy thuộc vào trình biên dịch và / hoặc loại biến, việc khởi tạo được thực hiện khi khởi động ứng dụng. Hay không.

Nó được sử dụng phổ biến để không dựa vào khởi tạo tự động.


0

Khởi tạo các biến (ngầm hoặc rõ ràng) là rất quan trọng. Không khởi tạo một biến luôn luôn là một lỗi (tuy nhiên, chúng có thể được khởi tạo ngầm, xem bên dưới). Các trình biên dịch hiện đại như trình biên dịch C # (làm ví dụ) coi đây là một lỗi và sẽ không cho phép bạn thực thi mã. Một biến chưa được khởi tạo chỉ đơn giản là vô dụng và có hại. Trừ khi bạn đang tạo một trình tạo số ngẫu nhiên, bạn mong đợi từ một đoạn mã để tạo ra kết quả xác định và có thể lặp lại. Điều này chỉ có thể đạt được nếu bạn bắt đầu làm việc với các biến khởi tạo.

Câu hỏi thực sự thú vị là liệu một biến được khởi tạo tự động hay bạn có phải thực hiện thủ công hay không. Nó phụ thuộc vào ngôn ngữ được sử dụng. Ví dụ, trong C #, các trường, tức là "biến" ở cấp lớp, luôn được tự động khởi tạo thành giá trị mặc định cho loại biến đó default(T). Giá trị này tương ứng với một mẫu bit bao gồm tất cả các số 0. Đây là một phần của đặc tả ngôn ngữ và không chỉ là một chi tiết kỹ thuật của việc thực hiện ngôn ngữ. Do đó bạn có thể dựa vào nó một cách an toàn. Sẽ không an toàn khi khởi tạo một biến một cách rõ ràng nếu (và chỉ khi) đặc tả ngôn ngữ nói rằng nó được khởi tạo ngầm.Nếu bạn muốn một giá trị khác, bạn phải khởi tạo biến rõ ràng. Tuy nhiên; trong các biến cục bộ C #, tức là các biến được khai báo trong các phương thức, không được khởi tạo tự động và bạn phải luôn luôn khởi tạo biến một cách rõ ràng.


2
đây không phải là một câu hỏi cụ thể về C #
DougM

@DougM: Tôi biết. Đó không phải là câu trả lời cụ thể của C #, tôi chỉ lấy C # làm ví dụ.
Olivier Jacot-Descombes

Không phải tất cả các ngôn ngữ yêu cầu các biến được khởi tạo rõ ràng. Câu lệnh "không khởi tạo luôn là lỗi" của bạn là sai và không thêm bất kỳ sự rõ ràng nào vào câu hỏi. bạn có thể muốn xem lại câu trả lời của bạn
DougM

@DougM: Bạn đã giám sát câu của tôi "Câu hỏi thực sự thú vị là liệu một biến được khởi tạo tự động hay liệu bạn phải làm điều đó bằng tay."?
Olivier Jacot-Descombes

bạn có nghĩa là một chôn ở giữa một đoạn? Đúng. Bạn nên làm cho nó nổi bật hơn và thêm một vòng loại vào yêu cầu "luôn luôn" của bạn.
DougM
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.