void*&캐스팅 질문과 const지정자 질문

somoe232의 이미지

제가 2번째 질문을 찾아 웹을 방황하다가 오히려 헷갈리는 코드를 맞닥뜨렸어요.
일단 출처는 스택오버플로우

#include <stdio.h>
 
void f(const int** p) { }
void main1()
{
	int* p;
	f(&p);
}
// error C2664: 'f' : cannot convert parameter 1 from 'int **' to 'const int **'
// Conversion loses qualifiers

지정자를 잃는 형태로 변환할 수 없단 건 알고있지만, 저건 지정자를 잃은 게 아니라 더한 거 아닌가요?
헷갈립니다... int*앞에 const를 붙이면 잘 되긴 합니다만..


그리고 두번째 원래 알고싶던 건데

#include <stdio.h>
 
void VoidPtrSwap(void*& ap, void*& bp)
{
	printf("&ap: %p\n ap: %p\n*ap: %p\n", &ap, ap, *(int*)ap);
	void* t;
	t = ap;
	ap = bp;
	bp = t;
	printf("%p %p\n", ap, bp);
}
void IntPtrRef(int*& a) { }
 
int main()
{
	int* a;
	IntPtrRef(a);
 
	char* ap = "success";
	char* bp = "failure";
	VoidPtrSwap((void*&)ap, (void*)bp);
	// ap는 허용되지만 bp는 허용 안 됨. 하지만...
	// void* bp = "failure";
	// VoidPtrSwap((void*&)ap, bp);
	printf("%s\n%s", (char *)ap, (char *)bp);
}

음.. int*가 int*&로 될 수 있듯이 char* -> void* -> void*&가 될 수 있을 줄 알았는데 안 되는 이유를 잘 모르겠어요.
스택오버플로우에선 type system breaking이라고 하지만 그 설명이 오히려 제게 첫번째 질문이란 혹을 달아버리는 바람에;;
그런데 오히려 처음부터 void*로 선언하면 딱히 문제가 없는 걸 보아 void* -> void*&는 문제가 없는 것 같더라고요.
근데 강제캐스팅한 변수는 암묵적 형변환이 안 된다니... 같은 void*가 아닌 걸까요?

꼭 답변이 아니라 실마리라도 좋을 것 같아요. 답글 부탁드립니다.

익명 사용자의 이미지

C++은 문외한이라, 첫번째 질문만 답변하겠습니다.

타입 한정자(Type-qualifier, const, restrict, volatile가 이에 해당) q에 대해서,
pointer to a non-q-qualified type은 pointer to the q-qualified vertsion of the type으로 변환될 수 있습니다.
따라서 아래의 ps는 pcs로 변환될 수 있습니다. 이것은 흔히 보던 방식일 것입니다.

char * ps : pointer to char
const char * pcs : pointer to const char

하지만, 이건 오로지 pointer to 다음 딱 1단계에서만 된다는 것을 알 필요가 있습니다. pointer to pointer to... 와 같이 다단계의 포인터일 경우, 그 안쪽에는 적용되지 않습니다.

int ** ppi : pointer to pointer to int
const int ** ppci : pointer to pointer to const int
int * const * pcpi : pointer to const pointer to int

ppi를 받을 수 있는 것은 두번째의 ppci가 아니라 pcpi 입니다.
즉, ppci = ppi 라는 대입 연산은 안되지만
pcpi = ppi 는 됩니다.

pointer to int가 pointer to const int로 변환될 수 있으니까,
pointer to pointer to const int도 pointer to pointer to int로 변환될 수 있을거라 생각하기 쉬운데,
ppci = ppi 라는 대입 연산이 허용되는 것이 아니라
(*ppci) = (*ppi) 라는 대입 연산이 허용되는 겁니다.

C++은 타입 시스템이 너무 복잡해서 두번째는 잘 모르겠네요.

익명 사용자의 이미지

근데 기억을 더듬어 생각해보니...
C++에서는 C와 다르게 void * -> char * 로의 암묵적 형변환이 안됩니다.

그래서 C에서는 malloc() 앞에 타입 캐스팅 연산자를 붙일 필요가 없는데도
C++와의 호환성 때문에 붙이는 사람들이 많지요.

위의 경우도 아마 그래서인 것 같네요. void * 에서 char * 로 호환이 안될 겁니다 아마.
char * 에서 void * 로의 암묵적 형변환은 되는지 안되는지 잘 모르겠네요...
될 꺼 같긴 한데 기억이 가물가물...

익명 사용자의 이미지

에고에고...
잘못 생각했습니다. 답변 달고 다시 보니 그런 문제가 전혀 아니네요...

VoidPtrSwap((void*&)ap, (void*)bp);

여기서 2번째 함수 인자인 (void*)bp는 r-value 값이니까,
2번째 함수 인자가 안되는 이유는 레퍼런스를 취할 수 없기 때문인것 같네요.

첫번째 함수 인자가 되는 이유가 좀 설명하기 곤란하네요.
제가 C++ 쪽 개념에 좀 약해서...

익명 사용자의 이미지

C++을 잘 모른다는걸 미리 밝혀두고
첫번째 함수 인자가 되는 이유를 추측해 보자면...

reference to ... 타입은 아무래도 l-value인것 같네요.
그렇다면, (void *&)는 reference to pointer to void 일테고,
ap는 실재하는 pointer to char 형 변수니까
reference to pointer to void 로 변환하면
char * 형인 ap를 함수에서는 마치 void * 형인 변수처럼 접근을 하겠죠.

다행히, 대부분의 구현체에선 char * 형하고 void * 형하고는 내부 표현이 같으니까 별 문제 없는 걸테고...

근데, C++에서도 char * 형과 void * 형이 내부 표현이 같다는 가정이 안전한가요?
C++ 잘아시는 분들께서 추가로 설명을 해주시길 부탁드립니다.

somoe232의 이미지

그럼 const가 사이에 낀 포인터는 직접 대입하긴 불가능한 거네요;;

하기야 그래야할 경우가 많진 않겠지만, 이게 당연한 이유가 따로 있을까요?

정말 C++문법은 단순하지만 직관적이진 않은건지...

익명 사용자의 이미지

pointer to pointer to const int에 pointer to pointer to int를 대입할 수 있다는건,
pointer to pointer to const int형 변수가 pointer to int를 가리킬 수 있다는 말과 같습니다.

int * pi;
const int ** ppci = π // 원래는 잘못된 코드지만, 이게 가능하게 된다!

이게 가능하게 되면 어떤 일이 벌어지냐 하면...

int * pi;
const int ** ppci = π // 당연히 이러면 안되지만 일단 가능하다 치자
const int ci = 3;
*ppci = &ci; // 아무 문제 없음.
*pi = 4; // 타입 상으로는 아무 문제 없지만, 실질적으로는 const인 변수에 쓰기를 시도하는 사건이 벌어진다.

즉, 위의 대입을 허용하게 되면, 컴파일시에 타입만 갖고 검사해서는 const에 대한 쓰기를 제대로 막을 수가 없습니다.

따라서, 허용하지 않는게 맞습니다.

그리고, C++ 문법은 절대 단순하지 않습니다. 단순한건 C까지만이고, C++은 정말 더럽습니다.
C99 표준은 기껏해야(?) 554쪽인데 비해, C++ 98는 C90에 상당부분을 의존하고 있으면서도 거기에 더해 776쪽입니다.
형변환의 복잡함이나 템플릿의 난해함이야 말할 필요도 없지요.

무엇보다 가장 큰 문제점은, C와 C++이 겹치는 부분 중에, 동일하게 동작하지 않는 부분이 있다는 겁니다.
(반면에, Objective C는 C++과 다르게 C의 완전한 수퍼셋입니다)

개인적으로 C++은 죽어야 되는 언어라고 생각합니다.
근데, 또 마땅한 대한이 없는게 현실이다 보니...

somoe232의 이미지

const int** ppci = π;가 ppci = &pi;구문인 거 맞죠? (HTML때문에 자동으로 바뀌는 군요;;)
그러면 *ppci = &ci;에서 pi에 ci 주소값이 들어갈테고... 그 pi는 const지정이 안 되어 있을 수 있는 게 문제라는 거네요.

그럼 포인터 다음 딱 1단계에만 그런 한정자를 바꾸면서 대입 가능하게 한 건 이 정도는 관리가 쉬울 거라는 계산인 건가요?
예를 들면 strlen(const char*)에 포인터를 집어넣고 그 내용을 스레드로 수시로 바꾸는 괴랄한 행동은 하기도 힘들테니까... 라거나?

익명 사용자의 이미지

관리라는 말이 무슨 의미로 사용되었는지 모르겠군요. 일반 객체를 const인것처럼 읽기 전용으로 다루는 것은 문제가 생기지 않으며, 따라서 허용하는 대로만 사용한다면 (프로그래머에 의한 것이든 컴파일러나 OS에 의한 것이든) 따로 관리나 주의가 필요하진 않습니다.

단, 멀티 쓰레드에서의 문제는 약간 다른 문제입니다. C나 C++은 언어 자체적으로는 멀티 쓰레드를 염두에 두고 있지 않습니다. 따라서 멀티 쓰레드를 지원하는건 다른 층위에서 해야 할 일입니다. 컴파일러 제조업체의 확장이거나, 기타 라이브러리나 API라던나... 제시하신 상황은 C/C++에서 언어 자체적으로 알아서 해결해야 할 문제가 아니고, 프로그래머가 락을 걸어가며 확인해야 할 문제입니다.

somoe232의 이미지

중첩된 포인터 타입을 T타입이라고 간주하고 그 안쪽의 포인터 타입은 건드릴 수 없다고 생각하면 간단하네요 ㅎ
이렇게 보니까 오히려 이 문법이 더 간단하다고 느꼈어요.

klyx의 이미지

(void*)p 는 rvalue(임시변수)입니다. rvalue를 reference로 받는 것은 const reference(이경우에는 void *const &)나 C++11에서 추가된 rvalue-reference만 가능합니다.
더불어서 당연히 테스트코드라 의미가 없다고는 생각하지만 혹시나 실제로 쓰신다면 swap의 그러한 구현은 피하시고 템플릿을 쓰셔야 합니다.

somoe232의 이미지

(void*)p가 rvalue취급을 받는 건 알았어요. 근데 제가 생각한 char* -> void* -> void*& 변환에선 어디가 문제점이 되나요?
스왑에서 rvalue reference는 필요가 없으니까.. 원래는 캐스팅이 필요없는 함수를 만들고 싶었는데
error C2664: 'VoidPtrSwap' : cannot convert parameter 1 from 'char *' to 'void *&'
이러면서 막혀서 우회한 거였거든요.

함수 원형을

void VoidPtrSwap(void*&& ap, void*&& bp)
로 바꿨을 때
error C2664: 'VoidPtrSwap' : cannot convert parameter 1 from 'void *' to 'void *&&'
                             You cannot bind an lvalue to an rvalue reference

가 떴거든요.

넘겨줄 때 lvalue를 넘겨준거면 expression category문제는 없는 게 아닌가 싶은데요..

일단 제가 이해를 너무 못한 게 아닌가 싶어 생각이 맞는지 여쭤봅니다.

익명 사용자의 이미지

그렇게 포인터를 강제로 형변환 하면서 타입을 속여 객체를 다루는 방식은
C에서도 매우 위험한 방식이고, C++ 이라면 더더욱 좋지 못한 방식입니다.
C++들어서 형변환이 더욱 엄격해진건 이유가 다 있습니다.

템플릿을 쓰시면 훨씬 더 쉽고 간단하게 Swap 함수 제작이 가능합니다.

template <class T> void swap ( T& a, T& b )
{
    T c(a); a=b; b=c;
}

그리고 &&가 원래 되는 거였나요? 레퍼런스의 레퍼런스는 의미가 좀 이상한데...
&&는 요새(?)는 rvalue reference라는 특별한 의미로 쓰이는거 같더군요.

에러 메시지도 그런 의미인거 같습니다.
lvalue인 매개변수를 rvalue-reference로서 사용할 수 없다고...

somoe232의 이미지

맞아요. 이전 댓글에서 &&은 우변값 레퍼런스라는 뜻으로 쓴 거고, 그렇게 함수원형을 바꿔봤던 이유가 혹시 변수의 범주(좌변 혹은 우변..)가 달라서 형변환이 안 됐던 건지 확인하기 위해서였어요.

char* -> void* -> void*&로의 암묵적 형변환을 막는 직접적인 원인(문법적 제한)이 뭔지 알 수 있을까요?

klyx의 이미지

C++의 레퍼런스는 포인터를 쓰기 좋게 포장한겁니다. 포인터로 안되는걸 레퍼런스로 할 수는 없습니다. void *&는 결국 void **를 넘겨주는 셈입니다. 즉 char **를 void **로 형변환하려하는 건데 C++에서 임의의 포인터간의 형변환은 명시적으로만 가능합니다. 암묵적으로 가능한건 void*로의 형변환 뿐입니다. 다시 말씀드리지만 템플릿을 쓰세요.

익명 사용자의 이미지

3단계 암묵적 형변환 규칙같은건 없습니다. C언어가 지원하는 것은, 어떤 char * 형 타입이 void * 가 들어가야 할 수식 위치에 들어가면 void * 형으로 자동 형변환 해주는 것 까지입니다.

출발지는 char * 에 목적지를 void *&로 설정해 놓으면, 중간에 잘 알아서 void * 를 거쳐서 갈 거라고 생각하는건 과도한 기대입니다.

somoe232의 이미지

가장 이상적인 게 템플릿이라고 스택~ 에도 나와있고 단지 전 이런 3단계 변환이 자연스럽다고 생각했는데 안 되는 이유가 궁금했거든요.
근데 이해하는데 좀 시간이 걸릴 것 같네요. 나중에 좀 더 능숙해지면 표준이라도 공부해야할까 봐요 ㅠㅠ

char* cp;
void* vp = cp;
void*& rvp = vp; // 최종적으로 변환이 불가능하진 않음.
// void*& rvp = cp; // 이러고 싶지만 에러.
void* const& crvp = cp; // 이건 되긴 되는데 왜 되는 건지. Swap용으로도 못 씀.

게다가 공부가 부족한 탓인지 또 헷갈리는 걸 만나버려서... 3단계 변환이 안 되는건 아닌건지.
C++어렵군요;;
klyx의 이미지

답변을 읽으세요........

somoe232의 이미지

제가 이해력이 달리는 건지, 내내 답변만 읽고 있었어요... 답변을 흘려 읽은게 아니니까 답답해도 이해해 주셨으면 해요 ㅠㅠ

char* cp;
void* vp = cp;
void*& rvp = cp; // 말인 즉슨 rvp가 void**를 넘겨받게 된다는 뜻인가요? 어쨌든 타입은 void*이라고 생각하는데...
void* const& crvp = cp; // 암묵적 형변환이 안 되는게 const하고 연관이 있는 건지요... 이게 되는 이유도 설명이 되나요?

레퍼런스가 포인터를 잘 감싼 거에 불과하다고 똑같이 생각했지만, 그걸로 생각해도 이게 왜 말이 안 되는지 이해가 안 되거든요... 스택오버플로우 답변중에 이게 있더라고요. 그냥 이중포인터로 스왑하라고. 제 생각이 그거예요. 이중포인터로 스왑할 수 있는 걸 왜 레퍼런스론 스왑할 수 없는 건지...
somoe232의 이미지

제가 이해력이 달리는 건지, 내내 답변만 읽고 있었어요... 답변을 흘려 읽은게 아니니까 답답해도 이해해 주셨으면 해요 ㅠㅠ

char* cp;
void* vp = cp;
void*& rvp = cp; // 말인 즉슨 rvp가 void**를 넘겨받게 된다는 뜻인가요? 어쨌든 타입은 void*이라고 생각하는데...
void* const& crvp = cp; // 암묵적 형변환이 안 되는게 const하고 연관이 있는 건지요... 이게 되는 이유도 설명이 되나요?

레퍼런스가 포인터를 잘 감싼 거에 불과하다고 똑같이 생각했지만, 그걸로 생각해도 이게 왜 말이 안 되는지 이해가 안 되거든요... 스택오버플로우 답변중에 이게 있더라고요. 그냥 이중포인터로 스왑하라고. 제 생각이 그거예요. 이중포인터로 스왑할 수 있는 걸 왜 레퍼런스론 스왑할 수 없는 건지...
me wrote:

가장 이상적인 게 템플릿이라고 스택~ 에도 나와있고 단지 전 이런 3단계 변환이 자연스럽다고 생각했는데 안 되는 이유가 궁금했거든요.

혹시 템플릿을 쓰라는 말을 안 읽었다고 하시는 거면... 읽었어요. 단지 전 안되는 이유가 궁금했을 뿐이예요.

(헛.. 뒤로가기로 내용을 고쳐썼는데 글이 중복됐군요;; 삭제할 방법이 마땅치 않네요..)

익명 사용자의 이미지

아래에도 답변을 달았지만,

void* const& crvp = cp; 

이건 암묵적 형변환이 되는 것이 아니고,
const pointer to void 형 임시객체를 만들어서
거기에 cp의 값을 대입하고, 그것을 crvp가 가리키게 됩니다.
cp의 주소값을 바꿔보고 crvp와 비교해 보면 명확하죠.

char c;
char * cp = & c;
void * const & crvp= cp;
 
std::cout << "cp  =" << (void *)cp << std::endl;
std::cout << "crvp=" << crvp << std::endl;
++cp;
std::cout << "cp  =" << (void *)cp << std::endl;    // cp는 변함
std::cout << "crvp=" << crvp << std::endl;
// crvp는 cp를 가리키지 않으므로 그 값이 변하지 않음

자세한 설명은 아래에 제가 달은 리플을 참고하세요.

쉬운 얘기는 아니니까 너무 좌절하실 필요는 없고,
이래서 제가 C++을 깊이 안파는 겁니다.

klyx의 이미지

https://kldp.org/node/140224#comment-598987
"(void*)p 는 rvalue(임시변수)입니다."

익명 사용자의 이미지

위에 다시 읽어보시면 아시겠지만 '3단계 암시적 형변환 규칙'이라고 써놨지요?
직접 각 단계를 프로그래머가 수행하면 108단계라도 가능합니다.
중요한건 암시적으로 그것이 가능하냐 이고...

char* cp;
void*& rvp = cp; // 이건 안됨
void* const& crvp = cp; // 이건 됨

제시하신 위의 문제는 형변환보다는, Reference의 Initializer와 관련된 문제인데,
관련 항목을 다 설명할 수는 없고, bind directly 될 수 없는 경우중에 특정한 경우,
T & a = (b); 일때 a와 b가 reference-compatible은 아니지만
b에서 a로의 대입은 가능하고 T가 const인 경우에는,

int i = 2;
const double & rcd = i;    // 된다
double & rd =  i;    // 안된다. 에러 발생. const가 아니므로.

위의 두번째 줄과 같이 일단 실행이 되긴 되지만
직접 가르키는 것이 아니고 임시 객체를 만들어서 그것을 가리키게 됩니다.

int i = 2;
const double & rcd = i;    // 오류는 없지만, i를 가리키는 것은 아님.
// 윗줄의 코드는 어떤 double형 임시객체를 만들어 거기에 2.0을 대입한다.
 
i = 3;    // i값의 변경
std::cout << i << std::endl;    // 3이 출력됨
std::cout << rcd << std::endl;    // 2가 출력됨
return 0

somoe232의 이미지

이건 아예 별개의 규칙이었군요..
그럼 원칙적으론 저런 형변환은 허용이 안 되는 거고, const&가 좀 예외의 규칙이었던 거네요.
그럼 두 단계를 거쳐서 암시적으로 캐스팅하는 규칙이 C++에 없어서 제가 원했던 형변환이 불가능하다고 이해하면 될까요?

klyx의 이미지

그냥 어떤 형변환이던 형변환은 무조건 안된다고 생각하세요. 정말 100%안전한 형변환이라고 생각하면 명시적으로 하시고, 암묵적 형변환은 무조건 쓰지 않는게 (C++의 철학에서는) 좋습니다.
명시적으로 하는 경우에도 어떤 형변환을 원하는 것인지, int->double같은 매우 단순한 경우와 자식->부모로의 형변환이 아닌한은 가급적이면 C++의 형변환 연산자들(static_cast, reinterpret_cast, const_cast, dynamic_cast)를 쓰는게 좋습니다.

somoe232의 이미지

C++이 타입이 골치아프다는 말이 이제 실감이가네요...
형변환을 다 알고 Swap을 만들어 쓸 바에야 템플릿이 몇 배 더 나은 거군요.
답답하셨을텐데 끝까지 이해를 도와주셔서 감사합니다 ㅎ

익명 사용자의 이미지

거듭 말하지만, 위의 문제는 형변환 문제가 아니고 레퍼런스의 초기화와 관련된 문제입니다.

somoe232의 이미지

확실히 이해했어요. 임시변수를 넘겨주는데 형변환이라고 볼 수가 없죠.
국어 잘쓰기도 어려워라 ㅎ

익명 사용자의 이미지

T & a = (b); 일때 a와 b가 reference-compatible은 아니지만
b에서 a로의 대입은 가능하고 T가 const인 경우에는,

"b를 사용한 타입 T의 초기화는 가능하고"로 고쳐주세요.
그러니까 T a(b); 가 가능한 경우를 말합니다.

댓글 달기

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