사이트 코더 바이트에서 'gets (stdin)'은 어떻게됩니까?


144

Coderbyte는 온라인 코딩 챌린지 사이트입니다 (2 분 전에 발견했습니다).

당신이 맞이할 첫 번째 C ++ 챌린지 에는 수정해야 할 C ++ 스켈레톤이 있습니다.

#include <iostream>
#include <string>
using namespace std;

int FirstFactorial(int num) {

  // Code goes here
  return num;

}

int main() {

  // Keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;

}

당신이 먼저 C ++과 거의 잘 알고있는 경우 * 당신의 눈에 아빠는이다 :

int FirstFactorial(int num);
cout << FirstFactorial(gets(stdin));

자, 코드 호출 gets은 C ++ 11부터 더 이상 사용되지 않으며 C ++ 14 이후로는 제거되어 자체적으로 나쁩니다.

그러나 나는 깨달았습니다 : gets유형 char*(char*)입니다. 따라서 FILE*매개 변수를 수락해서는 안되며 매개 변수 대신 결과를 사용할 수 없어야 int하지만 ... 경고 또는 오류없이 컴파일 될뿐만 아니라 실행되고 실제로 올바른 입력 값을에 전달합니다 FirstFactorial.

이 특정 사이트 이외의 코드는 예상대로 컴파일되지 않으므로 여기에서 무슨 일이 있습니까?


* 실제로는 첫 번째 using namespace std이지만 내 문제와 관련이 없습니다.


참고하는 stdin표준 라이브러리에있다 FILE*, 그리고 어떤 종류의 변환에 대한 포인터 char*의 인수의 유형입니다 gets(). 그러나 난독 화 된 C 경연 대회 밖에서 그런 종류의 코드를 작성해서는 안됩니다. 컴파일러에서 허용하는 경우 경고 플래그를 더 추가하고 해당 구문이있는 코드베이스를 수정하려는 경우 경고를 오류로 설정하십시오.
Davislor

1
@Davislor 아니오 그것은 "후보 기능을 실행할 수 없습니다 : 첫 번째 인수에 대해 'struct _IO_FILE *'에서 'char *'로 알려진 변환이 없습니다"
bolov

3
@Davislor huh, 그것은 고대 C에는 해당되지만 C ++에는 해당되지 않습니다.
Quentin

@Quentin 네. 컴파일해서는 안됩니다. 의도 된 문제는 "이 깨진 코드를 가져 와서해야 할 일에 대해 내 마음을 읽고 고치십시오"라는 것이었을 것입니다. 그러나이 경우에는 실제 사양이 있어야합니다. 테스트 사례
Davislor

6
아무도 이것을 시도하지 않은 것에 놀랐지 만 gets(stdin )(추가 공간이 있으면) 예상되는 C ++ 오류가 발생합니다.
Roman Odaisky

답변:


174

저는 Coderbyte의 창립자이며이 gets(stdin)핵 을 만든 사람입니다 .

이 게시물에 대한 의견은 그것이 찾기 및 바꾸기의 형태라는 것이 정확하므로 내가 왜 그렇게 빨리했는지 설명하겠습니다.

2012 년 경에 처음 사이트를 만든 당시에는 JavaScript 만 지원했습니다. 브라우저에서 실행되는 JavaScript에서 "입력을 읽는"방법이 없었으므로 함수가 있고 Node.js foo(input)readline()함수를 사용하여 처럼 호출했습니다 foo(readline()). 내가 어 렸고 더 잘 몰랐기 때문에 문자 그대로 readline()런타임에 입력으로 바꿨습니다. 그래서이 foo(readline())되었다 foo(2)또는 foo("hello")자바 스크립트 벌금을 근무한다.

2013/2014 년경에 더 많은 언어를 추가하고 타사 서비스를 사용하여 코드를 온라인으로 평가했지만 사용중인 서비스로 stdin / stdout을 수행하는 것은 매우 어려웠으므로 언어에 대해 동일한 어리석은 찾기 및 바꾸기를 고수했습니다. Python, Ruby 및 C ++, C # 등

오늘 빨리, 나는 내 컨테이너에서 코드를 실행하지만 사람들이 이상한 해킹에 익숙해 져서 stdin / stdout 작동 방식을 업데이트하지 않았습니다 (일부 사람들은 포럼에 게시하는 방법을 설명했습니다).

모범 사례가 아니며 새로운 언어를 배우는 사람이 이와 같은 해킹을 보는 데 도움이되지 않는다는 것을 알고 있지만 새로운 프로그래머는 입력을 전혀 걱정하지 않고 알고리즘을 작성하여 문제를 해결하는 데 집중했습니다. 문제. 몇 년 전 사이트 코딩 문제에 대한 일반적인 불만 사항 중 하나는 새로운 프로그래머가 stdin파일에서 줄 을 읽 거나 읽는 방법을 알아내는 데 많은 시간을 할애한다는 점입니다 . 따라서 새로운 코더가 Coderbyte에서이 문제를 피하기를 원했습니다.

기본 코드와 함께 전체 편집기 페이지를 곧 업데이트하고 stdin언어를 읽을 것입니다. 희망적으로 C ++ 프로그래머는 Coderbyte를 더 많이 사용하게 될 것입니다 :)


20
"[B]하지만 아이디어는 새로운 프로그래머가 전혀 입력을 읽는 것에 대해 걱정하지 않고 문제를 해결하기 위해 알고리즘을 작성하는 데 집중하는 것이 었습니다."- "실제와 비슷한 것을 쓰지 않고 발생하지 않았습니다. "코드, 그 자리에 구성된 함수 이름이나 명백한 자리 표시자를 넣으시겠습니까? 정말 궁금합니다.
Ruther Rendommeleigh 8

25
나는 이것을 게시 할 때 내 자신이 아닌 다른 답변을 선택할 것이라고 기대하지 않았습니다. 그렇게 위대한 방법으로 나를 잘못 증명해 주셔서 감사합니다. 당신의 대답을 보는 것은 정말 기쁩니다.
bolov

4
매우 흥미로운! 이 핵을 유지하려면 함수 호출을와 같은 TAKE_INPUT것으로 바꾸고 찾기 대체를 사용하여 #define TAKE_INPUT whatever_here맨 위에 삽입 하는 것이 좋습니다.
Draconis

18
"나는 x의 창시자이며 이것을 만든 사람"으로 시작하여 더 많은 답변이 필요합니다 .
파이프

2
@iheanyi 아무도 그것을 완벽하게 요구하지 않았습니다. 사실, 나는 거의 모든 자리 표시자가 초보자에게 유효한 코드처럼 보이지만 실제로 컴파일하지 않는 보다 낫다고 확신합니다 .
Ruther Rendommeleigh 8

112

나는 흥미 롭다. 따라서 조 사용 고글을 착용 할 시간이며 컴파일러 또는 컴파일 플래그에 액세스 할 수 없으므로 독창적이어야합니다. 또한이 코드에 대한 의미가 없기 때문에 모든 가정에 대해 나쁜 생각은 아닙니다.

먼저 실제 유형을 확인합시다 gets. 나는 약간의 트릭을 가지고있다 :

template <class> struct Name;

int main() { 
    
    Name<decltype(gets)> n;
  
  // keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;
    
}

그리고 그것은 ... 보통 보입니다.

/tmp/613814454/Main.cpp:16:19: warning: 'gets' is deprecated [-Wdeprecated-declarations]
    Name<decltype(gets)> n;
                  ^
/usr/include/stdio.h:638:37: note: 'gets' has been explicitly marked deprecated here
extern char *gets (char *__s) __wur __attribute_deprecated__;
                                    ^
/usr/include/x86_64-linux-gnu/sys/cdefs.h:254:51: note: expanded from macro '__attribute_deprecated__'
# define __attribute_deprecated__ __attribute__ ((__deprecated__))
                                                  ^
/tmp/613814454/Main.cpp:16:26: error: implicit instantiation of undefined template 'Name<char *(char *)>'
    Name<decltype(gets)> n;
                         ^
/tmp/613814454/Main.cpp:12:25: note: template is declared here
template <class> struct Name;
                        ^
1 warning and 1 error generated.

gets더 이상 사용되지 않는 것으로 표시되고 서명이 char *(char *)있습니다. 그러나 어떻게 FirstFactorial(gets(stdin));컴파일되고 있습니까?

다른 것을 시도해 보자.

int main() { 
  Name<decltype(gets(stdin))> n;
  
  // keep this function call here
  cout << FirstFactorial(gets(stdin));
  return 0;
    
} 

우리에게주는 것 :

/tmp/286775780/Main.cpp:15:21: error: implicit instantiation of undefined template 'Name<int>'
  Name<decltype(8)> n;
                    ^

마침내 우리는 무언가를 얻고 있습니다 : decltype(8). 따라서 전체 gets(stdin)가 텍스트로 입력 ( 8)으로 대체되었습니다 .

그리고 상황이 더 이상해집니다. 컴파일러 오류는 계속됩니다.

/tmp/596773533/Main.cpp:18:26: error: no matching function for call to 'gets'
  cout << FirstFactorial(gets(stdin));
                         ^~~~
/usr/include/stdio.h:638:14: note: candidate function not viable: no known conversion from 'struct _IO_FILE *' to 'char *' for 1st argument
extern char *gets (char *__s) __wur __attribute_deprecated__;

이제 우리는 cout << FirstFactorial(gets(stdin));

매크로를 확인했는데 매크로 #undef gets가 아닌 것처럼 보이지 않습니다.

그러나

std::integral_constant<int, gets(stdin)> n;

컴파일합니다.

그러나

std::integral_constant<int, gets(stdin)> n;    // OK
std::integral_constant<int, gets(stdin)> n2;   // ERROR                                          wtf??

n2줄 에서 예상되는 오류가 없습니다 .

그리고 다시, 거의 모든 수정으로 main라인 cout << FirstFactorial(gets(stdin));이 예상되는 오류를 내뿜습니다.

또한 stdin실제로 비어있는 것 같습니다.

그래서 소스를 구문 분석하고 gets(stdin)실제로 테스트 케이스 입력 값을 컴파일러에 공급하기 전에 테스트 케이스 입력 값 으로 대체하려고하는 작은 프로그램이 있다고 결론을 내릴 수 있습니다 . 더 나은 이론을 가지고 있거나 실제로 무엇을하고 있는지 알고 있다면 공유하십시오!

이것은 분명히 매우 나쁜 습관입니다. 이것을 조사하는 동안 나는 이것에 대해 적어도 여기 ( )에 대한 질문 이 있으며 사람들은 이것을 사용하는 사이트가 있다는 것을 알지 못하기 gets때문에 실제로는 "사용 하지 마십시오 ... 대신"입니다. stdin의 유효한 읽기 시도가이 사이트에서 실패하기 때문에 좋은 조언이지만 OP를 더 혼란스럽게합니다.


TLDR

gets(stdin)유효하지 않은 C ++입니다. 이 특정 사이트가 사용하는 특수 효과입니다 (어떤 이유로 알아낼 수 없는지). 사이트에 계속 제출하려면 (나는 그것을지지하지도 않고 그것을지지하지도 않습니다) 그렇지 않으면 이해할 수없는이 구조를 사용해야하지만 부서지기 쉽습니다. 거의 모든 수정 main은 오류를 뱉어냅니다. 이 사이트 외부에서는 일반적인 입력 판독 방법을 사용하십시오.


27
나는 정말 놀랐습니다. 어쩌면이 Q / A는 코딩 도전 사이트에서 배우지 않는 이유에 대한 표준 게시물 일 수 있습니다.
igel

28
정말 악한 일이 일어나고 있으며 컴파일러 외부의 소스 코드에서 텍스트를 대체하는 수준에 있다고 생각합니다. 이것을 시도하십시오 : std::cout << "gets(stdin)";그리고 출력은 8(또는 당신이 '입력'필드에 입력하는 것입니다. 이것은 불명예스러운 언어 남용입니다.
alter igel

14
@Stobor는 따옴표를 참고하십시오 "gets(stdin)". 이것은 전 처리기조차도 손대지 않을 문자열 리터럴입니다.
igel

2
제임스 커크 (James Kirk)의 말 : "이건 독특하다."
ApproachingDarknessFish

2
@alterigel 당신의 높은 말을 내려. 이것은 코딩 사이트로부터 학습하는 것이 유용한 지에 대한 언급이 아닙니다. 사람들이 물건을 연습하는 방법을 결정하려면 누구입니까?
Matsemann

66

mainCoderbyte 편집기에서 다음을 추가했습니다 .

std::cout << "gets(stdin)";

신비스럽고 수수께끼 같은 스 니펫 gets(stdin)이 문자열 리터럴 안에 나타납니다. 이것은 아마도 아무것도에 의해조차 처리기를 변형하고,해서는 안 모든 C ++ 프로그래머가이 코드는 정확한 문자열을 인쇄하는 기대한다 gets(stdin)표준 출력으로. 그럼에도 불구하고 우리는 코더 바이트에서 컴파일되고 실행될 때 다음과 같은 결과를 볼 수 있습니다 :

8

8편집기 아래의 편리한 '입력'필드에서 바로 값 을 가져옵니다.

매직 코드

이것으로부터,이 온라인 편집기는 소스 코드에 대한 맹검 찾기 및 바꾸기 작업을 수행 gets(stdin)하고 사용자의 '입력'으로 대체 된 것으로 보입니다 . 나는 개인적으로 이것을 부주의 한 전 처리기 매크로보다 나쁜 언어의 오용이라고 부릅니다.

온라인 코딩 챌린지 웹 사이트의 맥락에서 나는 이것이 기존의 비표준적이고 의미가 없으며 적어도 안전하지 않은 관행 gets(stdin)을 다른 플랫폼에서 반복 할 수없는 방식으로 가르치기 때문에 걱정됩니다 .

사용 하기가 어렵고 std::cin입력을 프로그램으로 스트리밍 하는 것이 쉽지 않을 것이라고 확신합니다 .


때로는 눈에 띄지 않는 "찾기 및 바꾸기"도되지 않습니다.
bolov

4
@bolov는 그것이 처음 gets(stdin)으로 대체 된 것일 수 있습니까? 나는 언어의 문법이나 문법을 모르는 것처럼 '맹목적'을 의미했다.
igel

네, 맞아요. 첫 번째 발생을 대체합니다. 나는 메인 앞에 하나를 넣어 보았고 그것이 실제로 얻은 것입니다.
bolov

1
추가 연구에 따르면 해당 사이트는 C ++뿐만 아니라 모든 언어에 대해 사이트를 수행한다고 제안합니다. 파이썬 / 루비는 일반적으로 stdin에서 문자열을 반환하지만 함수는 호출합니다 ( "raw_input ()"또는 "STDIN.gets"). 대신 해당 문자열의 문자열 대체. getline 함수에 대한 정규식 일치를 찾는 것이 너무 어려워서 C / C ++에 대해 gets (stdin)을 사용했습니다.
Stobor

4
@ 스토 보 당, 맞아. Java에서도 이러한 일이 발생한다는 것을 확인할 수 있으며, 정의되지 않은 경우에도 줄이 System.out.print(FirstFactorial(s.nextLine()9));인쇄 89됩니다 s.
igel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.