리턴과 포인터에 대해서

ututlinux의 이미지

#include <stdio.h>

int func1(int );
void func2(int, int* );

main()
{

    int a = 1;
    int b;
    int c;

    b = func1(a);
    func2(a, &c);

    printf("b = %d  ",b);
    printf("c = %d", c);

    printf("\n");

    return;

}

int func1(int a)
{
        a += 3;
        return a;
}

void func2(int a, int* x)
{
        *x = (a + 3);
}

저 func1과 func2의 결과는 일반적으로 같은 결과를 나타냅니다.

그런데 분명 저는 학교에서 다른 결과라고 얘기를 들었던거 같은데.
그 이유나 원인에 대해서는 기억이 안납니다.

리턴을 사용할 경우 리턴되는 값을 보장못한다.
왜냐면 그 함수를 빠져나가는 순간 그 리턴되는 변수의 주소는 무효화 되기 때문이다.
이게 맞나요?

저게 그저 학생들 프로그래밍 숙제내는데는 별 지장없을지라도...
지금 제가 보고 작성하는 함수들에대해서는 상당히 심각한 일을 만들수 있다고 생각합니다.
그리고 기존의 다른 프로그래머가 짜놓은 것도 리턴은 사용안하고 있고..
하지만 저는 리턴이 쓰기에는 아주 편한거 같아서 미련을 못버리고 있습니다.

아무리 제가 초보라도, 이미 현업에 뛰어든 상황에서도 저런 부분을 다시 생각하고 있는 제 자신이 부끄럽기까지 합니다만.
한번만 쪽팔리면 될듯 해서 여러 고수분께 여쭤봅니다.

만일 여러분께서 soft Real time 시스템을 개발하고 계신다면...
어느 코드를 사용하시겠습니까?

리턴입니까? 포인터입니까?

여기서 동적 메모리 할당에 대한 얘기는 제외하겠습니다.
전 간단하게 자주 쓸 방법론을 여쭤보려하는 거거든요. ^^

그럼 고견 부탁드립니다.

넘 간단하고 당연한거 물어본다고 면박이나 안주실지 걱정입니다.

버려진의 이미지

#include <stdio.h>

void func1(int);
void func2(int*);

int
main()
  {
    int a = 0;

    func1(a);
    printf("%d\n", a);

    func2(&a);
    printf("%d\n", a);

    return 0;
  }

void
func1(a)
  int a;
  {
    a = 100;
  }

void
func2(a)
  int * a;
  {
    *a = 200;
  }

학교에서 다른 결과라 했다는걸 보니 이런 예를 말씀하시는 것 같네요. 결과값은

Quote:
0
200

이렇습니다. 이유는.. func1()을 호출하는 방식에서는 a의 값을 전달해줍니다. 말 그대로 값만 전달됩니다. a가 0이었으니까 스택에 0을 넣고 func1()을 호출합니다. 복사됐다고 표현하면 될까요? func1()에서 받은 0을 암만 바꿔봐야 main함수 내의 a는 그대로입니다.

의도를 맞게 이해했나 모르겠네요. :roll:

익명 사용자의 이미지

void file_open_one(FILE* fp, char* name) {
FILE* tmp;
tmp = fopen(name, "r");
fp = tmp;
}

FILE* file_open_two(char* name) {
return fopen(name, "r");
}

int main(void)
{
FILE* fp;

file_open_one(fp, "hello.dat");
/* 어쩌고 저쩌고.... */
fclose(fp);

fp = file_open_two("hello.dat");
/* 어쩌고 저쩌고.... */
fclose(fp);

return 0;
}

file_open_one 과 _two 가 논리적으로 하는 일이 똑같아 보이지만 실제로 _one 에서는 문제가 생깁니다. return 에서 문제가 되는 경우는 주로 메모리 할당이나 포인터에 관련된 것이고 그 밖에는 무슨 문제가 되는지 모르겠네요. 글 쓰신 분이 쓴 예는 아무런 문제가 없어 보이네요.. -_-;

doldori의 이미지

ututlinux wrote:
리턴을 사용할 경우 리턴되는 값을 보장못한다.
왜냐면 그 함수를 빠져나가는 순간 그 리턴되는 변수의 주소는 무효화 되기 때문이다.
이게 맞나요?

정확하게 얘기하면 함수 내의 자동 변수에 대한 포인터를 반환하는 것은
잘못이라는 것입니다.

int* f()
{
    int i;
    return &i;    // error
}

int* g()
{
    static int i;
    return &i;    // ok
}

int* h()
{
    int* p = malloc(sizeof(int));
    return p;      // ok
}

ututlinux wrote:
리턴입니까? 포인터입니까?

상황에 맞게 선택하시면 됩니다. 보통 반환값은 함수가 에러 없이 정상적으로
수행되었는지 판단할 목적으로 많이 쓰입니다. 그러면 코드가 간결해질 때가 많지요.
반드시 포인터를 써야 할 때도 있습니다.
1. 인자가 함수 내에서 변경될 때
2. 배열을 넘길 때
3. 구조체를 넘길 때(구조체 변수가 함수 내에서 변경되지 않는다면 변수 자체를
넘겨도 되지만 비효율적이므로 포인터로 넘깁니다.)
익명 사용자의 이미지

call by value라는 말의 뜻과, 함수 내부에서 매개변수의 변경이 함수 외부에 전혀 영향을 미치지 못한다는 것만 기억하시면 됩니다.

그렇기 때문에 함수 내부에서 외부의 값을 수정해야 될 경우에는 그 변수의 포인터 주소값을 '값'으로 받아들여서 간접적으로 접근합니다. 위에 분이 올린 file_open_one() 함수를 정상적으로 동작하게 고치면 다음과 같습니다.

void file_open_one(FILE ** fp, char* name)
{
    FILE* tmp;
    tmp = fopen(name, "r"); 
    *fp = tmp; 
} 

int main(void)
{
    File * fp;
    ...
    file_open_one(&fp, "filename.dat");
    ...
}

함수 내부에서 fp를 수정한다고 해도, fp는 단지 외부의 변수로부터 그 안의 값을 복사해서 넘겨받은 임시 객체에 지나지 않기 때문에, 함수가 끝나면 당연히 소멸되며 외부의 변수에 영향을 주지 못합니다. 그렇기 때문에 그 객체의 포인터 주소값을 넘겨받아서 참조 연산자 * 를 이용해서 간접적으로 접근해야 합니다.

scanf() 함수를 사용할 때 매개변수 이름 앞에 &를 붙이는 이유도 이 때문입니다. 변수의 값을 넘겨받는 것이 아니라 변수의 포인터 주소값을 넘겨받습니다.

lyster의 이미지

int func1(int a) 
{ 
        a += 3; 
        return a; 
} 

위의 함수는 return by value이기 때문에 지역변수 무효화와는 관계없습니다.

게으름은 이제 그만

ututlinux의 이미지

여러분의 답변에 많은 걸 생각하게 되었습니다.
감사합니다.

Quote:
리턴을 사용할 경우 리턴되는 값을 보장못한다.
왜냐면 그 함수를 빠져나가는 순간 그 리턴되는 변수의 주소는 무효화 되기 때문이다.

사실 이말은 만일 func1의 리턴 변수.. 자동변수가 리턴값으로 넘어갈경우 함수를 빠져나오는 순간(스택에 잡혀있던 func1의 메모리 영역 전체가 free가 되는 순간이기도 하겠지요.)!.. 그 자동변수가 있던 스택 영역은 다른 프로세스도 접근할 수 있는 free영역이 되버려서 func1을 call한 함수가 그 리턴값을 받기전에 임의의 프로세스가 그 영역에 메모리를 잡고 써버릴경우 그 리턴값에는 뭐가 들어갈지 아무도 모르지 않을까하는 것입니다.
물론 저기에는 여러개의 프로세스는 없습니다만 실제로는 여러개의 프로세스들이 신나게 돌고 있지 않습니까?

리얼타임 시스템의 경우 그것은 충분히 치명적인 결과를 초래할 수 있다고 봅니다만...
제 생각에 태클좀 걸어주세요.

이참에 버릇 완전히 뜯어 고칠 맘입니다.

doldori의 이미지

ututlinux wrote:
Quote:
리턴을 사용할 경우 리턴되는 값을 보장못한다.
왜냐면 그 함수를 빠져나가는 순간 그 리턴되는 변수의 주소는 무효화 되기 때문이다.

사실 이말은 만일 func1의 리턴 변수.. 자동변수가 리턴값으로 넘어갈경우 함수를 빠져나오는 순간(스택에 잡혀있던 func1의 메모리 영역 전체가 free가 되는 순간이기도 하겠지요.)!.. 그 자동변수가 있던 스택 영역은 다른 프로세스도 접근할 수 있는 free영역이 되버려서 func1을 call한 함수가 그 리턴값을 받기전에 임의의 프로세스가 그 영역에 메모리를 잡고 써버릴경우 그 리턴값에는 뭐가 들어갈지 아무도 모르지 않을까하는 것입니다.
물론 저기에는 여러개의 프로세스는 없습니다만 실제로는 여러개의 프로세스들이 신나게 돌고 있지 않습니까?

리얼타임 시스템의 경우 그것은 충분히 치명적인 결과를 초래할 수 있다고 봅니다만...

단일 프로세스든 다중 프로세스든, 리얼타임이든 뭐든 관계없이
치명적인 결과를 초래할 수 있습니다.

익명 사용자의 이미지

함수의 결과값이 반환될 때에도 역시 return by value 입니다. 값이 복사되는 것이지 변수 그 자체가 전달되는 것이 아닙니다. 따라서 함수 내부의 임시객체에 대한 포인터값을 반환하는 것이 아니라면 그에 대해서는 걱정하실 필요가 없습니다.

return a;

이것은 a에 저장된 값을 복사해서 반환한다는 뜻이지, 변수 a 자체를 반환한다는 뜻이 아닙니다.

doldori님이 제시한 예제를 다시 한번 보시길 바랍니다.

그리고 C언어는 멀티 프로세서 시스템에서의 동작을 가정하지 않았습니다. ++i; 같은 간단한 수식마저도 문제를 일으킬 수 있습니다. 멀티 프로세서 시스템에서의 정상적인 동작을 위해서는 구현체에서의 추가적인 라이브러리라던가 기타 여러가지 지원이 필요합니다.

ututlinux의 이미지

손님님..

return a;

그렇다면 함수를 종료하기 전에 복사를 하나요?
아니면 함수 종료후에 복사를 하는 건가요?

함수 종료후라면 복사시도중 변수a의 값이 다른 프로세스에 의해서 덮혀 씌워지면
곤란할 것 같은데.

만일 return이 함수 종료 전에 복사를 마치고 main의 변수에다 넘겨주는 걸 모두 끝낼 수 있는 것이라면, 어떤 상황에서도 안전하게 리턴값을 넘겨줄수 있다면야...
제가 그럼 구지 버릇 고치려고 노력할 필요 없을 것 같습니다만.

lyster의 이미지

시스템마다 다르지만 보통 반환값은 레지스터에 저장하고, 함수가 끝난뒤 호출자가 레지스터의 내용을 읽어 자신의 변수에 기록합니다.
즉 변수 a 값이 덮혀 쓰여질 수가 없죠..

만약 레지스터의 갯수가 적은 시스템이라면 스택에 반환값을 저장할 호출자의 변수주소를 넘겨주어서 바로 기록하거나,
아니면 반환값을 호출자의 변수에 저장할 때까지 a의 메모리를 유지하는 방법도 있을 수 있을 겁니다.

어떤 방법을 쓰던 반환값을 제대로 전달해 주는게 컴파일러와 OS의 역할입니다. 그걸 못하는 시스템은
멀티프로그래밍을 지원한다고 말할 수 없습니다.

게으름은 이제 그만

익명 사용자의 이미지

혹시나 해서 적어두는 것이지만

File * file_open(const char *filename)
{
    File *fp;
    fp = fopen(filename, "r");
    return fp;
}

char * string_copy(const char *str)
{
    char *s;
    s = malloc(strlen(str) + 1);
    while ( str++ ) { s++ = str; }
    s = '\0';
    return s;
}

....
const char *str1 = "string1";
char *str2;
str2 = string_copy(str1);
printf("%s", str2);
....

위의 코드에는 아무런 문제가 없습니다.

return 문 자체의 리턴값 전달방식이 불안하다고 오해하시면 곤란합니다. return문을 통해서 리턴값은 '안전하고' '확실하게' 전달됩니다. 다만 그 리턴값이 포인터 주소값이고, 그 포인터 주소값이 가르키는 객체가 이미 사라졌다면 문제가 될 수 있습니다.

void func1(char ** p)
{
    char t[10];
    *p = t;
}

char * func2(void)
{
    char t[10]
    return t;
}

func2가 잘못된 이유는 return 문을 사용했기 때문이 아니라, 함수가 끝남과 동시에 사라지는 (즉, 외부에서 참조될 때에는 이미 존재하지 않는) 지역변수의 주소값을 외부로 전달했기 때문입니다.

익명 사용자의 이미지

ututlinux wrote:

그렇다면 함수를 종료하기 전에 복사를 하나요?
아니면 함수 종료후에 복사를 하는 건가요?

그 전달방식이 어떠한 식으로 이루어지는지, 혹은 안전한지에 대해서는 걱정하실 필요가 없습니다. 레지스터에 쓰던, 스택에 쓰던, 구현체는 안전하게 리턴값을 전달하도록 구현될 의무가 있습니다. 그걸 못한다면 C 컴파일러라 불릴 자격이 없습니다.

익명 사용자의 이미지

참고로 위에 위에 글에서 예로 든 func1과 func2 모두 잘못된 함수입니다.

익명 사용자의 이미지

char * string_copy(const char *str) 
{ 
    char *s; 
    s = malloc(strlen(str) + 1); 
    while ( str++ ) { s++ = str; } 
    s = '\0'; 
    return s; 
}

틀린 부분이 있어 정정합니다.
while ( s++ = str++ ) { } 이 맞습니다.

ututlinux의 이미지

Quote:
반드시 포인터를 써야 할 때도 있습니다.
1. 인자가 함수 내에서 변경될 때
2. 배열을 넘길 때
3. 구조체를 넘길 때(구조체 변수가 함수 내에서 변경되지 않는다면 변수 자체를
넘겨도 되지만 비효율적이므로 포인터로 넘깁니다.)

돌도리님 리스터님 그리고 손님님...
여러분 덕분에 저 말을 이해 하겠습니다.
촙님도 감사합니다.

간략하게 제가 이해한걸 정리해 드리자면...

리턴값이 들어있는 레지스터내에 CPU가 직접적으로 참조할수 있는 리턴일 경우는 안전하다.
하지만 그 레지스터에 있는 리턴값이 포인터일 경우는 안된다. 그 포인터가 가르키는 주소에는 이미 의미없는 값들이 있을수 있기때문이다.

이런 말씀들이신가요?

이건 버릇 고치고 안고치고의 문제가 아닌듯하네요.

무식해서 생긴버릇인 듯.. 후~

그나저나 모두모두 훌륭들하시고 자상들하시네요. ^^
부디 좋은 주말 되시기 바랍니다.
전 이것만으로도 좋은 주말 된거 같습니다.

익명 사용자의 이미지

ututlinux wrote:

리턴값이 들어있는 레지스터내에 CPU가 직접적으로 참조할수 있는 리턴일 경우는 안전하다.
하지만 그 레지스터에 있는 리턴값이 포인터일 경우는 안된다. 그 포인터가 가르키는 주소에는 이미 의미없는 값들이 있을수 있기때문이다.

이런 말씀들이신가요?

레지스터나 CPU 하고는 전혀 상관없는 이야기 이며, 객체의 기억 수명과 관련된 이야기 입니다. 심지어는 return의 방식하고도 별로 관계가 없는 이야기 입니다.

void func(int p1, p2, p3)
{
    int a, b, c;
    ...
}

위의 코드에서 p1, p2, p3는 func란 함수가 호출되면서 만들어지고, 값이 반환되면서 없어지는 지역 변수들입니다. 그 기억 수명은 정확히 함수가 호출되면서부터 반환될 때 까지 뿐입니다.

만약 위의 지역변수들에 대해서 함수 외부에서 포인터를 이용해서 참조하게 된다면?

int * func(void)
{
    int a = 3;
    return &a;
}

...
int *pi;
pi = func();
printf("%d", *pi);
*pi = 4;

우선 pi = func();에서 func() 함수가 호출되면서 함수 내의 지역변수 a는 만들어집니다. 그리고 그 지역변수 a의 포인터 주소값을 반환하며, 함수가 끝나면서 지역변수 a는 소멸합니다. 그러나 포인터 주소값은 어쨌든간에 함수 외부로 전달이 됩니다.

그리고 printf("%d", *pi); 라는 코드에서는 이미 사라진 변수에 대해서 읽어오는 것을 시도하게 됩니다. 그동안 a 안의 값이 변하지 않았다면 3이 출력되겠지만, 그렇지 않을 수도 있습니다.

운영체제가 그 사이에 해제된 메모리를 다른 용도로 사용했을 수도 있습니다. 또한 요즘의 CPU는 물리적인 메모리 주소값을 그대로 쓰는 것이 아니라 가상 주소값을 쓰기 때문에 같은 주소값이라 하더라도 그 주소값이 가리키는 물리적인 위치가 다를 수 있습니다. 어쨌던간에, 여기서 중요한 것은 '운영체제가 ~해서' 혹은 'CPU 구조가 ~해서'라는 이유가 아니라, 이미 사라진 객체에 대한 참조에 대한 결과는 정의된 바 없다는 겁니다. 공원에 놔두고 온 돈가방이 어떻게 될 지는 아무도 보장하지 못합니다. 누군가 가져갈 수도 있겠고 그대로 있을 수도 있습니다.

printf 문이라면 단지 읽는 것 뿐이므로 문제가 생기지 않을 수도 있겠지만 그 다음의 줄 *pi =4;에서는 아주 문제가 심각합니다. 그냥 pi가 가르키는 메모리 공간을 쓰지 않고 놔뒀을 수도 있지만 이미 다른 용도로 쓰고 있을 수도 있습니다. 따라서 그 공간을 사용하고 있는 다른 곳에서 문제가 발생할 수 있습니다.

그러면, return 문을 사용하지 않고 매개변수에서 포인터를 이용해서 전달하면 뭔가 달라질까요?

void func2(int * * ppi)
{
    int a = 3;
    *ppi = &a;
}

똑같은 이유로 잘못된 코드입니다.

그러면 이러한 문제가 함수 호출시에만 발생할 수 있을까요? 아닙니다.

char *str;
str = malloc(10); // str에 메모리 할당
free(str); // str에 할당된 메모리 해제
...
str[3] = 'a';

이것 역시 똑같은 이유로 잘못되었습니다.

익명 사용자의 이미지

틀린 부분이 있어서 정정합니다.

void func(int p1, p2, p3)
{
int a, b, c;
...
}

위의 코드에서 p1, p2, p3는

-> 위의 코드에서 p1, p2, p3, a, b, c는

익명 사용자의 이미지

단순히 리턴값의 타입이 포인터이기 때문에 문제가 생기는 것이 아닙니다. 그 포인터가 가르키는 개체가 존재하느냐 아니냐의 문제입니다.

익명 사용자의 이미지

Anonymous wrote:
혹시나 해서 적어두는 것이지만

File * file_open(const char *filename)
{
    File *fp;
    fp = fopen(filename, "r");
    return fp;
}

char * string_copy(const char *str)
{
    char *s;
    s = malloc(strlen(str) + 1);
    while ( str++ ) { s++ = str; }
    s = '\0';
    return s;
}

....
const char *str1 = "string1";
char *str2;
str2 = string_copy(str1);
printf("%s", str2);
....

위의 코드에는 아무런 문제가 없습니다.

return 문 자체의 리턴값 전달방식이 불안하다고 오해하시면 곤란합니다. return문을 통해서 리턴값은 '안전하고' '확실하게' 전달됩니다. 다만 그 리턴값이 포인터 주소값이고, 그 포인터 주소값이 가르키는 객체가 이미 사라졌다면 문제가 될 수 있습니다.

void func1(char ** p)
{
    char t[10];
    *p = t;
}

char * func2(void)
{
    char t[10]
    return t;
}

func2가 잘못된 이유는 return 문을 사용했기 때문이 아니라, 함수가 끝남과 동시에 사라지는 (즉, 외부에서 참조될 때에는 이미 존재하지 않는) 지역변수의 주소값을 외부로 전달했기 때문입니다.

코드 자체에는 문제가 있을 수가 없습니다. 컴파일 되어서 돌아가기만 한다면 버그도 사람관점에서의 문제(?)이지 컴퓨터관점에서는 문제가 아니지요. 초보자에게 C가 어려운 점이 사람관점에서의 문제 때문이라고 생각합니다. fileopen에서 든 예는 학부 2학년때 숙제 하다가 밤을 꼴딱 새웠던 문제였습니다. (정작 숙제 알고리즘 코딩은 못하고... -_-;) 몇년 지나서 우연히 보니까 위에서 글쓰신 분 말처럼 좀 깊이있게 들어가야 했던 문제였습니다. '그러게 매뉴얼을 잘 읽어봐야지..'라고 하신다면 할말 없지만.. 내일까지 숙제 내야 했던 그때는 정말 미치고 환장하겠더군요. ^_^

ututlinux의 이미지

이글, 그리고 훌륭한 리플들...

명예의 전당에 올려야 하는 것 아닌가요?

너무 아까운데요.

^^

정말 좋은 정보 감사드립니다.

담배피구 왔더만.. 좋은 정보들이 더 많이... 우훗~ ^^;;

익명 사용자의 이미지

Anonymous wrote:

코드 자체에는 문제가 있을 수가 없습니다. 컴파일 되어서 돌아가기만 한다면 버그도 사람관점에서의 문제(?)이지 컴퓨터관점에서는 문제가 아니지요.

우리가 보통 문제가 있는 코드라고 하는 것은, 프로그래머가 의도한 대로 동작하지 않는 코드를 뜻합니다. '문제'란 단어를 다른 뜻으로 사용하고 계신 것 같은데, 질문자를 혼동케 할 수 있는 말은 삼가해 주시지 않겠습니까?

익명 사용자의 이미지

Anonymous wrote:
while ( s++ = str++ ) { } 이 맞습니다.

또 틀렸군요. while( *s++ = *str++ ) { } 가 맞습니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.