Coderbyte sitesinde 'gets (stdin)' ile neler oluyor?


144

Coderbyte bir online kodlama meydan sitesidir (Ben sadece 2 dakika önce buldum).

Karşılaştığınız ilk C ++ meydan okumasında değiştirmeniz gereken bir C ++ iskeleti vardır:

#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 ++ 'a biraz aşina iseniz, gözlerinizde ortaya çıkan ilk şey * :

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

Tamam, kod çağırıyor gets C ++ 11'den beri kullanımdan kaldırıldı ve kendi içinde kötü olan C ++ 14'ten beri kaldırıldı.

Ama sonra fark ettim: getstür char*(char*). Bu nedenle, bir FILE*parametreyi kabul etmemeli ve sonuç, bir intparametre yerine kullanılabilir olmamalıdır , ancak ... yalnızca herhangi bir uyarı veya hata olmadan derlenmez, aynı zamanda çalışır ve gerçekte doğru giriş değerini iletir FirstFactorial.

Bu sitenin dışında, kod derlenmiyor (beklendiği gibi), bu yüzden burada neler oluyor?


* Aslında ilki using namespace stdburadaki sorunumla alakasız.


Not bu stdinstandart kitaplığı bir olduğunu FILE*ve herhangi bir tip dönüştürür bir işaretçi char*argümanı türüdür, gets(). Ancak, bu tür bir kodu asla gizli bir C yarışmasının dışında yazmamalısınız. Derleyiciniz bile kabul ederse, daha fazla uyarı bayrağı ekleyin ve içinde bu yapıya sahip bir kod tabanını düzeltmeye çalışıyorsanız, uyarıları hatalara dönüştürün.
Davislor

1
@Davislor hayır "aday işlevi geçerli değil: 'struct _IO_FILE *' dan '1. argüman için' char * 'ya bilinen bir dönüşüm yok"
bolov

3
@Davislor ha, bu eski C için doğru olabilir, ama kesinlikle C ++ için geçerli değildir.
Quentin

@Quentin Evet. Bu derlenmemeli. Amaçlanan zorluk, “Bu bozuk kodu alın, ne yapması gerektiği hakkındaki fikrimi okuyun ve düzeltin” olabilir, ancak bu durumda gerçek bir şartname olmalıdır. Test durumları ile.
Davislor

6
Kimse bunu denedi şaşırdım, ama gets(stdin )(ekstra bir boşluk ile) beklenen C ++ hatası üretir.
Roman Odaisky

Yanıtlar:


174

Ben Coderbyte'nin kurucusuyum ve bunu yaratan adamım gets(stdin) .

Bu yazıdaki yorumlar, bunun bir bul ve değiştir biçimi olduğu doğrudur, bu yüzden bunu neden gerçekten hızlı bir şekilde yaptığımı açıklayayım.

Siteyi ilk oluşturduğum gün (2012 civarında) yalnızca JavaScript'i destekledi. Tarayıcıda çalışan JavaScript'te "girişi okumak" için bir yol yoktu ve bu nedenle bir işlev olacaktı foo(input)ve readline()Node.js'den işlevi şöyle çağırmak için kullandım.foo(readline()) . Dışında bir çocuktum ve daha iyi bilmiyordum, bu yüzden tam anlamıyla readline()çalışma zamanında girdi ile değiştirdim . Yani foo(readline())oldu foo(2)yafoo("hello") JavaScript cezası çalıştığı.

2013/2014 civarında daha fazla dil ekledim ve çevrimiçi kodu değerlendirmek için üçüncü taraf hizmet kullandım, ancak kullandığım hizmetlerle stdin / stdout yapmak çok zordu, bu yüzden diller için aynı aptalca bul ve değiştir ile takıldım Python, Ruby ve sonunda C ++, C # vb.

Bugüne kadar, kodu kendi kaplarımda çalıştırıyorum, ancak stdin / stdout'un çalışma şeklini hiç güncellemedim, çünkü insanlar garip hack'e alıştılar (bazı insanlar nasıl dolaşacağını açıklayan forumlarda bile yayınladılar).

En iyi uygulama olmadığını biliyorum ve yeni bir dil öğrenen birinin bu tür saldırıları görmesi yararlı değildir, ancak fikir yeni programcıların giriş okuma konusunda endişelenmemesi ve sadece çözmek için algoritmayı yazmaya odaklanmasıydı. sorun. Kodlama alanlarının yıllar önce kodlanmasıyla ilgili yaygın bir şikayet, yeni programcıların sadece nasıl okuyacaklarını bulmak için çok zaman harcayacaklarıydı.stdin bir dosyadan satırları veya satırdan , bu yüzden yeni kodlayıcıların Coderbyte'deki bu sorunu önlemek için istedim.

Editör sayfasını yakında varsayılan kodla ve stdindiller için okuma ile birlikte güncelleyeceğim . Umarım C ++ programcıları daha fazla Coderbyte kullanmaktan zevk alacaktır :)


20
"[B] fikir yeni programcılar için giriş okuma hiç endişe ve sadece sorunu çözmek için algoritma yazmaya odaklanmak oldu" - ve benzer bir şey yazmak yerine, size gerçekleşmedi "gerçek "kod, sadece bu noktada bir tamamlanmış işlev adı veya bariz bir yer tutucu koymak? Gerçekten merak ediyorum.
Ruther Rendommeleigh

25
Bunu gönderdiğimde kendimden başka bir cevap seçeceğimi gerçekten beklemiyordum. Bu kadar güzel bir şekilde beni yanlış kanıtladığınız için teşekkürler. Cevabınızı görmek gerçekten bir zevk.
bolov

4
Çok ilginç! Bu hack'i tutmak istiyorsanız, işlev çağrısını benzer bir şeyle değiştirmenizi TAKE_INPUT, ardından #define TAKE_INPUT whatever_hereüste yerleştirmek için find-replace'i kullanmanızı öneririm .
Draconis

18
"Ben x'in kurucusuyum ve bunu yaratan adamım" ile başlayan daha fazla cevaba ihtiyacımız var .
boru

2
@iheanyi Kimse mükemmel olmasını istemedi. Aslında, neredeyse her yer tutucunun herhangi bir yeni başlayan için geçerli bir kod gibi görünen ancak aslında derlenmeyen bir şeyden daha iyi olacağına inanıyorum .
Ruther Rendommeleigh

112

İlgilendim. Bu yüzden, araştırma gözlüklerini takma zamanı ve derleyici veya derleme bayraklarına erişimim olmadığından yaratıcı olmalıyım. Ayrıca, bu kodla ilgili hiçbir şey mantıklı olmadığından, her varsayım kötü bir fikir sorusu değildir.

Önce gerçek türünü kontrol edelim gets. Bunun için küçük bir numara var:

template <class> struct Name;

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

Ve bu görünüyor ... normal:

/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.

getsonaylanmadı olarak işaretlendi ve imzası var char *(char *). Ama sonra nasıl FirstFactorial(gets(stdin));derleniyoruz?

Başka bir şey deneyelim:

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

Hangi bize verir:

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

Nihayet biz bir şey alıyorsanız: decltype(8). Böylece tüm gets(stdin)metinsel olarak input ( 8) ile değiştirildi .

Ve işler tuhaflaşıyor. Derleyici hatası devam ediyor:

/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__;

Şimdi beklenen hatayı alıyoruz cout << FirstFactorial(gets(stdin));

Bir makroyu kontrol ettim ve #undef getshiçbir şey yapmıyor gibi göründüğü için makro değil.

Fakat

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

Derler.

Fakat

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

n2Satırda beklenen hata ile yok .

Ve yine, mainhattın yapılması için hemen hemen tüm değişiklikler cout << FirstFactorial(gets(stdin));beklenen hatayı tükürür.

Üstelik stdinaslında boş görünüyor.

Bu yüzden sadece kaynağı ayrıştıran ve değiştirmeye çalışan (zayıf) küçük bir programları olduğu sonucuna varabilirim. gets(stdin) derleyen ve gerçekten derleyiciye beslemeden önce test durumu giriş değeri ile . Eğer herhangi biri daha iyi bir teoriye sahipse ya da ne yaptığını gerçekten biliyorsa lütfen paylaşın!

Açıkçası bu çok kötü bir uygulamadır. Bunu araştırırken burada en azından bir soru olduğunu buldum ( örnek ) olduğunu ve insanların bunu yapan bir site olduğunu bilmedikleri için, cevapları "kullanımı getskullanma ... yerine" kullanmayın. iyi bir tavsiye ancak OP'yi daha fazla karıştırır, çünkü stdin'den geçerli bir okuma girişimi bu sitede başarısız olacaktır.


TLDR

gets(stdin)geçersiz C ++. Bu sitenin kullandığı bir hile (hangi nedenlerle anlayamıyorum). Sitede göndermeye devam etmek istiyorsanız (ne onaylamıyorum ne de onaylamıyorum) aksi takdirde mantıklı olmayan, ancak kırılgan olduğunun farkında olan bu yapıyı kullanmanız gerekir. Hemen hemen tüm değişiklikler mainbir hata verecektir. Bu sitenin dışında normal giriş okuma yöntemleri kullanın.


27
Gerçekten şaşırdım. Belki bu soru-cevap kodlama meydan okuma sitelerinden neden öğrenilmeyeceğine dair kanonik bir yazı olabilir.
alter igel

28
Gerçekten kötü bir şey oluyor ve bence bu derleyicinin dışındaki kaynak kodundaki metin değiştirme seviyesinde. Bunu deneyin: std::cout << "gets(stdin)";ve çıkışı 8(ya da her ne 'giriş' alanına yazdığınız Bu dilin bir yüz kızartıcı kötüye kullanmaktır..
alter Igel

14
@Stobor etrafında tırnak not "gets(stdin)". Bu, önişlemcinin bile dokunmayacağı bir dize değişmezidir
algel igel

2
James Kirk'e alıntı yapmak için: "Bu çok tuhaf."
ApproachingDarknessFish

2
@ alterigel yüksek atından in. Kodlama sitelerinden öğrenmenin faydalı olup olmadığı konusunda bir ifade yoktur. İnsanların bir şeyler nasıl uyguladığına kim karar verirsiniz?
Matsemann

66

mainCoderbyte editöründe şu eklentiyi denedim :

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

Gizemli ve esrarengiz snippet'in gets(stdin)bir dizgi değişmezinin içinde göründüğü yer. Bu muhtemelen hiçbir şey tarafından dönüştürülmemelidir, önişlemci bile değil ve herhangi bir C ++ programcısı bu kodun tam gets(stdin)çıktıyı standart çıktıya yazdırmasını beklemelidir . Yine de, derlenmiş ve kodlayıcı üzerinde çalıştırıldığında aşağıdaki çıktıyı görüyoruz:

8

Değerin 8doğrudan düzenleyicinin altındaki uygun 'giriş' alanından alınması.

Sihirli kod

Bundan, bu çevrimiçi editörün, kaynak kod üzerinde kör bul ve değiştir işlemleri gerçekleştirdiği, bunun yerine gets(stdin)kullanıcının 'girişi' ile yer değiştirdiği açıktır . Ben şahsen buna dikkatsiz önişlemci makrolarından daha kötü olan dilin kötüye kullanılması diyebilirim.

Çevrimiçi bir kodlama meydan okuma web sitesi bağlamında, bunun için endişeleniyorum çünkü geleneksel olmayan, standart dışı, anlamsız ve en azından güvenli olmayan uygulamaları öğretiyorgets(stdin) ve diğer platformlarda tekrarlanamayacak bir şekilde .

Bunun olamaz eminim bu sadece kullanmak zor std::cinbir programa ve sadece dere girdi.


ve kör bir “bul ve değiştir” bile değildir, çünkü bazen onun yerini alır, bazen değiştirmez.
bolov

4
@bolov, sadece ilk gets(stdin)değiştirildiği yer olabilir mi? Dilin sözdiziminden veya dilbilgisinden habersiz görünmesi anlamında 'kör' demek istedim.
alter igel

Evet haklısın. İlk oluşumun yerini alır. Birini ana önüne koymayı denedim ve gerçekten de aldım.
bolov

1
Daha fazla araştırma, bu sitenin sadece C ++ için değil, python / ruby ​​için değil, genellikle stdin'den bir dize döndürecek ancak " bunun yerine bu dizenin dize ile değiştirilmesi. Ben getline işlevi için bir regex maç bulmak çok zordu, bu yüzden C / C ++ için gets (stdin) ile gitti.
19'da Stobor

4
@Stobor dang, haklısın. Bunun Java için de olduğunu doğrulayabilirim , tanımsız olsa bile çizgi System.out.print(FirstFactorial(s.nextLine()9));yazdırılır . 89s
alter igel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.