Hàm C này phải luôn trả về false, nhưng nó không

319
Dimitri Podborski 2016-04-08 02:27.

Tôi tình cờ gặp một câu hỏi thú vị trong một diễn đàn cách đây khá lâu và tôi muốn biết câu trả lời.

Hãy xem xét hàm C sau:

f1.c

#include <stdbool.h>

bool f1()
{
    int var1 = 1000;
    int var2 = 2000;
    int var3 = var1 + var2;
    return (var3 == 0) ? true : false;
}

Điều này sẽ luôn trở lại falsekể từ đó var3 == 3000. Các mainchức năng trông như thế này:

C chính

#include <stdio.h>
#include <stdbool.h>

int main()
{
    printf( f1() == true ? "true\n" : "false\n");
    if( f1() )
    {
        printf("executed\n");
    }
    return 0;
}

f1()luôn phải trả về falsenên người ta sẽ mong đợi chương trình chỉ in một sai ra màn hình. Nhưng sau khi biên dịch và chạy nó, thực thi cũng được hiển thị:

$ gcc main.c f1.c -o test
$ ./test
false
executed

Tại sao vậy? Mã này có một số loại hành vi không xác định?

Lưu ý: Tôi đã biên dịch nó với gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2.

4 answers

405
Lundin 2016-04-08 03:09.

Như đã lưu ý trong các câu trả lời khác, vấn đề là bạn sử dụng gcckhông có tùy chọn trình biên dịch nào được đặt. Nếu bạn làm điều này, nó sẽ mặc định là "gnu90", đây là một triển khai không chuẩn của tiêu chuẩn C90 cũ, đã bị rút từ năm 1990.

Trong tiêu chuẩn C90 cũ, có một lỗ hổng lớn trong ngôn ngữ C: nếu bạn không khai báo một nguyên mẫu trước khi sử dụng một hàm, nó sẽ mặc định là int func ()(trong đó ( )có nghĩa là "chấp nhận bất kỳ tham số nào"). Điều này thay đổi quy ước gọi của hàm func, nhưng nó không thay đổi định nghĩa hàm thực. Vì kích thước của boolintkhác nhau, mã của bạn gọi hành vi không xác định khi hàm được gọi.

Hành vi vô nghĩa nguy hiểm này đã được khắc phục vào năm 1999, với việc phát hành tiêu chuẩn C99. Khai báo hàm ẩn đã bị cấm.

Thật không may, GCC lên đến phiên bản 5.xx vẫn sử dụng tiêu chuẩn C cũ theo mặc định. Có lẽ không có lý do gì bạn nên biên dịch mã của mình dưới dạng chuẩn C. Vì vậy, bạn phải nói rõ ràng với GCC rằng nó nên biên dịch mã của bạn dưới dạng mã C hiện đại, thay vì một số GNU cũ hơn 25 năm tuổi, không chuẩn. .

Khắc phục sự cố bằng cách luôn biên dịch chương trình của bạn dưới dạng:

gcc -std=c11 -pedantic-errors -Wall -Wextra
  • -std=c11 yêu cầu nó thực hiện một nỗ lực nửa vời để biên dịch theo tiêu chuẩn C (hiện tại) (được gọi là C11).
  • -pedantic-errors bảo nó tận tình làm những điều trên và đưa ra lỗi trình biên dịch khi bạn viết mã sai vi phạm tiêu chuẩn C.
  • -Wall có nghĩa là cung cấp cho tôi một số cảnh báo bổ sung có thể tốt để có.
  • -Wextra có nghĩa là cung cấp cho tôi một số cảnh báo bổ sung khác có thể hữu ích.
143
dbush 2016-04-08 02:34.

Bạn không có một nguyên mẫu nào được khai báo f1()trong main.c, vì vậy nó được định nghĩa ngầm là int f1(), nghĩa là nó là một hàm nhận một số lượng đối số không xác định và trả về một int.

Nếu intboolcó kích thước khác nhau, điều này sẽ dẫn đến hành vi không xác định . Ví dụ, trên máy của tôi, intlà 4 byte và boollà một byte. Vì hàm được định nghĩa để trả về bool, nó sẽ đặt một byte vào ngăn xếp khi nó trả về. Tuy nhiên, vì nó được khai báo ngầm là trả về inttừ main.c, hàm gọi sẽ cố gắng đọc 4 byte từ ngăn xếp.

Các tùy chọn trình biên dịch mặc định trong gcc sẽ không cho bạn biết rằng nó đang làm điều này. Nhưng nếu bạn biên dịch với -Wall -Wextra, bạn sẽ nhận được điều này:

main.c: In function ‘main’:
main.c:6: warning: implicit declaration of function ‘f1’

Để khắc phục điều này, hãy thêm khai báo cho f1trong main.c, trước main:

bool f1(void);

Lưu ý rằng danh sách đối số được đặt thành void, điều này cho trình biên dịch biết rằng hàm không nhận đối số, trái ngược với danh sách tham số trống có nghĩa là một số lượng đối số không xác định. Định nghĩa f1trong f1.c cũng nên được thay đổi để phản ánh điều này.

37
Owen 2016-04-09 23:24.

Tôi nghĩ thật thú vị khi thấy sự không khớp về kích thước được đề cập trong câu trả lời tuyệt vời của Lundin thực sự xảy ra ở đâu.

Nếu bạn biên dịch với --save-temps, bạn sẽ nhận được các tệp lắp ráp mà bạn có thể xem. Đây là phần f1()thực hiện == 0so sánh và trả về giá trị của nó:

cmpl    $0, -4(%rbp)
sete    %al

Phần trở lại là sete %al. Trong quy ước gọi x86 của C, các giá trị trả về 4 byte hoặc nhỏ hơn (bao gồm intbool) được trả về thông qua thanh ghi %eax. %allà byte thấp nhất của %eax. Vì vậy, 3 byte trên %eaxđược để ở trạng thái không được kiểm soát.

Hiện tại main():

call    f1
testl   %eax, %eax
je  .L2

Kiểm tra này xem toàn bộ các %eaxbằng không, bởi vì nó nghĩ rằng nó đang thử nghiệm một int.

Thêm một khai báo hàm rõ ràng sẽ thay đổi main()thành:

call    f1
testb   %al, %al
je  .L2

đó là những gì chúng tôi muốn.

27
jdarthenay 2016-04-08 02:37.

Vui lòng biên dịch bằng một lệnh như sau:

gcc -Wall -Wextra -Werror -std=gnu99 -o main.exe main.c

Đầu ra:

main.c: In function 'main':
main.c:14:5: error: implicit declaration of function 'f1' [-Werror=impl
icit-function-declaration]
     printf( f1() == true ? "true\n" : "false\n");
     ^
cc1.exe: all warnings being treated as errors

Với một thông báo như vậy, bạn nên biết phải làm gì để sửa nó.

Chỉnh sửa: Sau khi đọc nhận xét (hiện đã bị xóa), tôi đã cố gắng biên dịch mã của bạn mà không có cờ. Chà, Điều này đã dẫn tôi đến lỗi trình liên kết không có cảnh báo trình biên dịch thay vì lỗi trình biên dịch. Và những lỗi liên kết khó hiểu hơn, vì vậy ngay cả khi -std-gnu99không cần thiết, hãy cố gắng bằng mọi cách sử dụng ít nhất -Wall -Werrornó sẽ giúp bạn đỡ đau đớn rất nhiều.

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

BoJack Horseman vẫn gan ruột và gan dạ hơn bao giờ hết trong phần 3

BoJack Horseman vẫn gan ruột và gan dạ hơn bao giờ hết trong phần 3

BoJack Horseman (Ảnh: Netflix) BoJack Horseman bắt đầu mùa thứ ba với sự xuất hiện của nhân vật tiêu biểu, người vẫn đang theo đuổi những lời khen ngợi từ giới phê bình trong một nỗ lực tuyệt vọng để tìm ra ý nghĩa trong cuộc sống của mình. Mùa thứ hai về số phận bi kịch của Raphael Bob-Waksberg dày đặc những trò đùa cũng như tuyệt vọng.

Xem cách tính năng lái tự động cập nhật của Tesla bùng phát như thế nào khi bạn bỏ qua các cảnh báo của nó

Xem cách tính năng lái tự động cập nhật của Tesla bùng phát như thế nào khi bạn bỏ qua các cảnh báo của nó

Bản nâng cấp Phiên bản 8.0 gần đây của Tesla đối với hệ thống Lái xe tự động được thiết kế để cải thiện hệ thống và mối quan hệ của nó với người lái.

UnREAL, Chương trình Truyền hình Về Truyền hình Thực tế Chúng tôi Không biết Chúng tôi Cần

UnREAL, Chương trình Truyền hình Về Truyền hình Thực tế Chúng tôi Không biết Chúng tôi Cần

Chắc chắn khi con cái chúng ta xem lại các chương trình chúng ta đã xem vào khoảng năm 2015, chúng sẽ thấy UnREAL, một chương trình truyền hình mới chiếu vào tối Thứ Hai của Lifetime — bạn sẽ thấy đúng lúc, sẽ phát sóng ngay sau khi The Bachelor kết thúc — như một chương trình không thể được thực hiện vào bất kỳ thời điểm nào khác trong lịch sử truyền hình, nhưng một chương trình cảm thấy cần thiết để xem bây giờ. UnREAL có sự tham gia của Shiri Appleby trong vai Rachel, một nhà sản xuất truyền hình cho một chương trình về cơ bản là The Bachelor, mặc dù trong thế giới này, nó được gọi là Vĩnh viễn.

Sau tất cả, Josh Trank sẽ không đạo diễn bộ phim tuyển tập Star Wars thứ hai

Sau tất cả, Josh Trank sẽ không đạo diễn bộ phim tuyển tập Star Wars thứ hai

Thông báo chính thức đến trực tiếp từ trang web của Star Wars: Fantastic Four, đạo diễn Josh Trank, một người bí ẩn không xuất hiện tại đại hội Star Wars Celebration gần đây, sẽ không chỉ đạo phần hai Star Wars Anthology. Đây là tuyên bố được đăng ngày hôm qua: Tất cả thực sự là ngôn ngữ rất lịch sự, nhưng một bài báo của Hollywood Reporter cho thấy tất cả đều không tốt ở hậu trường, gọi sự ra đi là "một vụ sa thải" một phần dựa trên "hành vi bất thường" của Trank trong khi quay Fantastic Four: The Bài báo trích dẫn các nguồn gọi là Trank “đôi khi thiếu quyết đoán và thiếu sáng tạo,” và lưu ý rằng Fantastic Four, khởi chiếu vào ngày 7 tháng 8, đã được khởi động lại vào cuối tháng 4.

Edwin McCain ra mắt Grand Ole Opry: Quay cảnh hậu trường với nhạc sĩ 'I'll Be'

Edwin McCain ra mắt Grand Ole Opry: Quay cảnh hậu trường với nhạc sĩ 'I'll Be'

McCain, người đang làm việc cho một album mới, lần đầu tiên bước vào vòng kết nối vào tối thứ Sáu ở Nashville

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Tôi viết như thế nào

Tôi viết như thế nào

Đối với tôi, mọi thứ là về dòng đầu tiên đó và nó sẽ đưa bạn đến đâu. Một số nhà văn bị điều khiển bởi cốt truyện, sự sắp xếp tinh tế của các quân cờ, trong khi những người khác bị lôi cuốn bởi một nhân vật và khả năng thực hiện một cuộc hành trình với một người bạn hư cấu mới.

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Language