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!
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?
#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?
#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ì?
#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?
#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&)
#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
#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)
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.
Chương trình nhỏ nhất
Đây là mã C hợp lệ. Nó sẽ biên dịch và liên kết thành công. Nó sẽ bị sập nếu bạn cố chạy nó. main;
- là biến toàn cục.
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à int
. Ngoài ra, không có sự thay đổi tên 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 main
với hàm 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à main
trong tệp đối tượng để liên kết chương trình.
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 fork
, 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 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: array[index]
giống như *(array + index)
, giống như (index + array)
và giống như 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
Không thể dự đoán được điều gì sẽ xảy ra! Hành vi phụ thuộc vào việc thực hiện.
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 O(n**2)
(n - độ dài của mẫu), khớp chuỗi O(m)
(m - độ dài của chuỗi). Phương pháp này không hỗ trợ 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à Foo(const Foo&)
. 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à const
. Điều này mở khóa hành vi idempotent cho lambdas.
Khi bạn di chuyển f
bạn tạo const Foo&&
. const Foo&&
là một kiểu lạ, do đó trình biên dịch chỉ sao chép 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
.
int bar(int(x));
— là cách kỳ lạ để khai báo hàm, nó bằng với int bar(int x);
.
Nếu bạn nhầm lẫn với ép kiểu, int bar((int(x)));
- đây là ép kiểu.
Sau đó chúng ta thử chuyển đổi ngầm địa chỉ hàm thành bool
, kết quả của việc chuyển đổi như vậy luôn luôn là true
.
Hàm bar()
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.
Các nhà xây dựng
Dòng cuối cùng là Foo(const Foo&, int)
.
Foo(i)
là khai báo biến, giống như Foo i
. Do đó, thành viên lớp dưới tên i
được ẩn trong phạm vi này.