Bạn đã sẵn sàng tham gia vào thế giới hùng vĩ của lập trình C và C++ chưa? Bạn có muốn đặt câu hỏi về sự tồn tại của mình sau vài dòng lệnh C++ đơn giản không? Nếu câu trả lời của bạn là "Yeh!", "Yep" hoặc "Why not?" - chào mừng bạn đến để kiểm tra kiến thức của bạn. Bạn sẽ được đưa ra nhiều câu hỏi liên quan đến C hoặc C++. Hãy tìm câu trả lời và lời giải thích đúng ở cuối câu chuyện. Chúc may mắn! 1. Chương trình nhỏ nhất main; Điều gì sẽ xảy ra nếu bạn thử biên dịch chương trình này bằng trình biên dịch C? sẽ không biên dịch sẽ biên dịch, sẽ không liên kết sẽ biên dịch và sẽ liên kết 2. Cái nĩa #include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); } Chương trình này có thể in ra bao nhiêu dòng? 1000 ít hơn 1000 hơn 1000 3. Tất cả những gì bạn cần là chỉ số #include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; } Chương trình này sẽ in ra những gì? 1 2 3 4 sẽ không biên dịch không xác định 4. Biểu thức chính quy #include <regex> #include <iostream> int main() { std::regex re("(.*|.*)*O"); std::string str("0123456789"); std::cout << std::regex_match(str, re); return 0; } Phải mất bao lâu để biểu thức chính quy này khớp với chuỗi đầu vào này? 1 giây 1 giây 1 phút 1 giờ 1 năm mãi mãi 5. Di chuyển và lambda #include <iostream> struct Foo { Foo() { std::cout << "Foo()\n"; } Foo(Foo&&) { std::cout << "Foo(Foo&&)\n"; } Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; } }; int main() { Foo f; auto a = [f = std::move(f)]() { return std::move(f); }; Foo f2(a()); return 0; } Dòng cuối cùng được chương trình này in ra là… Foo() Foo(Foo&&) Foo(const Foo&) 6. X và thanh #include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; } Chương trình này sẽ in ra những gì? 0 1 0x0 sẽ không biên dịch sẽ không liên kết 7. Các nhà xây dựng #include <iostream> struct Foo { Foo() { std::cout << "Foo()\n"; } Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; } Foo(int) { std::cout << "Foo(int)\n"; } Foo(int, int) { std::cout << "Foo(int, int)\n"; } Foo(const Foo&, int) { std::cout << "Foo(const Foo&, int)\n"; } Foo(int, const Foo&) { std::cout << "Foo(int, const Foo&)\n"; } }; void f(Foo) {} struct Bar { int i, j; Bar() { f(Foo(i, j)); f(Foo(i)); Foo(i, j); Foo(i); Foo(i, j); } }; int main() { Bar(); } Dòng cuối cùng được chương trình này in ra là… Foo(int, int) Foo(const Foo&, int) Foo(int, const Foo&) Foo(int) Thay vì kết luận Tôi hy vọng bạn sẽ không bao giờ tìm thấy một trong những mẩu thông tin kỳ lạ này ngoài tự nhiên. Câu trả lời Chương trình nhỏ nhất Đây là mã C hợp lệ. Nó sẽ bị sập nếu bạn cố chạy nó. - là biến toàn cục. Nó sẽ biên dịch và liên kết thành công. main; Trong mã C, bạn có thể bỏ qua rất nhiều thứ. Ví dụ, bạn có thể bỏ qua kiểu của một biến toàn cục. Theo mặc định, trình biên dịch sẽ cho rằng kiểu này là . Ngoài ra, không có trong C (không giống như trong C++), vì vậy khi liên kết, không có cách nào để phân biệt biến với hàm . int sự thay đổi tên main main Do đó, trình biên dịch sẽ biên dịch mã hợp lệ và trình liên kết sẽ tìm mục có tên là trong tệp đối tượng để liên kết chương trình. main Cái nĩa Đây là tính năng POSIX hơn là tính năng C hoặc C++. Việc triển khai các hoạt động IO sử dụng bộ đệm để tối ưu hóa hiệu suất. Khi bạn gọi , hệ điều hành sẽ tạo bản sao chép khi ghi của bộ nhớ quy trình, bộ đệm IO có khả năng cũng sẽ sao chép và các chuỗi được đệm . fork có khả năng sẽ được in hơn 1000 lần Tất cả những gì bạn cần là chỉ số Câu trả lời là 3 Để hiểu đoạn mã này, chúng ta hãy xem xét kỹ hơn cách các chỉ mục trong C và C++ hoạt động: giống như , giống như và giống như . array[index] *(array + index) (index + array) index[array Manh mối thứ hai là toán tử . Toán tử nhị phân của nó là loại bỏ đối số bên trái và trả về đối số bên phải. , Biểu thức chính quy điều gì sẽ xảy ra! Hành vi phụ thuộc vào việc thực hiện. Không thể dự đoán được Trong môi trường của tôi, chương trình này đưa ra ngoại lệ The complexity of an attempted match against a regular expression exceeded a pre-set level. Các lựa chọn khả thi khác có thời gian thực hiện lâu đáng ngạc nhiên hoặc hoạt động như mong đợi. Đó là vì có hai cách tiếp cận khả thi để triển khai biểu thức chính quy. Đầu tiên - chuyển đổi biểu thức chính quy thành automata hữu hạn (n - độ dài của mẫu), khớp chuỗi (m - độ dài của chuỗi). Phương pháp này không hỗ trợ O(n**2) O(m) quay lui. Thứ hai - phương pháp tham lam + DFS, hỗ trợ quay lui nhưng dễ bị phức tạp theo cấp số nhân đối với một số mẫu nhất định. Di chuyển và lambda Câu trả lời là . Lambdas mặc định là bất biến, tất cả các giá trị được ghi vào lambda với đều ngầm định là . Điều này mở khóa hành vi cho lambdas. Foo(const Foo&) [] const idempotent Khi bạn di chuyển bạn tạo . là một kiểu lạ, do đó trình biên dịch chỉ sao chép f const Foo&& const Foo&& Foo Có hai cách để khắc phục điều này: Tạo lambda có thể thay đổi auto a = [f = std::move(f)]() mutable { return std::move(f); }; Khai báo hàm tạo Foo(const Foo&&) X và thanh . Chương trình sẽ in ra 1 — là cách kỳ lạ để khai báo hàm, nó bằng với . int bar(int(x)); int bar(int x); Nếu bạn nhầm lẫn với ép kiểu, - đây là ép kiểu. int bar((int(x))); Sau đó chúng ta thử chuyển đổi ngầm địa chỉ hàm thành , kết quả của việc chuyển đổi như vậy luôn luôn là . bool true Hàm chưa bao giờ được sử dụng, nó cho phép chúng ta tránh lỗi ký hiệu không được tham chiếu khi liên kết. bar() Các nhà xây dựng Dòng cuối cùng là . Foo(const Foo&, int) là khai báo biến, giống như . Do đó, thành viên lớp dưới tên được ẩn trong phạm vi này. Foo(i) Foo i i