Không phải là dấu chấm phẩy (';') sau khi khai báo hàm trong C ++?


174

Gần đây tôi mới làm một bài kiểm tra lập trình trung gian và một trong những câu hỏi tôi đã sai là như sau:

Dấu chấm phẩy (';') là không cần thiết sau khi khai báo hàm.

Đúng hay sai.

Tôi đã chọn "false" (và vui lòng sửa lại cho tôi nếu tôi sai vì tôi cảm thấy mình sắp phát điên), một khai báo hàm là những gì bạn viết trước định nghĩa (ở đầu mã) để trình biên dịch biết hàm gọi trước cả khi gọi nó, và một định nghĩa hàm là cái tạo nên toàn bộ hàm.

I E,

Tờ khai:

int func();

Định nghĩa:

int func() {
  return 1;
}

Không nên trả lời cho điều này là sai?


41
Một định nghĩa cũng là một tuyên bố. Nhưng tôi sẽ nói câu trả lời của bạn là chính xác.

216
Đó là một câu hỏi khó khăn về nitpicking và không ảnh hưởng đến khả năng lập trình tốt của bất cứ ai.
phonetagger

40
Tôi luôn tìm thấy những câu hỏi, kết quả là hai tiêu cực, khó hiểu. Trong tâm trí của tôi, những câu hỏi như vậy được thiết kế để sinh viên đi lên. Tại sao câu hỏi không thể được hình thành theo cách sau: "Dấu chấm phẩy (';') luôn luôn cần thiết sau khi khai báo hàm. Đúng hay Sai."? : /
Algirdas Preidžius

18
@phonetagger Tất cả sự nhầm lẫn này diễn ra cho thấy câu hỏi đó tệ đến mức nào.
François Andrieux

34
Dao cạo của Hanlon gợi ý rằng tác giả của bài kiểm tra đã trộn lẫn giữa "tuyên bố" và "định nghĩa".
Sneftel

Câu trả lời:


161

Bạn có thể có một tình huống trong đó bạn khai báo và xác định hàm trong một bước, tức là nếu bạn bao gồm định nghĩa hàm tại điểm mà bạn khai báo nó. Vì vậy, về mặt kỹ thuật tôi cho là đúng là chính xác. Nhưng câu hỏi được diễn đạt theo cách mà tôi sẽ trả lời nó theo cách bạn đã làm.


10
Tôi cho rằng sự thật không đúng vì lý do bạn đưa ra. Nếu có những trường hợp khi cần một dấu chấm phẩy thì đó là sai (hoặc không đúng). Đúng là tuyệt đối với tôi, nếu có những trường hợp rõ ràng khi cần thì bạn không thể nói đúng.
I Funball

16
@IFunball Lập luận tốt. Ngôn ngữ tự nhiên ngu ngốc. Câu "Dấu chấm phẩy (';') không cần thiết sau khi khai báo hàm" có thể được đọc là "Dấu chấm phẩy (';') không (bao giờ) cần sau khi khai báo hàm" hoặc là "Dấu chấm phẩy (';' ) không (luôn luôn) cần thiết sau khi khai báo hàm ". Liệu có đủ điều kiện tuyên bố là bản lề đúng hay sai khi chọn giải thích. Nói một cách nghiêm túc câu hỏi không rõ ràng và do đó không có câu trả lời rõ ràng.
Peter - Tái lập Monica

6
@IFunball Đó là vì "tuyên bố", không có ngữ cảnh và không có tuyên bố rằng chúng tôi đang hợp pháp hóa ngôn ngữ, thường được hiểu là "tuyên bố không xác định". Câu hỏi không công bằng.
Các cuộc đua nhẹ nhàng trong quỹ đạo

2
Bất kỳ câu hỏi thi nào không rõ ràng đối với người biết nội dung đang được kiểm tra đều bị lỗi.
Nat

2
Âm thanh như chúng ta cần thêm một điều khoản hành vi không xác định vào ngôn ngữ tiếng Anh
Nick Mertin

147

Ngoài điều "một định nghĩa cũng là một tuyên bố", sau đây là C ++ hợp pháp:

int f(), g();

Điều này khai báo hai hàm, fg, cả hai đều không có đối số và với kiểu trả về int, nhưng định nghĩa fkhông được tuân theo (ngay lập tức) bởi dấu chấm phẩy. Tương tự như vậy, đây là hợp pháp:

int f(), i = 42;

Nhưng nó thực sự không được phép bỏ qua dấu chấm phẩy hoàn toàn trong những trường hợp này, vì vậy sẽ hơi ngạc nhiên nếu được lấy làm ví dụ về một tuyên bố mà không có dấu chấm phẩy sau. Trong thực tế, sau đây là bất hợp pháp:

void *p, f() {}

Khác với khai báo hàm (chỉ), một định nghĩa hàm không thể được kết hợp với bất kỳ khai báo hoặc định nghĩa nào khác cho cùng một chỉ định kiểu . (Nếu điều này là hợp pháp, nó sẽ xác định cả a void *pvà a void f() {}.)

Trong mọi trường hợp, đây dường như là một loại câu hỏi "gotcha" không nên có trong một bài kiểm tra lập trình trung gian.

(Ồ, nhân tiện, xin đừng viết mã như thế int f(), i = 42;.)


2
Cũng có thể sử dụng typedef để xác định loại hàm và sau đó sử dụng hàm đó để khai báo nhiều hàm cùng một lúc, ví dụ: typedef int fooProc(int); fooProc a,b.c.d.e;Tôi không chắc tại sao các tiêu đề chuẩn cho trình biên dịch dựa trên ổ đĩa mềm lại không làm điều đó trở lại trong ngày, vì tôi nghĩ nó sẽ cho phép các tệp tiêu đề nhỏ hơn khá nhiều và do đó xử lý nhanh hơn.
supercat

Cũng xem xét int f(int(&g)(),int(*h)()){return g()+h();}Điều này có ba khai báo hàm, một trong số đó được theo sau bởi dấu ngoặc nhọn mở, một dấu phẩy khác và dấu phẩy thứ ba bằng dấu ngoặc đơn đóng.
David Hammen

1
@DavidHammen: Điều đó không nghiêm ngặt khai báo các chức năng khác int f(stuff). Ngay cả trong phạm vi của hàm, glà một biến tự động của kiểu tham chiếu đến hàmhlà một con trỏ tới hàm .
Peter Cordes

83

Các câu trả lời và bình luận khác gọi ra một vài trong số nhiều cách mà đây là một câu hỏi kinh khủng, gây hiểu lầm và viết sai. Nhưng có một vấn đề khác mà chưa ai xác định được. Câu hỏi là:

Dấu chấm phẩy (';') là không cần thiết sau khi khai báo hàm. Đúng hay sai.

OK, chúng ta hãy xem một khai báo hàm:

int func();       /* */
/*           ^       */
/*           |       */
/* That whitespace is "after the function declaration". */

Toàn bộ điều đó là tuyên bố . Khai báo không phải int func()và sau đó a ;. Khai báo là int func();và sau đó là khoảng trắng.

Vì vậy, câu hỏi là: một dấu chấm phẩy cần thiết sau khi khai báo ? Dĩ nhiên là không. Tuyên bố đã có một dấu chấm phẩy trong đó chấm dứt nó. Một dấu chấm phẩy sau khi tuyên bố sẽ là vô nghĩa. Ngược lại, int func(); ;sẽ là một dấu chấm phẩy sau khi khai báo hàm .

Câu hỏi gần như chắc chắn có ý định đặt câu hỏi "đúng hay sai: mã thông báo cuối cùng trong khai báo hàm luôn là dấu chấm phẩy" Nhưng đó không phải là câu hỏi mà họ đã viết, vì tác giả của bài kiểm tra không suy nghĩ rõ ràng về vấn đề.

Lời khuyên của tôi là tránh hoàn toàn các câu hỏi ngôn ngữ lập trình. Họ thật kinh khủng.


Thực tế thú vị, trong khi chúng ta đang ở chủ đề này. Trong C #, tất cả đều hợp pháp:

class C {}
class D {};
struct E {}
struct F {};

Trong C #, một khai báo lớp hoặc cấu trúc có thể kết thúc bằng dấu chấm phẩy, hoặc không, theo ý của bạn. Tính năng nhỏ kỳ lạ này đã được thêm vào vì lợi ích của các lập trình viên C / C ++ đến với C #, những người có nó trong tầm tay của họ rằng các khai báo kết thúc bằng một dấu chấm phẩy vô nghĩa; nhóm thiết kế không muốn trừng phạt họ vì thói quen này. :-)


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Samuel Liew

25

Bạn cũng có thể khai báo một hàm như thế này:

int func(){
    return 1;
}

Câu nói rất mơ hồ. Câu trả lời đúng phải là: nó phụ thuộc vào cách bạn khai báo hàm.

Dù sao, tôi cũng đã chọn sai và có lẽ bạn có thể báo cáo câu hỏi cho ai đó.


3
Dù sao, đừng đặt điều đó ở cấp độ cá nhân. Điều quan trọng là bạn đã hiểu cách hoạt động của định nghĩa khai báo hàm, vì vậy đừng lo lắng về nó quá nhiều, chỉ cần đảm bảo rằng câu hỏi ít nhất sẽ được kiểm tra và tiếp tục
Luca Corsini

11
Chắc chắn rồi. Thành thật mà nói, tôi đã học được nhiều hơn về định nghĩa khai báo hàm từ việc đặt câu hỏi sai hơn là tôi đã hiểu đúng.
Logan

1
@Logan đừng quá lo lắng. Nếu bạn biết cách viết và đọc một chức năng đó là tất cả những gì bạn cần. Cá nhân tôi ghét những loại câu hỏi mà 1. không được xác định rõ 2. kiểm tra kiến ​​thức lý thuyết của bạn về cú pháp. Đối với tôi nó giống như bộ nhớ cơ bắp. Khi tôi viết từng chữ số một cách dễ dàng vào phím, nó sẽ đi, nhưng nếu bạn đưa cho tôi một bài kiểm tra về những phím nào nên bấm chữ số, tôi sẽ hoàn toàn vô vọng nếu không có bàn phím để thực hiện hành động ...
bolov

2
... Viết cú pháp chung (ví dụ như một hàm) sẽ trở thành bản chất thứ hai đối với bạn. Và khi bạn sẽ làm hỏng nó bởi vì bạn vừa chuyển đổi ngôn ngữ, thì ... intellisense và cú pháp tô sáng sẽ tạo ra các giải pháp nhanh chóng và hiệu quả. Đầu tư thời gian và năng lượng của bạn vào một cái gì đó hữu ích hơn.
bolov

20

Dấu chấm phẩy (';') là không cần thiết sau khi khai báo hàm.

Đúng hay sai.

Đúng . Một dấu chấm phẩy là không cần thiết sau bất kỳ tuyên bố. Cũng không theo bất kỳ định nghĩa. Cũng không sau bất kỳ tuyên bố.

Nhiều loại khai báo phải kết thúc bằng dấu chấm phẩy, như cú pháp trong phần 7 [dcl.dcl] chỉ định. Nhưng không bao giờ có nhu cầu viết một cái thứ hai sau đó.


1
Tôi thấy rằng Eric Lippert đã tranh luận về điểm này. Tôi đoán tất cả các upvote làm cho tôi bỏ qua nó. Hãy bỏ phiếu của bạn ở đó.
Marc van Leeuwen

Khá nhiều câu hỏi đặt ra, "X luôn luôn đúng: đúng hay sai?" sẽ có câu trả lời "sai." Heck, không cần phải có dấu chấm phẩy ở bất cứ đâu ; trình biên dịch có thể phàn nàn và từ chối biên dịch chương trình của bạn, nhưng đó hầu như không phải là ngày tận thế; Tôi sẽ không gọi nó là một nhu cầu cơ bản . ;)
Quuxplusone

@Quuxplusone nếu trình biên dịch từ chối chương trình của bạn, chương trình của bạn không có bất kỳ khai báo hàm nào trong đó :)
Ben Millwood

6

Điều này phụ thuộc vào việc chúng ta khai báo hay xác định hàm. Nếu chúng ta khai báo hàm, chúng ta cần bao gồm dấu chấm phẩy ( ;) và nếu chúng ta đang xác định hàm, dấu chấm phẩy là không cần thiết.

Một tuyên bố là như thế này:

int add(int, int);

Và một định nghĩa là như thế này:

int add(int a, int b)
{
    // ...
}

10
Vấn đề với câu trả lời này là nó gợi ý rằng các định nghĩa và khai báo là loại trừ lẫn nhau. Trong thực tế, mỗi định nghĩa là một tuyên bố; định nghĩa là một tập hợp con của khai báo.
MSalters

6

Mặc dù tôi đồng ý với hầu hết tất cả các câu trả lời khác, nói rằng câu hỏi được diễn đạt rất mơ hồ và câu trả lời của bạn là đúng về mặt kỹ thuật, cho phép tôi đưa ra một quan điểm khác:

Đây là cách tôi luôn gọi họ:

void func();  // The function prototype

...

void func()
{
    // The function definition
}

Tôi cho rằng câu hỏi đã được tạo ra với thuật ngữ này trong tâm trí.

Định nghĩa và tuyên bố đều là cùng một khái niệm trong mắt tôi. "Tôi xác định x = y" == "Tôi khai báo x = y".

Nhưng tất nhiên, có một sự khác biệt lớn giữa nguyên mẫu hàm (trên cùng) và định nghĩa thực tế của hàm.


Đối với tôi nguyên mẫu của bạn là tuyên bố dựa trên cách tôi đã học (dù không nói bạn sai), nhưng sau đó tôi cũng mong đợi một nguyên mẫu chỉ định số lượng và loại đối số hoặc bỏ trống, nhưng tôi hy vọng bạn đã bỏ qua điều đó cho ngắn gọn.
David S

David S: Vâng, tất nhiên, nó cũng sẽ chứa số lượng và loại đối số, nhưng tôi thực sự đã bỏ qua chúng để cho ngắn gọn (lưu ý rằng cũng không có đối số trong khai báo hàm thực tế). Tuy nhiên, tôi không thực sự đồng ý khi bạn nói khai báo hàm đầy đủ được gọi là nguyên mẫu. Tôi trích dẫn Wikipedia: "một nguyên mẫu hàm hoặc giao diện hàm là một khai báo của hàm chỉ định tên và kiểu chữ ký của hàm (arity, kiểu dữ liệu của tham số và kiểu trả về), nhưng bỏ qua phần thân hàm."
Opifex

@DavidS: Trong C ++, khai báo hàm luôn là nguyên mẫu (hoặc định nghĩa) và void func();hoàn toàn tương đương với void func(void);. Điều này rất khác với C , trong void func();đó không nói cho trình biên dịch bất cứ điều gì về các đối số và không giống như void func(void);. Một nguyên mẫu hoặc định nghĩa sau này là một ý tưởng tốt, nếu không, người gọi phải áp dụng các chương trình khuyến mãi arg mặc định (ví dụ: float -> double và các kiểu số nguyên hẹp cho int. Các quy tắc tương tự như đối với các hàm biến đổi.)
Peter Cordes

Tôi xin lỗi, cuối cùng tôi đã xem xét một cái gì đó liên quan đến C và không lưu ý sự thay đổi ngôn ngữ. Tôi sẽ không xóa bình luận của mình vì lợi ích của sự rõ ràng, nhưng xem xét nó rút lại.
David S

6

Thật đáng tiếc cho câu hỏi mà bạn đã không nói "ngay sau đó". Ví dụ, chúng ta có thể viết điều này:

int func()  /* My function */ ;

Hoặc tôi có thể viết:

int func()
int a = 42;

Trong trường hợp đầu tiên, dấu chấm phẩy không trực tiếp sau khai báo, nhưng điều đó sẽ ổn.

Trong trường hợp thứ hai có dấu chấm phẩy "sau" khai báo, nhưng không trực tiếp sau.

Tôi nghĩ Eric Lippert có ý kiến ​​đúng trong câu trả lời của mình .

Nó giống như nói "nên có một khoảng thời gian sau khi kết thúc một câu bằng tiếng Anh?". Có thể cho rằng, một câu đã có một khoảng thời gian ở cuối (nếu không nó sẽ không phải là một câu) và do đó không nên có một khoảng thời gian sau câu ..


4
Đẹp. Kết thúc câu đó với một khoảng thời gian thêm. Tôi thấy những gì bạn đã làm ở đó.
David S

2
int func() int a=42;không biên dịch. Bạn cần một dấu phẩy, không phải dấu phẩy khác int. Xem câu trả lời của @ Arne được đăng hơn một ngày trước đó. Điều mới duy nhất trong câu trả lời này là đoạn cuối cùng, tương tự như câu tiếng Anh.
Peter Cordes

1
Tôi không nói ví dụ thứ hai được biên soạn. Tôi đã chỉ ra rằng nói rằng một dấu chấm phẩy là cần thiết "sau" tuyên bố là mơ hồ. Ví dụ của tôi có một dấu chấm phẩy sau khi khai báo nhưng nó không được biên dịch.
Nick Gammon

1
Vấn đề tương tự này xảy ra trong các thông báo lỗi; một ví dụ yêu thích từ C # là " Tham số params phải là tham số cuối cùng trong danh sách tham số chính thức ". Bây giờ, giả sử thay vì tôi đã nói "Một frob phải là gloob cuối cùng trong danh sách gloob". Điều này có nghĩa là gì không , mục cuối cùng phải là số ít, như số chẵn có thể có bất kỳ số nào là 02468, nhưng một trong số đó phải là số cuối cùng, hoặc ...
Eric Lippert

... (3) một danh sách gloob có thể có 0 hoặc một frobs, và nếu nó có một danh sách, nó sẽ ở cuối? Nếu bạn không biết ngữ cảnh, tôi nghĩ rằng (1) là lời giải thích hợp lý nhất, nhưng trong trường hợp "tham số params", (3) là lời giải thích chính xác. Nhiều mô tả không chính thức về các yếu tố ngôn ngữ lập trình có một thuộc tính mà bạn bè biên tập kỹ thuật của tôi gọi là "COIK" - Chỉ xóa nếu biết. Nếu bạn chưa hiểu kỹ về tài liệu, một mô tả về nó là vô ích đối với bạn, nhưng nếu bạn đã hiểu kỹ về nó, bạn không cần mô tả!
Eric Lippert

4

Bạn chỉ có thể sử dụng ;cho các nguyên mẫu.


4

Đó là một câu hỏi khó, nhưng họ đã sử dụng từ khai báo có nghĩa như thế này:

int example();

Vì vậy, nó đúng trong trường hợp này.

Nếu họ đã sử dụng từ thực thi thì đó là sai.


2

Dấu chấm phẩy (;) được sử dụng để báo cho trình biên dịch rằng sau dấu chấm phẩy này (;), một câu lệnh mới bắt đầu.

Vì vậy, tôi nghĩ rằng dấu chấm phẩy (;) chỉ được yêu cầu trong khi khai báo hàm. Vì vậy, theo tôi, câu trả lời sẽ là sự thật.


Tuyên bố không phải là tuyên bố mặc dù.
HolyBlackCat

nhưng sau khi khai báo hàm, chúng ta đang thực thi một dòng mã mới bằng trình biên dịch. Vì vậy, tôi nghĩ rằng trước khi thực hiện một dòng trình biên dịch mã mới phải biết dòng mã trước đó chỉ kết thúc ở đâu thì trình biên dịch có thể tạo mã gốc (tức là 0101).
Jatinder

2

Khi các hàm được định nghĩa trước hàm main () :

  • Không cần dấu chấm phẩy vì chức năng đã được xác định

Khi các hàm được định nghĩa sau hàm main () :

  • Cần dấu chấm phẩy vì bạn đang tạo nguyên mẫu cho hàm đó và báo cho trình biên dịch rằng hàm đó thoát.
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.