sourcecode

이 네 줄의 까다로운 C 코드 뒤에 있는 개념

copyscript 2022. 8. 3. 23:08
반응형

이 네 줄의 까다로운 C 코드 뒤에 있는 개념

코드가 하는 이유는 입니까?C++Sucks그배배 는는 ?념? ??? ???

#include <stdio.h>

double m[] = {7709179928849219.0, 771};

int main() {
    m[1]--?m[0]*=2,main():printf((char*)m);    
}

여기서 테스트해 보세요.

★★7709179928849219.0 64비트로서 .double:

01000011 00111011 01100011 01110101 01010011 00101011 00101011 01000011
+^^^^^^^ ^^^^---- -------- -------- -------- -------- -------- --------

+.^ 그리고 수의 -가즉 ( 즉즉 、 수의의 ) 。

표현은 2진수 지수와 가수 지수를 사용하기 때문에 숫자를 두 배로 하면 지수가 1씩 증가합니다.)는 1075입니다10000110011 )은+ =; 은 1075 + 771 = 1846이다; 1846년 2월11100110110결과 패턴은 다음과 같습니다.

01110011 01101011 01100011 01110101 01010011 00101011 00101011 01000011
-------- -------- -------- -------- -------- -------- -------- --------
0x73 's' 0x6B 'k' 0x63 'c' 0x75 'u' 0x53 'S' 0x2B '+' 0x2B '+' 0x43 'C'

이 패턴은 인쇄된 문자열에 대응합니다(뒤로만).동시에 어레이의 두 번째 요소는 0이 되어 늘 터미네이터를 제공하므로 문자열은 전달하기에 적합하다.printf().

읽기 쉬운 버전:

double m[2] = {7709179928849219.0, 771};
// m[0] = 7709179928849219.0;
// m[1] = 771;    

int main()
{
    if (m[1]-- != 0)
    {
        m[0] *= 2;
        main();
    }
    else
    {
        printf((char*) m);
    }
}

으로 불리다.main()곱하기 771파운드

에 ★★★★★★★★★★★★★★★★★★」m[0] = 7709179928849219.0의 약자입니다.C++Suc;C에서 " " " "m[0]두 배로 늘어나서 마지막 두 글자를 "수리"할 수 있습니다.콜에서는 " " " 입니다.m[0] ASCII 문자 .C++Sucks ★★★★★★★★★★★★★★★★★」m[1]0만 포함되기 때문에 에는 null 터미네이터가 있습니다.C++Sucks이 「」라고 가정하고 있습니다.m[0]는 8바이트에 저장되므로 각 문자는 1바이트가 소요됩니다.

및 부정이 없는 경우main()호출하는 방법은 다음과 같습니다.

double m[] = {7709179928849219.0, 0};
for (int i = 0; i < 771; i++)
{
    m[0] *= 2;
}
printf((char*) m);

면책사항:이 답변은 C++만을 언급하고 C++ 헤더를 포함하는 질문의 원래 형식에 게시되었습니다.질문의 순수 C로의 변환은 커뮤니티에 의해서 행해졌습니다.원래 질문자의 입력은 없었습니다.


형식적으로 말하면, 이 프로그램은 형식이 잘못되어 있기 때문에 추론할 수 없습니다(즉, 합법적인 C++가 아닙니다).C++11 [basic.start.main]p3에 위반됩니다.

프로그램 내에서 기능 메인(function main)을 사용해서는 안 된다.

별개로 에서는 다음과 같은 합니다.double는 8바이트 길이로 잘 알려진 내부 표현을 사용합니다.때 첫 알고리즘의 됩니다.double입니다.C++Sucks배열의 두 번째 요소는 다음과 같습니다.0.0는 ""입니다0내부 표현에서 유효한 C-스타일 문자열로 만듭니다. 이 할 때 쓰다, 쓰다, 쓰다, 쓰다, 쓰다, 쓰다를 사용합니다.printf().

위의 중 일부가 유지되지 않는 HW에서 이 작업을 실행하면 대신 가비지 텍스트(또는 액세스 범위 외)가 발생할 수 있습니다.

가 인쇄됩니다.C++Suc;C

double m[] = {7709179928849219.0, 0};
printf("%s\n", (char *)m);

아마도 코드를 이해하는 가장 쉬운 방법은 거꾸로 일을 처리하는 것이다.이치노C++Rocks를 사용합니다.중요한 점: 원작과 마찬가지로 정확히 8자 길이입니다.원문과 같이 (대략) 역순으로 인쇄하기 때문에 역순으로 인쇄하는 것부터 시작합니다. 단계에서는 이 로 double결과를 출력합니다.

#include <stdio.h>

char string[] = "skcoR++C";

int main(){
    printf("%f\n", *(double*)string);
}

produces이가 생성됩니다.3823728713643449.5그래서 우리는 그것을 어떤 식으로든 조작하고 싶습니다.그것은 명백하지는 않지만 되돌리기 쉬운 것입니다. 256이 978874550692723072이제 256으로 나누기 위해 난독화된 코드를 작성하고, 그 각각의 바이트를 역순으로 출력하면 됩니다.

#include <stdio.h>

double x [] = { 978874550692723072, 8 };
char *y = (char *)x;

int main(int argc, char **argv){
    if (x[1]) {
        x[0] /= 2;  
        main(--x[1], (char **)++y);
    }
    putchar(*--y);
}

많은 를 (으로 있습니다.main물론 우리가 하고 있는 일이 매우 간단하다는 사실을 감추기 위해 완전히 자의적으로 보이는 수치로 보이는 것은 완전히 무시되고 있다(그러나 증감을 얻기 위한 평가는 매우 중요하다).

물론 전체적인 요점이 난독화이기 때문에 그렇게 느낀다면 더 많은 조치를 취할 수 있습니다.해서 '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락 '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락', '단락'의'if스테이트먼트를 단일 표현으로 변환하기 때문에 메인 본문은 다음과 같습니다.

x[1] && (x[0] /= 2,  main(--x[1], (char **)++y));
putchar(*--y);

골프 않은 합니다. 인 코드(또는 코드 골프)를 합니다.andmain 단락 평가가를 깨닫지 못하고 생각하지 재귀를 알 수 경우가 .더욱이 단락 평가가 어떻게 작동하는지 깨닫지 못하고(그리고 생각하지 않고) 무한 재귀를 어떻게 회피하는지는 즉시 명확하지 않을 수도 있습니다.

다음 단계는 각 문자를 인쇄하는 것과 해당 문자를 찾는 것을 분리하는 것입니다.이 , 이 문자의 할 수 .main하는 것은 무엇입니까?main★★★★

x[1] && (x[0] /= 2,  putchar(main(--x[1], (char **)++y)));
return *--y;

적어도 나에게는 그것이 충분히 난해한 것 같기 때문에, 그 정도로 하겠습니다.

먼저 다음과 같이 2진수 형식으로 메모리에 저장되는 것을 기억해야 합니다.

(1) 표식용 1비트

2 지수용 11비트

3 매그니튜드용 52비트

비트 순서는 (i)에서 (iii)로 감소한다.

우선, 10진수를 등가 소수 2진수로 변환한 후, 2진수로 매그니튜드 형식으로 표현한다.

그래서 숫자 7709179928849219.0은

(11011011000110111010101010011001010110010101101000011)base 2


=1.1011011000110111010101010011001010110010101101000011 * 2^52

이제 매그니튜드를 고려할 때 모든 매그니튜드 방법은 1부터 시작하므로 비트 1은 무시됩니다.

따라서 매그니튜드 부분은 다음과 같습니다.

1011011000110111010101010011001010110010101101000011 

이제 2거듭제곱은 52이고, 2^(지수 -1)-1에 대한 비트)-1로 바이어싱 수를 더해야 합니다. 즉, 2^(11 -1)-1 = 1023이므로, 우리의 지수는 52 + 1023 = 1075가 됩니다.

이제 우리의 코드는 2,771배숫자를 나누는데, 이것은 지수를 771배로 증가시킨다.

따라서 우리의 지수는 (1075+771)= 1846이고, 그 2진수는 (11100110110)이다.

이제 우리의 숫자는 양수이므로 부호 비트는 0입니다.

따라서 수정된 번호는 다음과 같습니다.

부호 비트 + 지수 + 매그니튜드(비트 연결)

0111001101101011011000110111010101010011001010110010101101000011 

m은 char pointer로 변환되므로 LSD에서 비트 패턴을 8개의 청크로 분할합니다.

01110011 01101011 01100011 01110101 01010011 00101011 00101011 01000011 

(16진수 등가:)

 0x73 0x6B 0x63 0x75 0x53 0x2B 0x2B 0x43 

ASCII 차트는 그림과 과 같이 나옵니다

s   k   c   u      S      +   +   C 

m[1]이 설정되면 0은 NULL 문자를 의미합니다.

little-endian 머신에서 이 프로그램을 실행한다고 가정하면(낮은 순서의 비트가 낮은 주소에 저장되어 있습니다), 포인터 m 포인터가 가장 낮은 주소 비트로 이동한 후 8의 척(char*에 type casted)의 비트를 차지하면 printf()는 마지막 청크에 0000000000을 포함하면 중지됩니다.

다만, 이 코드는 포터블이 아니다.

이중 배열(16바이트)을 구축하고 있을 뿐이며, 문자 배열로 해석될 경우 문자열 "C++Sucks"의 ASCII 코드를 구축합니다.

단, 이 코드는 각 시스템에서 동작하는 것은 아닙니다.다음의 미정의 사실에 의존합니다.

다른 사람들은 질문을 꽤 자세히 설명했고, 나는 이것이 표준에 따른 정의되지 않은 행동이라는 것을 덧붙이고 싶다.

C++11 3.6.1/3 주요 기능

프로그램 내에서 기능 메인(function main)을 사용해서는 안 된다.main의 링크(3.5)는 구현에 정의되어 있습니다.main을 deleted로 정의하거나 main을 inline, static 또는 constexpr로 선언하는 프로그램은 형식이 잘못되었습니다.main이라는 이름은 따로 예약되지 않습니다.[예: 멤버 함수, 클래스 및 열거는 다른 네임스페이스의 엔티티와 마찬가지로 main이라고 할 수 있습니다.: 엔드 예시]

코드는 다음과 같이 재작성할 수 있습니다.

void f()
{
    if (m[1]-- != 0)
    {
        m[0] *= 2;
        f();
    } else {
          printf((char*)m);
    }
}

있는 은, 「1」의 세트입니다.double 배 arraym'C++Sucks'는 'C++Sucks'입니다.표준 표현에서는 771배로 하면 어레이의 두 번째 멤버가 제공하는 늘 터미네이터를 사용하여 바이트 세트가 생성되는 이중 값을 선택하여 코드를 난독화했습니다.

엔디안을 사용하다 「」, 「」를 합니다.main()는 엄밀하게 허용되지 않습니다.

언급URL : https://stackoverflow.com/questions/17992553/concept-behind-these-four-lines-of-tricky-c-code

반응형