[C언어 기초 CURSE] Hello, World 출력하기
일반적으로 프로그래밍을 배울 때는 C언어부터 시작하고, (요즘은 python부터라지만) C언어를 제일 처음 배울 때 접하는 코드는 바로 그 유명한 'Hello, world!'를 출력하는 코드다. 오늘은 그 코드를 출력하는 방법들에 대해 알아보자.
1번 : 근본
#include <stdio.h>
main()
{
printf("hello, world\n");
}
해설
K&R의 <The C Programming Language>에서 제일 처음 등장하는 근본 있는 코드라고 할 수 있다.
C언어는 코드가 본격적으로 실행되면 main()
함수 내부에 있는 코드를 우선적으로 실행한다. 내부의 printf
함수는 문자열을 인자로 받아 출력하므로 "hello, world\n"
라는 문자열을 전달해주면 hello, world
가 출력된다.
2번: hello world 없는 hello world 코드(1)
#include <stdio.h>
int main() {
unsigned a[3] = { 560229490, 1867980911, 1819043144 };
int i = 2;
do {
do {
putchar(a[i] & 255);
} while ( a[i] >>= 8 );
} while(i--);
}
해설
해당 코드는 i
가 2부터 1씩 줄어들면서, 배열 a[3]
에 있는 각각의 요소를 255로 나눈 나머지 값(마지막 8비트)에 대해 해당하는 문자를 출력하고, 이후 a[i]
에 대해 비트 시프트 연산을 수행하여 그 다음 8비트에 대해 같은 과정을 수행한다. 560229490
, 1867980911
, 1819043144
는 16진수로 변환하면 각각
21646C72
6F57206F
6C6C6548
가 되고, 마지막 8비트씩 호출되는 순서대로 끊어서 써보면 48 65 6C 6C 6F 20 57 6F 72 6C 64 21
이고, 각각 대응되는 ASCII 문자로 표현하면 Hello World!
가 된다.
3번 : hello world 없는 hello world 코드(2)
이쯤 되면 알겠지만 제목의 curse는 course의 오타가 아니였다. 우린 지금 기초 curse를 살펴보고 있다.
int main() {
// Some floating point numbers for testing
float b[] = {1.1431391224375825e+27, 6.6578486920496456e+28, 7.7392930965627798e-19, 3.2512161851627752e-9};
// Print all numbers in array b[]
puts(b);
return 0;
}
해설
컴파일 타임에 b
를 초기화 하면서 Hello, World!
를 littel endian으로 표현한 값이 들어가도록 숫자들을 적어둔 코드다. IEEE 754에 맞추어 주어진 값들을 HEX로 나타내면 Hello, World!
임을 알 수 있다.
4번 : GREET PLANET
외계인이 지구에 처음 오면 무슨 말을 할까? hello world!
#include <stdio.h>
main() {
long long P = 1,
E = 2,
T = 5,
A = 61,
L = 251,
N = 3659,
R = 271173410,
G = 1479296389,
x[] = { G * R * E * E * T , P * L * A * N * E * T };
puts((char*)x);
}
해설
이건 long long int로 표현했을 뿐, 원리는 앞선 3번 예시와 같다.
5번 : ?
이젠 사람의 언어로 표현할 수 없는 코드다.
#include <stdio.h>
#define o stdout
#define p fputs
int main(_){int*I=&_,_I=2113,l1=3271;_=14557;_I*=503;_<<=3;_*='=';_I<<=0==0;_I=7*'Y'*853<<2;
p(I,o);I=&_I;p(I,o);I=&_;
_+= l1*11*11;
_I += 0xF5<<8;p(I,o);I=&_I;p(I,o);}
해설
복잡해보이게 여러 특수문자들을 섞어놨지만 원리는 앞선 것들과 같다.
각 변수들의 값이 어떻게 변하는지 유의하며 코드를 살펴보자.
6번 : ????
이게)무(슨)코드)인지(해(석())도(못)(하겠)다)
#define u unsigned char
#define v while(*x)
#define z(x) putchar(*x);
#define y(x) ++*x;
#define p(x) ++x;
#define e(x) --*x;
#define d(x) --x;
#define w(x) x x
main(){u *x=calloc(12,1);u *l=x;w(w(w(y(x))))w(y(x))v{p(x)w(w(y(x)))w(y(x))y(x)p
(x)w(w(w(y(x))))w(y(x))p(x)w(y(x))y(x)p(x)w(w(w(y(x))))y(x)w(w(d(x)))e(x)}p(x)w(
y(x))z(x)p(x)y(x)z(x)w(w(y(x)))w(y(x))y(x)w(z(x))w(y(x))y(x)z(x)p(x)w(y(x))z(x)p
(x)w(e(x))e(x)z(x)w(d(x))z(x)w(y(x))y(x)z(x)w(w(e(x)))w(e(x))z(x)w(w(w(e(x))))z(
x)p(x)y(x)z(x)free(l);}
해설
하아… define들을 다 풀어 놓으면 다음 코드와 같다.
main(){unsigned char *x=calloc(12,1);unsigned char *l=x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x;
while(*x){
++x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++x; ++*x; ++*x; ++*x; ++x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; --x; --x; --x; --x; --*x;
}
++x; ++*x; ++*x;
putchar(*x);
++x; ++*x;
putchar(*x); ++*x; ++*x; ++*x; ++*x; ++*x; ++*x; ++*x;
putchar(*x);
putchar(*x); ++*x; ++*x; ++*x;
putchar(*x); ++x; ++*x; ++*x;
putchar(*x); ++x; --*x; --*x; --*x;
putchar(*x); --x; --x;
putchar(*x); ++*x; ++*x; ++*x;
putchar(*x); --*x; --*x; --*x; --*x;--*x; --*x;putchar(*x); --*x; --*x; --*x; --*x; --*x; --*x; --*x; --*x;
putchar(*x); ++x; ++*x;
putchar(*x);
free(l);}
포인트*x
를 커서로 두고 while문으로 할당한 배열의 마지막으로 커서를 옮긴다. 그 후 뒤에서부터 ascii code에 맞는 값을 설정하고, 한 글자를 출력하고, 그 다음칸에서 같은 과정을 반복하면서 Hello World!
를 출력한다.
7번: 저 집에 갈래요
집에 가고 싶어졌다.
#include <stdio.h>
#define $ __func__
int main() {
printf("%c%c%c%c%c%c%c%c%c%c%c%c", ($[0]-$[($[3]-$[0])+($[3]-$[0])])*(($[0]-$[($[3]-$[0])])+($[0]-$[($[3]-$[0])+($[3]-$[0])])+($[3]-$[0])+($[3]-$[0])), ($[3]-$[($[3]-$[0])+($[3]-$[0])])*($[3]-$[($[3]-$[0])+($[3]-$[0])])*($[0]-$[($[3]-$[0])+($[3]-$[0])])+($[3]-$[0]), ($[0]-$[($[3]-$[0])])*(($[0]-$[($[3]-$[0])+($[3]-$[0])])+($[3]-$[($[3]-$[0])+($[3]-$[0])])), ($[0]-$[($[3]-$[0])])*(($[0]-$[($[3]-$[0])+($[3]-$[0])])+($[3]-$[($[3]-$[0])+($[3]-$[0])])), $[($[3]-$[0])+($[3]-$[0])+($[3]-$[0])]+($[3]-$[0]), ((($[0]-$[($[3]-$[0])])+($[0]-$[($[3]-$[0])+($[3]-$[0])]))*($[0]-$[($[3]-$[0])+($[3]-$[0])]))>>($[3]-$[0]), ($[0]-$[($[3]-$[0])])+($[0]-$[($[3]-$[0])])/($[0]-$[($[3]-$[0])+($[3]-$[0])])*($[3]-$[($[3]-$[0])+($[3]-$[0])])*($[3]-$[($[3]-$[0])+($[3]-$[0])]), $[($[3]-$[0])+($[3]-$[0])+($[3]-$[0])]+($[3]-$[0]), $[($[3]-$[0])+($[3]-$[0])+($[3]-$[0])]+($[0]-$[($[3]-$[0])+($[3]-$[0])]), $[0]-($[3]-$[0]), $[($[3]-$[0])]+($[0]-$[($[3]-$[0])+($[3]-$[0])])-($[3]-$[0]), (($[0]-$[($[3]-$[0])])-($[3]-$[0]))*(($[3]-$[($[3]-$[0])+($[3]-$[0])])-($[3]-$[0])-($[3]-$[0])));
}
해설
__func__
는 C++11에 추가된 표준으로, 호출된 함수의 이름을 문자열로 반환한다. 즉, 이 코드는 main
이라는 문자열을 지지고 볶아서 Hello, World!
로 만들어낸 것이다.
레퍼런스
- The C Programming Language
- https://codegolf.stackexchange.com/questions/22533/weirdest-obfuscated-hello-world
- https://www.facebook.com/groups/System.out.Coding/posts/4618578478201811/
글을 쓰기 위한 원동력을 얻기 위해 자발적인 후원을 받고 있습니다.
후원해 주신 분들께는 뉴스레터를 통해 일부 컨텐츠가 선공개될 예정입니다.
(구독자 이름과 후원자 이름이 동일해야 발송이 가능합니다)