C와 C++ 프로그래밍의 장엄한 세계에 합류할 준비가 되셨나요? 간단한 C++ 몇 줄을 읽고 자신의 존재에 대해 의문을 품고 싶으신가요? 만약 당신의 답이 "Yeh!", "Yep" 또는 "Why not?"라면, 당신의 지식을 테스트해 보세요. C 또는 C++와 관련된 여러 질문이 주어질 것입니다. 정답과 설명은 스토리의 끝에서 찾아보세요. 행운을 빌어요! 1. 가장 작은 프로그램 main; 이 프로그램을 C 컴파일러를 사용하여 컴파일하려고 하면 무슨 일이 일어날까요? 컴파일되지 않습니다 컴파일은 되고 링크는 안됨 컴파일하고 링크할 것입니다 2. 포크 #include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); } 이 프로그램은 몇 줄을 인쇄할까요? 1000 1000 미만 1000 이상 3. 필요한 것은 인덱스뿐입니다 #include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; } 이 프로그램은 무엇을 인쇄할까요? 1 2 3 4 컴파일되지 않습니다 한정되지 않은 4. 정규 표현식 #include <regex> #include <iostream> int main() { std::regex re("(.*|.*)*O"); std::string str("0123456789"); std::cout << std::regex_match(str, re); return 0; } 이 정규 표현식이 입력 문자열과 일치하려면 얼마나 걸릴까요? 1밀리초 1초 1분 1시간 1년 영원히 5. 이동 및 람다 #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; } 이 프로그램이 인쇄하는 마지막 줄은… Foo() Foo(Foo&&) Foo(const Foo&) 6. X와 막대 #include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; } 이 프로그램은 무엇을 인쇄할까요? 0 1 0x0 컴파일되지 않습니다 링크하지 않습니다 7. 건설자 #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(); } 이 프로그램이 인쇄하는 마지막 줄은… Foo(int, int) Foo(const Foo&, int) Foo(int, const Foo&) Foo(int) 결론 대신 이런 특이한 조각들을 야외에서 발견하지 못하기를 바랍니다. 답변 가장 작은 프로그램 이것은 합법적인 C 코드입니다. 실행하려고 하면 충돌합니다. - 은 전역 변수입니다. 성공적으로 컴파일하고 링크할 것입니다. main; C 코드에서는 많은 것을 생략할 수 있습니다. 예를 들어 전역 변수의 유형을 생략할 수 있습니다. 기본적으로 컴파일러는 이 유형을 로 가정합니다. 또한 C에는 없으므로(C++와 달리) 링크할 때 변수 과 함수 을 구별할 방법이 없습니다. int 이름 망글링이 main main 따라서 컴파일러는 유효한 코드를 컴파일하고, 링커는 개체 파일에서 이라는 이름의 파일을 찾아 프로그램을 연결합니다. main 포크 이것은 C 또는 C++ 기능보다는 POSIX 기능에 가깝습니다. IO 작업 구현은 성능 최적화를 위해 버퍼를 사용합니다. 호출하면 OS는 프로세스 메모리의 복사-쓰기 복제본을 생성하고 IO 버퍼도 복제될 가능성이 높으며 버퍼링된 문자열은 높습니다. fork 1000회 이상 인쇄될 가능성이 필요한 것은 인덱스뿐입니다 답은 3입니다 이 코드를 이해하기 위해 C 및 C++에서 인덱스가 작동하는 방식을 자세히 살펴보겠습니다. 는 와 같고, 와 같고, 와 같습니다. array[index] *(array + index) (index + array) index[array 두 번째 단서는 연산자 입니다. 이진 연산자로, 왼쪽 인수를 버리고 오른쪽 인수를 반환합니다. , 정규 표현식 무슨 일이 일어날지 합니다! 행동은 구현에 달려 있습니다. 예측하는 것은 불가능 제 환경에서 이 프로그램은 예외를 발생시킵니다. The complexity of an attempted match against a regular expression exceeded a pre-set level. 다른 가능한 옵션은 놀랍게도 오랜 시간이 걸리거나 예상대로 작동합니다. 정규 표현식을 구현하는 데 두 가지 가능한 접근 방식이 있기 때문입니다. 첫째 - 정규 표현식을 유한 오토마타 (n - 패턴 길이)로 변환하고, 문자열 일치 (m - 문자열 길이). 이 접근 방식은 O(n**2) O(m) 백트래킹을 지원하지 않습니다. 두 번째 - 탐욕적 접근법 + DFS, 백트래킹을 지원하지만 특정 패턴에서는 지수적 시간 복잡도가 발생하기 쉽습니다. 이동 및 람다 답은 입니다. 람다는 기본적으로 불변이며, 로 람다에 캡처된 모든 값은 암묵적으로 입니다. 이를 통해 람다에 대한 동작이 잠금 해제됩니다. Foo(const Foo&) [] const 멱등 이동하면 가 생성됩니다. 는 이상한 유형이므로 컴파일러는 그냥 복사합니다. f const Foo&& const Foo&& Foo 이 문제를 해결하는 방법은 두 가지가 있습니다. 변경 가능한 람다 생성 auto a = [f = std::move(f)]() mutable { return std::move(f); }; 생성자 선언합니다. Foo(const Foo&&) X와 막대 프로그램은 . 1 인쇄합니다 — 함수를 선언하는 이상한 방법으로, 와 같습니다. int bar(int(x)); int bar(int x); 형 변환과 혼동하셨다면, 가 바로 형 변환입니다. int bar((int(x))); 그런 다음 함수 주소를 로 암묵적으로 캐스팅하려고 하면 이러한 캐스팅의 결과는 항상 입니다. bool true 함수는 한 번도 사용되지 않았는데, 이를 통해 링크할 때 참조되지 않은 심볼 오류를 피할 수 있습니다. bar() 생성자 마지막 줄은 입니다. Foo(const Foo&, int) 는 변수 선언이며 와 동일합니다. 따라서 라는 이름의 클래스 멤버는 이 범위에서 숨겨집니다. Foo(i) Foo i i