[WIN32 API] 'APIENTRY'와 'CALLBACK' 의 의미..

gyxor의 이미지

API정복이라는 책을 처음 읽어 가면서 ...

APIENTRY 와 CALLBACK이 어떤 의미를 가지며 어떤 역할을 하는지가

궁금했습니다.

함수명과 리턴타입 사이에 지시자가 들어가는것도 새로웠구요

devpia에서 검색을 해봤습니다.

http://www.devpia.com/Forum/BoardView.aspx?no=3745&page=1&Tpage=1&forumname=vc_qa&stype=&ctType=&answer=&KeyR=title&KeyC=
위 내용에서 본대로..

"Argument Passing and Naming Conventions"
라는 검색어로 msdn을 살펴보니..

_stdcall과 _cdecl의 차이점이 바로
스택을 청소하는 책임이 caller에 있느냐 callee에 있느냐라고 나와있습니다.

-Microsoft C++ Language Reference for eMbedded Visual C++  
[__cdecl]

Because the stack is cleaned up by the caller, it can do vararg functions. The __cdecl calling convention creates larger executables than __stdcall, 
...
 The callee cleans the stack, so the compiler makes vararg functions __cdecl. 

이것이 사실 이해가 안됩니다.
제가 알기로 스택은 cleaning이 되는게 아니고 스택포인터의 위치만 옮김으로써 마치 스택이 지워진것과 같은 효과를 내는것으로 알고 있습니다.
예를들어
http://bbs.kldp.org/viewtopic.php?t=26042&highlight=
제가 전에 kldp사이트에 올렸던 질문에서처럼
스택을 지우는것은 함수가 진행됨에 따라서 스택포인터가 자연히 이동이 되는것이고 나중에 return 주소값(Location Counter최초 호출 위치) 위치가 저장된 스택의 값을 마지막으로 참조해서 돌아간후 스택이 원위치 되는것으로 알고 있습니다. (물론 과거에 메모리상에 저장된 내용은 엄밀하게 말해서 지워진것은 아닙니다.)


첫째,
따라서 "스택을 clear하는 책임은 caller 또는 callee에 있다" 라고 볼수 없는것 아닌가요?

둘째,
APIENTRY와 CALLBACK 모두 __stdcall에 에 해당한다면
두가지의 차이점은 무엇인지 모르겠습니다.

왜냐면..
두가지를 없애고 테스트 해봤고 바꿔가면서 해봤지만 모든경우에
실행이 되었습니다.

#include<windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass="First";

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
      LPSTR lpszCmdParam,int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst = hInstance;

 WndClass.cbClsExtra = 0;
 WndClass.cbWndExtra = 0;
 WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor = LoadCursor(NULL,IDC_ARROW);
 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance = hInstance;
 WndClass.lpfnWndProc = (WNDPROC)WndProc;
 WndClass.lpszClassName = lpszClass;
 WndClass.lpszMenuName = NULL;
 WndClass.style = CS_HREDRAW | CS_VREDRAW;
 RegisterClass(&WndClass);
 hWnd = CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
  NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);
 while(GetMessage(&Message,0,0,0) ){
  TranslateMessage(&Message);
  DispatchMessage(&Message);}
 return Message.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam){
 switch(iMessage){
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

두가지 답변해주시면 정말 감사하겠습니다.

pynoos의 이미지

Linux 쪽에서는 모두 caller 가 청소하기 때문에 __stdcall과 __cdecl 의 구분이 없습니다.

말씀하신대로 스택 포인터 값을 복귀 시키는 책임이 caller에 있느냐 callee에 있느냐의 차이가 있는 것입니다.

두 차이는 caller가 청소하는 경우 자기가 넘긴것은 자기가 청소하므로 확실합니다. 하지만 callee가 청소하는 경우에는 부르는 쪽과 불리는 쪽이 스택에 들어간 값을 명확히 하지 않으면 나중에 스택포인터가 잘못되는 경우가 생깁니다.

대부분의 Windows API가 stdcall 방식을 사용하고 이 방식이 define 문으로 여러 형태로 나타나곤합니다.

이 관례는 DOS 시절 이전부터 사용된 것을 아직도 그대로 사용하지 않나 싶습니다. callee가 스택을 제거하면 부를 때 마다 제거하는 것보다 몇 바이트 코드를 줄일 수 있거든요...

어떤 것이든 사용자 마음대로 선언할 수 있지만, printf 같이 가변인자를 넘기고 인자 개수를 넘기지 않는 구조에서는 caller가 제거해야만합니다. 따라서 모두 stdcall 이 가능할지라도 가변인자가 사용된 곳은 cdecl 일 수 밖에 없습니다.

pynoos의 이미지

stdcall과 cdecl 만 있는 것이 아니라..

비슷하게 fastcall, C++에서 사용하는 thiscall 등이 있고, 과거에는 그 위치에 pascal 이라는 calling convention도 있었습니다.

simpid의 이미지

pynoos wrote:
stdcall과 cdecl 만 있는 것이 아니라..

비슷하게 fastcall, C++에서 사용하는 thiscall 등이 있고, 과거에는 그 위치에 pascal 이라는 calling convention도 있었습니다.

__stdcall 이 바로 pascal calling conversion 입니다.

thiscall은 C++의 멤버 함수가 사용하는 거니까 어차피 선택사항이 되지 않고(x86 CPU 기준으로 DX레지스터가 this 포인터를 갖았던걸로 기억함->정확치 않음))

fastcall(인자전달에 ECX, EDX 사용)은 VC에서 사용하는 거지만... 예전에 DOOM등을 만들때 쓰였던 Wacom C는 레지스터 4개(eax, ebx, ecx, edx)를 인자 전달로 사용하였었습니다. 두 방식다 2개나 4개 이후의 인자는 스택을 사용합니다.

참고로 pascal(__stdcall) 방식이 c방식 (__cdecl)보다 속도가 빠릅니다.
그래서 Windows에선 pascal 방식을 __stdcall 이라고 이름 붙이고 표준으로 사용합니다.

gyxor의 이미지

스택 포인터를 되돌려 놓는 책임이.. caller 또는 callee 에게 있을
수 있군요..
__stdcall과 _cdecl의 차이점에 관해서는 개괄적으로나마
이해하겠습니다.

전에 찾아본 내용중에..
CALLBACK == PASCAL == WINAPI == __stdcall
이런 내용이 있었는데요..
PASCAL이 있는 이유를 알겠습니다.

그런데 위 내용이 맞다면..
제가 질문한 APIENTRY 와 CALLBACK도 서로 '같다' 라고 보면
되는것인가요?

cinsk의 이미지

글 주제와는 별 상관없지만, 언제부터인가 국내에서는 API가 Win32 API를 의미하는 말이 된거 같군요. GTK+나 다른 API는 API가 아닌가요? -_-;;;

ssehoony의 이미지

pynoos wrote:
Linux 쪽에서는 모두 caller 가 청소하기 때문에 __stdcall과 __cdecl 의 구분이 없습니다.

이거 맞는 말인가요?
기본 값이 __cdecl 이겠지 구분이 없는건 아니겠죠?
g++ 이 콜링컨버전이 안된다는 말씀인가요?

pynoos wrote:

두 차이는 caller가 청소하는 경우 자기가 넘긴것은 자기가 청소하므로 확실합니다. 하지만 callee가 청소하는 경우에는 부르는 쪽과 불리는 쪽이 스택에 들어간 값을 명확히 하지 않으면 나중에 스택포인터가 잘못되는 경우가 생깁니다

값을 명확히 해야한다는건 프로그래머가 명시적으로 사이즈를 알려 주는 방법이 있다는건가요?
컴파일러가 알아서 계산하는거 아닌가요?

caller 가 청소하는 것과 callee 가 하는 것을 별도로 만든것은 안정성 문제가 아니고(둘다 안정적입니다. 컴파일러에 버그가 없다면) caller 가 스택을 정리하게 되면 함수콜이 일어날때 마다 스택을 정리하는 인스트럭션이 소스에 포함되게 되서 실행코드가 커지게되고, callee가 하게 되면 콜된 함수 내부에 스택 정리 인스트럭션이 있어서 caller 가 하는 것 처럼 함수콜에 스택정리 인스트럭션이 필요가 없어서 실행코드가 작아지고 메모리캐쉬 히트율도 높아지는 장점이 생기지요, 하지만 printf 와 같은 파라미터가 가변인것은 함수 콜 할때 결정되는거라 이거는 callee에서 해결할 방법이 없어서 어쩔 수 없이 caller 가 청소를 해야 하지요.
이런 특징 때문에 구별을 하는 것으로 알고 있습니다.

Quote:
Microsoft C++ Language Reference for eMbedded Visual C++
[__cdecl]

Because the stack is cleaned up by the caller, it can do vararg functions. The __cdecl calling convention creates larger executables than __stdcall,
...
The callee cleans the stack, so the compiler makes vararg functions __cdecl.

이 말이 제가 위에서 설명드린 것이죠.

pynoos의 이미지

devilhero wrote:
pynoos wrote:
Linux 쪽에서는 모두 caller 가 청소하기 때문에 __stdcall과 __cdecl 의 구분이 없습니다.

이거 맞는 말인가요?
기본 값이 __cdecl 이겠지 구분이 없는건 아니겠죠?


표현이 명확하지 않아서인데, caller가 청소하는 방법만 있으므로 Linux 쪽에는 __stdcall 이라는 방법이 없다는 말이었습니다.

devilhero wrote:
g++ 이 콜링컨버전이 안된다는 말씀인가요?

이 말은 무슨 뜻이지요?

devilhero wrote:

pynoos wrote:

두 차이는 caller가 청소하는 경우 자기가 넘긴것은 자기가 청소하므로 확실합니다. 하지만 callee가 청소하는 경우에는 부르는 쪽과 불리는 쪽이 스택에 들어간 값을 명확히 하지 않으면 나중에 스택포인터가 잘못되는 경우가 생깁니다

값을 명확히 해야한다는건 프로그래머가 명시적으로 사이즈를 알려 주는 방법이 있다는건가요?
컴파일러가 알아서 계산하는거 아닌가요?

이론상 그렇기 때문에 caller와 callee사이에서 사이즈가 협상이 되어야하며,
이것은 실제로 function naming mangling에 들어 있습니다. __stdcall 로 정의된 함수의 mangling (decoration)에는 스택청소해야하는 크기가 포함됩니다. 따라서 일반 __cdecl 의 경우 c compiler에서는 암묵적인 형선언으로 올바른 link가 일어나지만, __stdcall 과 같이 mangling이 들어가는 경우에는 명시적인 형선언이 앞에 있어야만 링크가 가능합니다.

devilhero wrote:

caller 가 청소하는 것과 callee 가 하는 것을 별도로 만든것은 안정성 문제가 아니고(둘다 안정적입니다. 컴파일러에 버그가 없다면) caller 가 스택을 정리하게 되면 함수콜이 일어날때 마다 스택을 정리하는 인스트럭션이 소스에 포함되게 되서 실행코드가 커지게되고, callee가 하게 되면 콜된 함수 내부에 스택 정리 인스트럭션이 있어서 caller 가 하는 것 처럼 함수콜에 스택정리 인스트럭션이 필요가 없어서 실행코드가 작아지고 메모리캐쉬 히트율도 높아지는 장점이 생기지요, 하지만 printf 와 같은 파라미터가 가변인것은 함수 콜 할때 결정되는거라 이거는 callee에서 해결할 방법이 없어서 어쩔 수 없이 caller 가 청소를 해야 하지요.
이런 특징 때문에 구별을 하는 것으로 알고 있습니다.

Quote:
Microsoft C++ Language Reference for eMbedded Visual C++
[__cdecl]

Because the stack is cleaned up by the caller, it can do vararg functions. The __cdecl calling convention creates larger executables than __stdcall,
...
The callee cleans the stack, so the compiler makes vararg functions __cdecl.

이 말이 제가 위에서 설명드린 것이죠.

이 말은 제가 위에 쓴 글에도 다 있는 내용입니다.

marten의 이미지

cinsk wrote:
글 주제와는 별 상관없지만, 언제부터인가 국내에서는 API가 Win32 API를 의미하는 말이 된거 같군요. GTK+나 다른 API는 API가 아닌가요? -_-;;;

저도 cinsk님과 똑같은 생각을 가지고 있습니다. 왜 API가 Win32 API를 가리키는 말로 통하는지 모르겠습니다. :?

gimmesilver의 이미지

gyxor wrote:
스택 포인터를 되돌려 놓는 책임이.. caller 또는 callee 에게 있을
수 있군요..
__stdcall과 _cdecl의 차이점에 관해서는 개괄적으로나마
이해하겠습니다.

전에 찾아본 내용중에..
CALLBACK == PASCAL == WINAPI == __stdcall
이런 내용이 있었는데요..
PASCAL이 있는 이유를 알겠습니다.

그런데 위 내용이 맞다면..
제가 질문한 APIENTRY 와 CALLBACK도 서로 '같다' 라고 보면
되는것인가요?

APIENTRY나 CALLBACK, WINAPI등은 단순히 __stdcall을 #define으로 재정의한것에 불과합니다...
windef.h 헤더 파일을 한번 살펴보세요...
windows API들이나 콜백함수가 __stdcall로 정의된 이유는 다른 언어에서도 해당 API함수들을 사용할 수 있게 하기 위해서라고 알고 있습니다.

------------------------
http://agbird.egloos.com

gimmesilver의 이미지

marten wrote:
cinsk wrote:
글 주제와는 별 상관없지만, 언제부터인가 국내에서는 API가 Win32 API를 의미하는 말이 된거 같군요. GTK+나 다른 API는 API가 아닌가요? -_-;;;

저도 cinsk님과 똑같은 생각을 가지고 있습니다. 왜 API가 Win32 API를 가리키는 말로 통하는지 모르겠습니다. :?

용어를 잘못 사용하는 사람들이 있긴 하지만 국내에서 통용된다고 생각하시는 것은 좀 확대 해석한 것 같네요...
최초 질문하신 분이 언급한 'API 정복'이라는 책도 정식 명칭은 'Windows API 정복'입니다...

------------------------
http://agbird.egloos.com

gyxor의 이미지

WIN 32 API라고 구체적으로 적지 않은것 죄송합니다.
수정했습니다.

windef.h 파일을 보고
APIENTRY 와 CALLBACK 이 완전 같다는걸 알았습니다.
서로 바꿔서 컴파일 했을때 정상 수행이 되는게 당연한 거였군요..

그런데..

pynoos wrote:

어떤 것이든 사용자 마음대로 선언할 수 있지만, printf 같이 가변인자를 넘기고 인자 개수를 넘기지 않는 구조에서는 caller가 제거해야만합니다. 따라서 모두 stdcall 이 가능할지라도 가변인자가 사용된 곳은 cdecl 일 수 밖에 없습니다.

위 내용에서..

어짜피 사용자 정의 함수들도 파라미터개수가 고정이긴 하지만
개수 자체!를 전달하지는 않는데요..

printf 함수의 경우는 다른 함수와 달리 파라미터개수가
가변적이긴 하지만..
그렇더라도..
호출된 printf 함수 안에서 전달된 파라미터의 개수를 파악할수 있는것 아닌가요?
따라서 printf함수등에서도 stdcall이 사용될 수 있는것 아닌가요?

설명 부탁드립니다.

cocas의 이미지

가변인자 함수들은 함수들의 목적코드가 생성 될 때 인자의 갯수를 알 수 없습니다. 하지만 caller가 printf를 호출 할 때 그 인자의 갯수를 알게 되죠. 그래서 caller가 정리해야 합니다.

하지만 인자가 고정되어 있는 경우 함수의 목적 코드를 만들 때 그 갯수를 이미 알게 됩니다.

pynoos의 이미지

gyxor wrote:
그런데..
pynoos wrote:

어떤 것이든 사용자 마음대로 선언할 수 있지만, printf 같이 가변인자를 넘기고 인자 개수를 넘기지 않는 구조에서는 caller가 제거해야만합니다. 따라서 모두 stdcall 이 가능할지라도 가변인자가 사용된 곳은 cdecl 일 수 밖에 없습니다.

위 내용에서..

어짜피 사용자 정의 함수들도 파라미터개수가 고정이긴 하지만
개수 자체!를 전달하지는 않는데요..

음... 요즘 들어 글을 쓸 때 비교되는 대상에 대해 정확히 써야겠다는 생각이 듭니다. :)
"printf 같이 가변인자를 넘기고 인자개수를 넘기지 않는 구조" 라고 표현한 것이 "존재할지 모르는 어떤 calling convention에서는 인자개수를 넘기는 구조"와 비교하여 표현한 것은 아닙니다.
현재 대부분의 구현에서는 인자로 개수를 넘기지는 않습니다.

gyxor wrote:

printf 함수의 경우는 다른 함수와 달리 파라미터개수가
가변적이긴 하지만..
그렇더라도..
호출된 printf 함수 안에서 전달된 파라미터의 개수를 파악할수 있는것 아닌가요?
따라서 printf함수등에서도 stdcall이 사용될 수 있는것 아닌가요?

설명 부탁드립니다.

printf 는 넘겨 받은 파라미터의 개수를 파악할 수 없습니다. gcc 같은 훌륭한(?) 컴파일러가 보여주는 printf 계열의 함수를 컴파일 할 때 "%"로된 포맷과 인자 개수를 비교하여 warning을 내어주기는 합니다만 이것은 printf가 파악하는 것이 아니라 컴파일러가 훌륭한 것이죠.

다음 프로그램은 적절합니다. printf 에 포맷만 있고 변수는 넘기지 않는 것에 주의해보시고 실행해보세요.

#include <stdio.h>

int main(int argc, char *argv[], char *arge[] )
{
        printf("ARG: %x %x %x\n", &argv, argv, arge );
        printf("%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n");
        return 0;
}

그리고 실행 인자를 주다보면, argc 값에 해당하는 숫자가 바뀌는 것이 있음을 알 수 있습니다.
만약 printf가 넘겨 받은 인자 개수를 안다면 오류를 내고 return 하는 방법이 표준에 들어 갔겠지만, 불행히도 포맷과 넘겨 받는인자의 개수에 대한 오류처리는 없습니다.

이런 문제를 stdcall에서는 어떻게 해결하냐면...

C:\PROGRA~1\MICROS~4\work>type b.c
void __stdcall func_a(int x)
{

}

void __stdcall func_b(int x,int y,char z)
{

}

C:\PROGRA~1\MICROS~4\work>cl /c b.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3052 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.

b.c

C:\PROGRA~1\MICROS~4\work>dumpbin /symbols b.obj
Microsoft (R) COFF/PE Dumper Version 7.10.3052
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file b.obj

File Type: COFF OBJECT

COFF SYMBOL TABLE
000 00000000 DEBUG  notype       Filename     | .file
    b.c
002 005F0BEC ABS    notype       Static       | @comp.id
003 00000001 ABS    notype       Static       | @feat.00
004 00000000 SECT1  notype       Static       | .drectve
    Section length   2A, #relocs    0, #linenums    0, checksum        0
006 00000000 SECT2  notype       Static       | .debug$S
    Section length   66, #relocs    0, #linenums    0, checksum        0
008 00000000 SECT3  notype       Static       | .text
    Section length   17, #relocs    0, #linenums    0, checksum 86D3D5DA
00A 00000000 SECT3  notype ()    External     | _func_a@4
00B 00000010 SECT3  notype ()    External     | _func_b@12

String Table Size = 0x19 bytes

  Summary

          66 .debug$S
          2A .drectve
          17 .text

잘보시면 _func_a , _func_b 라는 심볼 뒤에 @4, @12라는 mangling(decoration)이 붙어 함수 명을 다르게 만들어 주는 것으로 자기가 불리울 때 청소하게될 스택의크기를 이름에 넣어둡니다. 따라서 compiler는 저런 stdcall 류의 함수를 부를 때 이미 몇 바이트를 해제할 것인지 알 뿐, 몇 바이트 해제하라고 지시하지는 않습니다....

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.