시작...

블로그 이미지
mutjin

Article Category

분류 전체보기 (148)
기록 (3)
개발새발 (8)
2010년 이전 글 (133)

Recent Post

Recent Comment

Recent Trackback

Calendar

«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Archive

My Link

  • Total
  • Today
  • Yesterday
  1. 2010.04.19
    지금까지 본 웹툰(일부만) 정리!! 1
  2. 2010.04.12
    메모리 지정 new(placement new)
  3. 2010.04.09
    parameter & argument
  4. 2010.04.02
    const 정리
  5. 2010.04.01
    Prologue...
  6. 2008.11.04
    한글 putty 0.58 1
  7. 2008.06.18
    "진중권 VS 이문열" Ver.2000
  8. 2008.03.08
    웨이크보드 소개 - I
  9. 2008.03.04
    000 싸이트 자바스크립트 유틸 메소드 모음
  10. 2008.01.22
    부록 A 라이브러리에 있는 C 언어 기능들

주인장은 웹툰 폐인인 것 같다..
아니, 폐인까진 아니고 그냥 즐겨 본다^^

이곳 저곳에서 많이 본거 같아서 그동안 본걸 정리해보려고 한다..
근데 생각해보니 뭐, 별로 많이 안 봤네ㅋㅋ

정리 순서는 생각나는데로 T 고;; 완결된 작품만 언급 했다. 지금 보고 있는 연재작들은 완결 되면 업데이트 할 거다.

그리고 와탕카나 트라우마 같은 단편들은 제외하고, 스토리가 있는 작품들만 적었다.
그러다 보니 다음 만화속세상에 연재된 작품만 봤구나...-_-;

만화 제목을 클릭하면 해당 페이지가 뜰꺼다~


  • 강풀 - 순정만화
    • 웹툰을 아시는 분이라면 강풀은 다 아실테고, 강풀을 아신다면 강풀의 첫 번째 연재작인 순정만화를 아실거다. 2003년 작품인데.. 아마도 이때 쯤 본거 같다. 그림체는 보통이하였지만 스토리 구성은 좋구나 라고 생각한 작품이었다.
    • 이연희 주연의 영화로도 만들어진 인기작이므로 필히 봐야할 웹툰에 속한다.
  • 강풀 - 아파트
    • 역시 강풀의 미스테리심리썰렁물 1탄!! 어두운 방안에서 혼자 본다면 좀더 오싹하지 않을까 싶기도한.. 그러나 무서운 것 보다는 슬프게 끝난 만화다. 역시 고소영 주연의 영화로도 제작되었으나 망했다지?
  • 강풀 - 바보
    • 계속 강풀이네.. 순정만화 시리즈 2탄이라 그런 것 같다. 동네 바보의 이야기... 이 작품은 영화를 먼저 보고 봤다.. 이게 나왔을 때는 슬픈 만화는 지양하고 있었거든ㅋㅋ 영화가 의외로 감동적이라서 후에 원작을 찾아 봤다. 역시 슬픈 내용.. 감동적이었던걸로 기억한다.
  • 캐러멜 - 남아돌아
    • 당시로는 귀여운 그림체로 잘 그렸네.. 라고 평가했던거 같다. 지금 다시 봐도 나쁜 편은 아니지만 요즘 작가들은 만화를 너무 잘 그리셔서..^^ 내용은 재대백수의 사랑얘기? 주인공이 만화가로 나오는걸 봐서는 약간 작가의 얘기 인 것 같기도 하다.. 가끔 실사 컷도 있는걸 봐선 백프로다ㅋㅋ
      내용은 잘 기억나진 않지만, 작가의 개그 센스가 뛰어났던 걸로 기억한다.. 서랍의 비밀공간 같은건 언젠가 써먹어야지 하고.. 아직도 기억하고 있다 -_-b 친절하게 찾아서 링크ㅋㅋ
  • 강풀 - 타이밍
    • 미스테리심리썰렁물 2탄!! 1탄인 아파트와도 연관된 인물이 등장하지만, 시간을 컨트롤 할 수 있는 능력자들이 나오는 판타지 만화이다. 치밀한 내용과 마지막 반전까지 있는 좋은 작품이다. 이건 왜 영화로 안 나오는거지...
  • 풍경(팀) - 에스탄시아
    • 연재된지는 오래되었지만, 비교적 최근에 본 작품이다. 09년 10월쯤에 봤던가? 에스탄시아라는 지역을 탈출하려는 자들과 그들을 막으려는 자들의 이야기라고 해도 되려나.. 에스탄시아2도 있고, 블러드 오션도 있고, 이 작품과 연계된 작품들이 연재되었고 연재 되고 있다. 세계관이 좀 어려워서 대충 본지라 잘 기억나지 않는다.
  • 이림 - 죽는남자
    • 어느날.. 병으로 사망선고를 받는 남자의 이야기... 약간은 영화 아는여자와 비슷한 컨셉이지만, 내용은 완전 다르다. 주인공이 너무 정의롭게 미화 된건 아닌가 싶을 정도로 착한남자로 변한다. 감동적인 내용과 여운이 남는 결말이 기억나는 작품이다.
  • 강풀 - 26년
    • 5.18 광주 민주항쟁을 다룬 만화다. 현재(2006년)와 26년전의 두 시대가 나오면서 과거를 회상하는 사람들의 이야기였던거 같다
      ( 잘 기억안나므로 확인 후에 수정하겠음...)
  • 고영훈 - 도깨비
    • 트레이스의 작가의 첫 연재작이다. 제목보고 보긴 본건데.. 하고 생각했다.. 다시 간략하게 훑어보니까 본건 맞는데 머릿속에 내용이 없다. 내용은 뭐라 설명할 수가 없다.. 기억나지 않으니깐( 이런 저질 기억력 때문에 이 글을 쓰고 있는 것이다ㅠㅠ)...
  • 풍경(팀) - 블러드 오션
    • 글을 쓰고 있는 현 시점(2010년 4월)에 중반 부터 보려고 하는 작품이다;; 작년(2009년)말에 보다가 너무 길기도 하고 어려운 내용이라서 완결이 된 작품임에도 불구하고 보다가 그만 뒀다-_-;
      이번달에 다시 시도 해보려고 한다!
  • 제피가루 - 브이
    • 지구(한국)를 지키는 로보트 태권브이가 2007년에 부활(?) 했다! 김훈이 김과장아저씨가 되고 영희는 아줌마가 되고 철이는 과거의 꿈을 잊지 못하고 다시 태권브이를 만들었다. 뭐 이런 내용이다.
  • 고영훈 - 트레이스
    • 어느날 갑자기 생긴 능력자 돌연변이(트레이스)들에 대한 이야기.. 5가지의 에피소드로 구성된 대작이다. 거지 아저씨와 모노리아진 같은 멋진 케릭터들과 인간이지만 트레이스를 사랑해서 트레이스가 되려고 하는 아이.. 이런 내용들이 있다;;;
      이후 작품들(술,장마)도 기대가 되고 보고 싶긴 하지만 시간이 없어서 못 보고 있다.
  • 강풀 - 그대를 사랑합니다
    • 강풀의 순정만화 시리즈 3탄.. 이번엔 할아버니 할머니에 대한 내용이다. 역시 슬프고 감동적인 내용이다. 만화로는 이런 내용 지양하고 있기 때문에 망설이다가 완결 된 후에 봤다.
  • 이림 - 봄,가을
    • 죽는남자를 본 이후에 바로 보려고 했었는데 1화만 보고 슬픈 내용일 것 같아서 미뤘던 작품... 다행히(?) 슬프기만 한 내용은 아니었다. 미래를 보는 미스테리한 가을과 그에게 호감을 느낀 봄의 만남.. 결말은 아직도 잘 이해되지 않고 있지만... 봄이 가을을 사랑한다면... 봄에도 단풍이 들까? 하는 호기심에 탄생한 만화라고 한다(맞나?;;)... 왠지 아름다운 생각이다..
  • 장이 - 퍼펙트게임
    • 사회인 야구에 대한 이야기... 천하무적야구단의 탄생 계기가 되지 않았을까 하는 생각이 들었던.. 매우 유쾌하고 재밌는 내용이다. 완결 되지 않고 중단되서 현재(2010년 4월) 시즌2가 연재중이다.
  • 주니쿵 - 염라공주 모모레 시즌1~3
    • 이 글을 쓰고 있는 시점에 가장 최근에 보고 있는 완결된 작품이다. 내용은염라대왕의 딸인 모모레와 그 똘마니격인 저승사자 마귀도가 나온다. 모모레가 가출해서 한국에서 살면서 생기는 일들(시즌1)과 심심하던 차에 사건이 터지고 그 사건을 해결하는 일(시즌2)들이다. 간간히(아니, 자주) 개그가 터지는 유쾌한 작품이다. 현 시점에 시즌3을 보려고 하고 있다. 내용이 너무 긴게 단점아닌 단점이다ㅋ
  • hun - 항해
    • 활쏘는 여자아이와 힘쎈 남자아이가 한국을 구하는 내용이었나(?) 암튼, 일본의 음흉한 계략에 맞써서 뭔가 하려고 하는 내용이다. 뭘 하려는 지는 보여주지도 않고 시즌이 끝났다. 이어지는 내용은 아마도(아직 안봐서 모름) 지옥에서 웃어라에 나올꺼다. 이달안에 봐야지;;
  • 강풀 - 이웃사람
    • 미스테리심리썰렁물 3탄. 1탄과 2탄의 인물이 나오지는 않고, 왠지 요즘 세상의 실태(이웃에 대한 무관심)을 지적하기 위한 작품인 것 같다.
  • hun - 데자뷰
    • 천사도 나오고 사신도 나오고... 윤회와 인연에 대한 내용이라고 기억한다(아닐수도...).. 무슨 짓을 해도 인연의 끈은 끊을 수 없다가 결론이었던가-_-; 역시 저질 기억력이다ㅠㅠ
  • 와룡은자 - 해골택시
    • 보긴 봤는데.. 끝까지 안 본거 같다.. 자살한 자들을 처리하는 저승의 택시가 있고 그 기사가 된 자살자에 대한 내용이었던가... 그저 그런 내용.
  • 윤태호 - 이끼
    • 엄청난 몰입도를 갖는 엄청난 작품이다. 겉보기에 평화로워 보이는 작은 마을과 그 마을에 이상함을 느낀 한 방문자(?)가 머물게 되면서 밝혀지는 과거와 추악함에 대한 내용이었나;; 암튼 이런 시나리오는 영화로도 다시 나오기 힘들것이다. 실제로 이 작품이 영화화 된다고 하던데.. 언제 나올진 모르겠지만 영화로 이 내용과 표현을 살리기는 쉽지 않을 것이다. 영화 개봉전에 다시 한번 보고 싶은 작품!!
  • 제피가루 - 방벽동
    • 본격 농촌 로봇 SF 만화? (태권)브이의 작가의 차기 작품이다. 로못 이름이 농촌과 관련있는 것들이고.. 농기구가 무기고 허수아비가 미사일 발사한다... 식량문제와 그로 인해 생겨날 법한 다국적, 다행성적 식량 기업의 음모(?)에 관한 내용이다.
      다루기 어려운 내용(에스탄시아,블러드오션 처럼)을 쉽고 유쾌하게 풀었다.
  • hun -
    • 샴 쌍둥이가 사고로 인해 지킬,하이드 처럼 되었다는 내용. 내가 hun 작가를 알게된 첫 작품이다.
  • 이림 - R에 관해서
    • 4명의 여자들에 관한 내용. 아마도 다음 만화속 세상에 첫 리플을 단 작품이다. 보는동안 작가가 여자들을 잘 아는것 같다고 생각이 들었다.
  • hun - 향연상자
    • 학교폭력에 대한 내용인가? 일명 왕따, 빵셔틀과 그를 괴롭히던 놈들 혹은 방관하던 놈들에게 용서를 구할 기회를 준다. 하지만, 썩어버린 놈들에게 자비란 사치일 뿐...
  • 장이 - 미확인비행물체
    • 대박!! 그저 그런 외계인 따위의 내용이려니 생각하고 봤는데, 내가 좋아하는 사회 비판적인 내용이면서 유머가 듬뿍 담긴 훌륭한 작품이었다. 나와 상관 없는 일들이 나와 상관 있어질 때 인간은 비로소 깨닫게 된다... 언론과 정부가 하는 짓꺼리가 나와는 상관 없어보였지만 요즘엔 그렇지 않다고 느끼는 경우가 많았는데, 이 만화를 보고는 핵심을 찔렸다ㅠㅠ
  • 심승현 - 파페포포
    • 책으로 이전 시리즈가 있어서, 보게된 작품... 다른 시리즈들과 그닥 다르다고 생각되진 않았던거 같다.
  • 홍경원 - 무차별! 강팀장
    • 광고 회사의 유능한 팀장과 청각장애를 가진 사원의 이야기. 드라마로 나와도 재밌을 것 같은 내용이다. 사회생활은 혼자 잘나서 되는건 아니다!! 라는 교훈(?)을 얻었다.
  • 강풀 - 어게인
    • 미심썰 4탄. 시간 지배자들과 추가적으로 능력자들이 더 나온다. 강풀 작품들의 특징은 그다지 길지 않은 연재 기간이지만 많은 내용들이 담겨 있다는 거랄까? 기존 인물과, 새로 등장하는 인물들을 잘 설명하면서 내용도 잘 진행시킨다. 내용은 미심썰 2탄과 이어지고 어게인이라는 존재들이 등장하는데 이들을 막는 내용이다.
  • hun - 그래피티
    • 벽에 락커로 그림을 그리거나 글을 쓰는 아트. 그래피티에 대한 만화이다. 이 만화를 본 후론 지나가다 그래피티를 보면 왠지 반갑다ㅋㅋ
  • 임강혁 - 수퍼우먼
    • 하늘을 날고 힘도 쎄고 총 맞아도 총알이 안 박히는.. 신상구두 중독자인 여자에 대한 내용이다. 이런 영웅들이 존재한다고 범죄가 사라질까? 하는 생각을 하게 됬다.
  • 랑또 - 악연 (악당의사연)
    • 지구 정복을 꿈꾸는 악당이 아닌 왜 있는지도 모를 악당기업 홍어단과 그들에게 대항하는 러브레인저 ㅋㅋ 심하게 허무맹랑하고 어이없는 내용이지만, 그냥 코믹하므로 시간 때우기로 볼만 했다.
  • 윤태호 - 야후
    • 대박!! 실제를 배경으로한 픽션 만화일까나.. 대한민국 근대의 여러 실제로 일어난 사건들이 나오지만 조금은 다르게 풀어나간..하지만 절대로 가볍지는 않은 훌륭한 작품이다. 진작 알아서 일찍 봤다면 그저 역사로 치부해 버렸을지도 모를 내용이지만 지금의 나는 이런 지식에 민감해졌다. 누구 덕분에... 이거 고마워 해야하는건지.. 암튼, 민감한 내용인 사회적이고 정치적인 일들을 잘 표현했다.
       



우와.. 많이도 봤구나..
정리하는데 5시간이 넘게 걸렸다.. 아직 정리가 끝난 것도 아닌데ㅠㅠ
and

공유 메모리나 메모리 맵 I/O를 사용하는 어플을 만들 때 유용한 방식이다.

char *myBuffer = new char[1024];
myClass *pMyClass = new (myBuffer) myClass( 10 );


대신, 객체를 삭제할 때에 소멸자를 직접 호출 해야 한다.

pMyClass->~myClass();  // 파괴자 직접 호출
delete[] myBuffer;           


참조자료 : MEC++ 83 page, http://fromdj.egloos.com/3545830

'개발새발 > 기초' 카테고리의 다른 글

postgreSQL 에서 oracle의 rowid 같은 값 이용하기  (0) 2011.02.18
참조카운팅과 String  (0) 2010.04.26
parameter & argument  (0) 2010.04.09
const 정리  (0) 2010.04.02
Prologue...  (0) 2010.04.01
and

parameter : 매개변수, 함수를 선언 할 때 괄호 안에 명시하는 데이터 변수
argument : 인자, 함수를 호출할 때 넘기는 실제 데이터 값

명칭이 혼용되서 사용되는 경우가 있는데, 명확한 구분이 필요하다고 한다.

'개발새발 > 기초' 카테고리의 다른 글

postgreSQL 에서 oracle의 rowid 같은 값 이용하기  (0) 2011.02.18
참조카운팅과 String  (0) 2010.04.26
메모리 지정 new(placement new)  (0) 2010.04.12
const 정리  (0) 2010.04.02
Prologue...  (0) 2010.04.01
and


코딩 하다보면 const 를 사용해야 하는 경우가 있다.
그러나 정확하게 기억나지 않아서 좀 헷갈리고, 결국 안 쓰고 넘어가게 된 적이 몇 번 있다ㅠㅠ
이런 저질 기억력...

첫번째 컨닝 페이퍼다.


constant 의 약자이다.



C 에서의 const는 전처리 단계에서 상수라기 보단 읽기 전용 변수로 취급하기 때문에 define 과 같은 의미로 사용할 수 없지만 C++ 에서는 완전한 상수로 취급하므로 define을 대체해서 사용할 수 있다.
즉, 배열의 열 갯수로 사용 할 수 있다는 얘기다.


1. 일반 변수에서의 const

const int val = 10;
int const val2 = 20;

위의 코드는 동일한 동작을 한다.


2. 포인터 변수에서의 const

char aBuf[10];
char bBuf[10];
const char * ptrBuf1 = aBuf;   // char const * ptrBuf1 = aBuf; 와 같다
char * const ptrBuf2 = aBuf;

*ptrBuf1 = 'a'; // 에러
ptrBuf1 = bBuf; // 성공

*ptrBuf2 = 'a'; // 성공
ptrBuf2 = bBuf; // 에러

*(포인터 기호?) 을 기준으로 ptrBuf1은 포인터 변수이므로 포인터 주소 값을 변경할 수 있지만, 데이터 형이 const형이므로 주소가 가르키는 실제 값을 바꿀 수는 없다.
ptrBuf2 역시도 * 을 기준으로 const 포인터 변수이므로 포인터 주소 값을 변경할 수는 없지만, 데이터형의 값은 바꿀 수 있다.

추가적으로 const char * const pBuf = aBuf; 처럼 주소와 주소가 가르키는 값 모두 const로 만들 수도 있다.

아...이래도 헷갈린다 ㅠㅠ

 

3. 일반 변수와 const 변수

int num = 10;
const int * const pNum = # // 성공

const int num = 10;
int * pNum = # // 에러
const로 선언된 변수의 주소는 일반 포인터 변수에 대입할 수 없다.


4. 참조와 const

int num = 10;
int const &refNum = num;
refNum = 20; // 에러
num = 20;    // 성공
const 참조 변수는 직접적으로 값을 변경 할 수 없지만, 원본을 통해서는 변경할 수 있다.

int & const refNum2 = num;
refNum2 = 20;
이 경우는 컴파일시에 참조가 무시된다.(vc2005 기준)


5. 함수와 const

const int func()  // 이건 무의미하다.
const
int * func() // 상수화된 int 포인터 주소를 리턴
int * const func() // 위와 같음
int const & func( const int * arg ) // 상수화된 참조형식으로 리턴


6. classconst

6-1. 맴버함수 뒤에 붙는 const

class
TestClass
{
public:
 TestClass() { num = 100; } // 100으로 초기화
 int func() const;
 int func(){ num = 2; return num; }
 int func2(){ num = 10; return num; }
 int func3() const { return num; }
protected:
 int num;
};

int TestClass::func() const
{
 int a = 1;
 int b = a++;
 num = b; // 에러
 return num;
}

class의 맴버 함수 정의 뒤에 const가 붙으면, 해당 함수 내부에서 해당 클래스의 맴버변수를 변경할 수 없다.


6-2. 객체에 붙는 const

const TestClass test;
int num = test.func(); // const func() 함수가 호출됨.
test.func2(); // 에러

TestClass test2;
int num2 = test2.func(); // func() 함수가 호출됨.
test2.func3(); // 성공

cout << "num : " << num << endl << "num2 : " << num2 << endl;

결과
num : 100
num2 : 2

const 객체는 const 맴버 함수만 호출 할 수 있고, 함수 오버로딩 된 경우도 const 함수가 호출된다.
const 객체는 const 함수를 호출 할 수 있다.

 
7. cast

const num = 10;
num = 20; // 에러
int * num2 = const_cast<int*>(&num);

상수형 포인터를 비 상수형 포인터로 변경할 때 사용된다.
const 는 가급적 변경하지 않는게 바람직 하다.
간혹 함수의 비 상수형 인자로 상수형 변수를 전달하여야 할 경우에 사용된다.



글의 내용이 100% 정확하다고는 못 하겠다. 참고한 책이 좀 오래된 책이기도 하고.. 생각 나는데로 작성한 것이기도 하고...

시간내서 검증좀 해보고 수정이 필요하면 수정 하고 뭐 더 추가할 만한게 있나 생각해보고 있으면 추가도 해야겠다.

p.s. 또 6시 넘었다. 퇴근!!

'개발새발 > 기초' 카테고리의 다른 글

postgreSQL 에서 oracle의 rowid 같은 값 이용하기  (0) 2011.02.18
참조카운팅과 String  (0) 2010.04.26
메모리 지정 new(placement new)  (0) 2010.04.12
parameter & argument  (0) 2010.04.09
Prologue...  (0) 2010.04.01
and


나름 개발자 인생 8년(대학4년, 회사4년).
그러나 난 기초가 부족하다.
아니, 원래 부족하진 않았다.
단지 까먹었을 뿐이다ㅠㅠ
이런 돌머리...
그래서 컨닝페이퍼를 작성하고자 한다.

완전 처음부터 시작하고 싶지만,  시간적 여유가 넘치는 건 아니니까.
좀 헷갈리는 것들, 가~~끔 쓰여서 자꾸 까먹지만 중요한 것들, 한번 쯤 공부해 봤으나 잘 기억나지 않고 있는 도움될 만한 것들... 위주로 Reference 처럼 작성해 보려고 한다.
물론, 기준은 완벽히 내맘이므로, 쓸데 없을 수도 있다ㅋㅋ

과연, 게으른 내가 몇 개의 글을 포스팅 할 수 있을지ㅋㅋ
하루에 하나씩, 20개만 쓰자!

p.s.
오예~ 6시 넘었다. 퇴근!ㅋㅋ

p.s.2
사진은 글의 내용과 무관하다.

'개발새발 > 기초' 카테고리의 다른 글

postgreSQL 에서 oracle의 rowid 같은 값 이용하기  (0) 2011.02.18
참조카운팅과 String  (0) 2010.04.26
메모리 지정 new(placement new)  (0) 2010.04.12
parameter & argument  (0) 2010.04.09
const 정리  (0) 2010.04.02
and
그냥 좀 필요해서... 올려둠


이거만 올리면 되려나 모르겠다 ㅋ

'2010년 이전 글 > computer' 카테고리의 다른 글

TCPDUMP 요약글입니다.  (0) 2007.10.30
TCPDUMP 사용법  (0) 2007.10.30
블루스크린 ErrorCode Define 값입니다.  (0) 2007.09.21
HP-UX 쉘 명령어 총정리  (0) 2007.08.22
gcc와 make 강좌  (0) 2007.05.01
and
대한민국에 진중권에게 입담으로 이길 자가 있을까?

2대1의 싸움에 당당히 승리를 쟁취한 진중권...



and

<1>웨이크보드는 어떤 운동?

<2>일단 도전하자~!!!

<3>가서 웨이크만 타나~?

<4>패션

<5>장비

<6> 이 바닥...

 

 

제가 웨이크 보드를 처음 접한건 얼추 3년전인거 같은데

겨울에 스노우보드 타다 여름에 즐길 수 있는 뭐 보드비슷한 거시기를 찾다가

알게 되었습죠...

 

 

일단 물에서 하는 웬지 럭셔리(?) 레포츠같아

감히 나같은 대한민국 50%에 해당하는 서민들이

접하긴 정말 어려운 운동이라는 인식이 있었고

이 글을 보시는 여러분들도 분명 저와 같은 생각을 지금 가지고 계시리라 생각되네요~

 

일단 저는 이 웨이크보드란  녀석을 입벌어지게 타는 수준도 아니고

그냥 주말에 머리식힐 겸 몸 사려가며 타는 평범한 사람입니다.

 

웨이크보드의 기술적인 부분이나 전문적인 용어를 설명드리고자 함이 아니라

혹시나 도전은 하고 싶은데 어떻게 시작을 해야할 지 모르시는 분들을 위해

조금이나마 도움이 될까 싶어 제가 아는 선에서 알려드리고자 함입니다.

 

레포츠에 관심있는 분이라도 웨이크보드라는 레포츠가 생소하게 들릴 수도 있습니다.

예전보단 즐기시는 분들이 많이 계시긴 하지만 전체로 본다면 아주 적은 인구죠.

 

설명하기가 날로 귀찮아져서 그냥 아는 지인들한테는 수상스키탄다고 그럽니다만 웨이크보드나 수상스키 모두 물에서 즐긴다 해도 웨이크보드가 추구하는 것은 수상스키와 아주 많이 다릅니다.

 

사용자 삽입 이미지

 

위의 사진은 수상스키 예를 든겁니다. 두개의 스키를 이용한다 해서 투스키라고 보통 부르고

한 개의 스키에 두발을 모두 올려놓고 타는것을 원스키라 그럽니다.

 

 

아래 사진은 웨이크보드를 타는 외국 프로의 사진입니다.

 

입 쩍 벌어지시죠?

 

어떻게 물위에서 가다가 저런 높이까지 올라갈 수 있는지...

실로 놀라운 세계가 아닐 수 없습니다.

 

사용자 삽입 이미지

 

위의 사진에서 보듯이 웨이크보드는 공중기술을 위한 운동이라 해도 과언이 아닐 정도입니다.

수상스키가 물위에서의 고속의 라이딩을 추구한다면 웨이크보드는 궁극적으로 공중트릭을 추구하는 것이죠.

 

아...

제가 한가지 빼먹은게 있네요.

 

왜 웨이크보드란 이름이 붙었을까?

 

영어로 써보면 'wakeboard" 입니다.

 

눈에서 타면 'snowboard' 그런데 'wake'는 뭐냐?

 

가까운 네이버사전 찾아보시면 

 

wake2

1배가 지나간 자리, 항적(航跡)
2(물건의) 지나간 자국, 흔적

 

랍니다.

 

 

배가 일정한 속도로 나아가게 되면 배가 지나간 자리로  △ 모양의 물모양이 만들어집니다.

 

이것을 웨이크 'wake' 라고 하는데요.

 

 

사용자 삽입 이미지
 
 
저 웨이크를 옆에서 보면
 
 
 

사용자 삽입 이미지
 
웨이크를 옆에서 보면 위 사진에서 보시는 바와 같이
높이차가 있습니다.
 
저걸 타고 올라가서 공중기술을 하게 되는 것이죠.
수면에서의 공중도약에 대해 조금 더 복잡하게 설명드릴게 있습니다만
나중으로 미루겠습니다.  
 
 
일단 프로들동영상 보시면서
 두근두근 하세요~
 
타고 싶으시죠?
 
 
 

 

(사용된 예나 그림,동영상 그외 저의 글쓰는 방법까지... 세련된 문체나 감각적이 유머는 기대하지 마세요~넘 머라고도 하지 말아주시구요. 전공책외엔 읽은 책이 손가락으로 셀 정도랍니다...--;)

가면 이런거 실컷 탈 수있으려나... 근데 가기전에 타보고싶다

and

/*************************************************************
최종수정일:

일반함수

btMov_AddList
checkNumber

일반함수

BT_getComboData 셀렉트 생성
BT_CheckLastAcctno 계좌번호 끝에 3자리 000 제거
BT_makeAcntnoDash 계좌번호에 하이픈을 넣음
BT_MakeAcntList 계좌번호를 리스트로 만듬
BT_MakePayAcntList 계좌번호를 리스트로 만듬
BT_ReduceDash 하이픈을 삭제
BT_makeCardnoDash 카드번호 하이픈 넣기
BT_getToday 오늘 날짜열을 YYYYMMDD의 형식으로 만듬
BT_make2Length 월일을 두자리로 만듬

BT_makeCardnoDash 카드번호 하이픈 넣음
BT_makeMembernoDash 회원번호(16자리) 하이픈 넣기

BT_TrimSpace 공백없애기

BT_calc 금액 더하기 계산
BT_Del_comma 콤마 삭제
BT_Add_comma 입력폼 작성시 콤마 추가
BTCom_Replace 문자변환

BT_Add_Displaycomma 금액 Display 시 콤마 추가
BT_Add_NumberComma 금액의 앞자리 0을 없애고 콤마추가
BT_Add_NumberComma1
BT_toMoneyF 환율,외화에 소수점,콤마 추가

BT_Reduce_comma 콤마 제거

BT_isAlphabet 값이 알파벳만 있는지 체크
BT_isNumber 값이 숫자만 있는지 체크
BT_number 숫자만 입력
BT_isTrsfPass 이체 비밀번호 체크 루틴

BT_getRandomNumber 씨크릿 카드 번호 생성

BT_makeCustnoDash 9자리 고객번호 하이픈을 넣음

BT_makeResnoDash 주민번호 하이픈 넣기

BT_makeLoannoDash 대출계좌에 하이픈 넣기

BT_makeDateDash yyyy-mm-dd
BT_makeDateDash_1 yyyy-mm
BT_makeDateDash_2 mm-dd
BT_makeDateDash_3 mm
BT_makeTime 00:00:00

BT_StrToInt 문자열을 10진수로 변환

BT_makeDecimalPoint 소수점이 포함된 숫자 앞에 포함된 0 을 제거

last_page 페이지 설정

BT_EngNum 숫자와영어만 허용

BT_SetServiceCode ServiceCode박아주기
BT_Setreturn_url return_url 박아주기
BT_getRandomNumber() { //씨크릿 카드 순차번호 생성

pwCheck() 비밀번호 연번 확인 => 주석확인

***************************************************************/
var alphaArray = new Array();
alphaArray[0]  = 'a' ;alphaArray[1] = 'b';alphaArray[2] = 'c';alphaArray[3] = 'd';
alphaArray[4]  = 'e' ;alphaArray[5] = 'f';alphaArray[6] = 'g';alphaArray[7] = 'h';
alphaArray[8]  = 'i' ;alphaArray[9] = 'j';alphaArray[10] = 'k';alphaArray[11] = 'l';
alphaArray[12] = 'm';alphaArray[13] = 'n';alphaArray[14] = 'o';alphaArray[15] = 'p';
alphaArray[16] = 'q';alphaArray[17] = 'r';alphaArray[18] = 's';alphaArray[19] = 't';
alphaArray[20] = 'u';alphaArray[21] = 'v';alphaArray[22] = 'w';alphaArray[23] = 'x';
alphaArray[24] = 'y';alphaArray[25] = 'z';
alphaArray[26] = 'A';alphaArray[27] = 'B';alphaArray[28] = 'C';alphaArray[29] = 'D';
alphaArray[30] = 'E';alphaArray[31] = 'F';alphaArray[32] = 'G';alphaArray[33] = 'H';
alphaArray[34] = 'I';alphaArray[35] = 'J';alphaArray[36] = 'K';alphaArray[37] = 'L';
alphaArray[38] = 'M';alphaArray[39] = 'N';alphaArray[40] = 'O';alphaArray[41] = 'P';
alphaArray[42] = 'Q';alphaArray[43] = 'R';alphaArray[44] = 'S';alphaArray[45] = 'T';
alphaArray[46] = 'U';alphaArray[47] = 'V';alphaArray[48] = 'W';alphaArray[49] = 'X';
alphaArray[50] = 'Y';alphaArray[51] = 'Z';

function getCode(argObj1,argObj2){
 Key1 = argObj1;
 Key2 = argObj2;
 if(top.iWallet.LoadCode_state == false){
  top.iWallet.LoadCode = top.iWallet.LoadCode + 1;
  if(top.iWallet.LoadCode <= 5){
   setTimeout("getCode(Key1,Key2)", 1000)
  }else{
   alert("코드 초기화에 실패하였습니다.\n처음으로 돌아갑니다.")
   SID = get_sessionid();
   CryptoClient.Logout(SID); 
   top.location.href="/btindex.asp";
  }
 }else{
  retString = ""
  try {
   sKey = argObj1 + "^" + argObj2;
   retString = top.iWallet.sTradcode[sKey];
   if (!retString) retString = "";
   return retString;
  }catch(e) {
   return "";
  } 
 }
}

//OPEN창에서 사용
function getCode2(argObj1,argObj2){
 Key1 = argObj1;
 Key2 = argObj2;
 if(opener.top.iWallet.LoadCode_state == false){
  opener.top.iWallet.LoadCode = opener.top.iWallet.LoadCode + 1;
  if(opener.top.iWallet.LoadCode <= 5){
   setTimeout("getCode2(Key1,Key2)", 1000)
  }else{
   alert("코드 초기화에 실패하였습니다.\n처음으로 돌아갑니다.")
   SID = get_sessionid();
   CryptoClient.Logout(SID); 
   top.location.href="/btindex.asp";
  }
 }else{
  retString = ""
  try {
   sKey = argObj1 + "^" + argObj2;
   retString = opener.top.iWallet.sTradcode[sKey];
   if (!retString) retString = "";
   return retString;
  }catch(e) {
   return "";
  } 
 }
}

function BT_LoadCode(){
 var dgbcode=ttradcode();
 tmp1 = dgbcode.split("^^^");
 for(k=0;k<tmp1.length;k++){
  tmp2 = tmp1[k].split("^^");
  top.iWallet.sTradcode[tmp2[0]]=tmp2[1];
 }
}

//코드구분, 셀렉트이름, selected값
function BT_getComboData(argObj1,selname,argObj2){
 Key1 = argObj1;
 Key2 = selname;
 Key3 = argObj2;

 if(top.iWallet.LoadCode_state == false){
  top.iWallet.LoadCode = top.iWallet.LoadCode + 1;
  if(top.iWallet.LoadCode <= 5){
   setTimeout("BT_getComboData(Key1,Key2,Key3)", 1000)
  }else{
   alert("코드 초기화에 실패하였습니다.\n처음으로 돌아갑니다.")
   SID = get_sessionid();
   CryptoClient.Logout(SID); 
   top.location.href="/btindex.asp";
  }
 }else{
  try {
    var data=top.iWallet.ttradcode();
    var split_data=data.split("^^^");
    var len = split_data.length;
    var L_ListText = "<select name=\"" + selname + "\" id=\"" + selname + "\">";
    

    for(i=0;i<len;i++) {
     var splitdata_in=split_data[i].split("^^");
     var splitdata_in2=splitdata_in[0].split("^");

     if(splitdata_in2[0] == argObj1)
     {
      if (splitdata_in2[1] == argObj2) {
       L_ListText += "\t<option value=\"" + splitdata_in2[1] + "\" selected>" + splitdata_in[1] + "</option>\n";
      } else {
      L_ListText += "\t<option value=\"" + splitdata_in2[1] + "\">" + splitdata_in[1] + "</option>\n";
      }
     }
     
    }

    L_ListText = L_ListText + "</select>"
    return L_ListText;
   }catch(e) {
   return false;
   }  
 }
}

function btAlertNoValue ( thisvalue , alertvalue )
{
 if ( thisvalue.value == "" )
 {
  var smsg;
  smsg = alertvalue + "을(를) 입력하셔야 합니다.";
  alert(smsg);
  thisvalue.focus();
  return 1;
 }

 return 0;
}


function btMov_AddList(argObj, argData, argValue,selval){
 
 argObj.length = argObj.length + 1 
 argObj.options(argObj.length-1).text = argData
 argObj.options(argObj.length-1).value = argValue
 if(selval != ""  && selval == argValue) argObj.options(argObj.length-1).selected = true;

}

//숫자외의 문자 check
function checkNumber(data) {
 t = data.value ;
 for(i=0;i<t.length;i++)
  if (t.charAt(i)<'0' || t.charAt(i)>'9') {
  if (t.charAt(i) != '*') {
   alert("숫자만 입력해주세요.") ;
   data.value="";
   data.focus() ;
   return false ;
  }
 }
}


function btTextCheckDate(Dnum,StaYer,StaMon,StaDay,EndYer,EndMon,EndDay)
{

     now = new Date();
     var cur_year= now.getYear();
  var cur_month =  now.getMonth();
  var month =  now.getMonth()+1;
  month=new String(month);
  var cur_day = now.getDate();
  cur_day=new String(cur_day);
     var d_cur_date = new Date(cur_year,cur_month,cur_day);
  var dategubun;
     
  var num = Dnum;
 
        eval("doc."+StaYer+".readOnly = true");
        eval("doc."+StaMon+".readOnly = true");
        eval("doc."+StaDay+".readOnly = true");
        eval("doc."+EndYer+".readOnly = true");
        eval("doc."+EndMon+".readOnly = true");
        eval("doc."+EndDay+".readOnly = true");
  if(num==0){ //당일
   var before_date = new Date(Date.parse(d_cur_date) - 1*1000*60*60*24);
   dategubun = "1주일전";
  }else if(num==1){ //1주일전
   var before_date = new Date(Date.parse(d_cur_date) - 7*1000*60*60*24);
   dategubun = "1주일전";
  }else if(num==2){ //15일전
   var before_date = new Date(Date.parse(d_cur_date) - 15*1000*60*60*24);
   dategubun = "15일전";
  } else if(num==3){ //30일
   var before_date = new Date(Date.parse(d_cur_date) - 30*1000*60*60*24);
   dategubun = "30일";
  } else if(num==4){ //기간설정(3개월) 
   var before_date = new Date(Date.parse(d_cur_date) - 91*1000*60*60*24);
          eval("doc."+StaYer+".readOnly = false");
          eval("doc."+StaMon+".readOnly = false");
          eval("doc."+StaDay+".readOnly = false");
          eval("doc."+EndYer+".readOnly = false");
          eval("doc."+EndMon+".readOnly = false");
          eval("doc."+EndDay+".readOnly = false");  
   dategubun = "기간설정";
  }
 
  var yest_date = new Date(Date.parse(d_cur_date)-1000*60*60*24);
  var yest_year = new String(yest_date.getYear());
  var yest_month = new String(yest_date.getMonth()+1);
  
  
  if(yest_month.length==1){
   yest_month = '0'+yest_month;
  }
     
  if(month.length==1){
   month = '0'+ month;
  }
     
     
  if(cur_day.length==1){
   cur_day = '0'+cur_day;
  }
     
  var before_year = new String(before_date.getYear());
  var before_month = new String(before_date.getMonth() +1);
  if(before_month.length==1){
   before_month = '0'+before_month;
  }
  var before_day = new String(before_date.getDate());
  if(before_day.length==1){
   before_day = '0'+before_day;
  }

    if(num==0){
          eval("doc."+StaYer+".value=cur_year");
          eval("doc."+StaMon+".value=month");
          eval("doc."+StaDay+".value=cur_day");
          eval("doc."+EndYer+".value=cur_year");
          eval("doc."+EndMon+".value=month");
          eval("doc."+EndDay+".value=cur_day");
         }
         else if(num==4){
        eval("doc."+StaYer+".value=''");
          eval("doc."+StaMon+".value=''");
          eval("doc."+StaDay+".value=''");
          eval("doc."+EndYer+".value=''");
          eval("doc."+EndMon+".value=''");
          eval("doc."+EndDay+".value=''");                
  } 
         else
     {
      eval("doc."+StaYer+".value=before_year");
        eval("doc."+StaMon+".value=before_month");
        eval("doc."+StaDay+".value=before_day");
        eval("doc."+EndYer+".value=cur_year");
        eval("doc."+EndMon+".value=month");
        eval("doc."+EndDay+".value=cur_day");
     }
}


//*--  필드 AutoSkip  --*//
function btAutoSkip(){

 var  el = event.srcElement
 if ((el.value == null) || (event.keyCode==13)) return(false)
  
 // 방향키 무시
 var sKeys = "8;16;46;;37;38;39;40;33;34;35;36;45;229;"
 if (sKeys.indexOf(event.keyCode+";") > -1) return;

 if  (el.tagName == "INPUT" )
  if  (el.value.length  >=  el.maxLength ) {
   var i=0
   while (el != el.form.elements[i] )  i++
   if ( (i+1) != el.form.elements.length ) {
    while ( ( el.form.elements[i+1].type == "hidden" ) ||
     ( el.form.elements[i+1].type == "button" ) ||
     ( el.form.elements[i+1].type == "checkbox" ) ||
     ( el.form.elements[i+1].type == "radio" ) ||
     ( el.form.elements[i+1].tagName == "FIELDSET" ) ||
     ( el.form.elements[i+1].style.display == "none" )  ||
     ( el.form.elements[i+1].disabled == true ) ||
     ( el.form.elements[i+1].style.visibility == "hidden" )) {
    i++
    if ( (i+1) == el.form.elements.length ) return;
   }
   }
   else{
    el.form.elements[i].focus();
    return(false);
   }
       
   el.form.elements[i+1].focus()
   if(el.form.elements[i+1].tagName != "SELECT") el.form.elements[i+1].select()
   else if(el.form.elements[i+1].tagName == "SELECT") {
    if(el.form.elements[i+1].value==null || el.form.elements[i+1].selectedIndex==-1)
                el.form.elements[i+1].value = "";
   }
  }
}

//계좌번호에 하이픈 넣기 - pansory
function BT_makeAcntnoDash (val) {
 var DashedAcntno = val 
 if (val.length == 11) { //계좌번호가 11자리일 경우
  DashedAcntno = val.substring(0,3) + "-" + val.substring(3,5) + "-" + val.substring(5)
 } else if (val.length == 12){
  DashedAcntno = val.substring(0,3) + "-" + val.substring(3,5) + "-" + val.substring(5,11) + "-" + val.substring(11)
 }else if(val.length == 13) { //계좌번호가 13자리일 경우
  DashedAcntno = val.substring(0,3) + "-" + val.substring(3,5) + "-" + val.substring(5,11) + "-" + val.substring(11)
 } else if (val.length == 14) { //계좌번호가 14자리일 경우
  DashedAcntno = val.substring(0,3) + "-" + val.substring(3,5) + "-" + val.substring(5,11) + "-" + val.substring(11)
 } else if (val.length == 16){
  DashedAcntno = val.substring(0,3) + "-" + val.substring(3,5) + "-" + val.substring(5,11) + "-" + val.substring(11)
 } 
 return DashedAcntno;
}

//계좌번호 끝3자리에서 000 제거
function BT_CheckLastAcctno(val){
 var TempAccntno = val;
 var len = val.length;
 var tempAcc = len - 3;
 var lastAccNo = val.substring(tempAcc, len);
 
 if (lastAccNo == "000"){  
  TempAccntno = BT_makeAcntnoDash(val.substring(0, tempAcc));
 } else {  
  TempAccntno = BT_makeAcntnoDash(TempAccntno);
 }
 return TempAccntno;
}

//계좌번호 선택..
function BT_SelectAcntno(USR_AccsList, NeedAcntno) {
 
 var no = NeedAcntno.length;
 var Acntno_No = USR_AccsList.length;
 var SelectedAcntno = new Array();
 var k = 0;

 if (no == 0) {
  SelectedAcntno = USR_AccsList;
  //나중에 확인 필요함.. 고른 것들 중에 첫번째 것을 세션에 넣기..????  
  save_session("ACNTNO", USR_AccsList[0]);
  save_flush();
  
 } else {
  for (var i=0; i < Acntno_No ; i++) {
   
   for(var j=0 ; j < no; j++) {
    
     if(USR_AccsList[i].length > 12) {
      stCode = USR_AccsList[i].substring( 9, 11);
      
     } else {
      stCode = "0"; //Case Error
     }
     
     if (stCode == NeedAcntno[j]) {
      SelectedAcntno[k] = USR_AccsList[i];
      
      
      
      if (k == "0") {
       //나중에 확인 필요함.. 고른 것들 중에 첫번째 것을 세션에 넣기..????  
       save_session("ACNTNO", USR_AccsList[i]);
       save_flush();
      }
      
      k++;
     }
   }
  }
 }
 
 return SelectedAcntno;
 
}

//계좌번호 Array를 셀렉트 박스로 변환 - pansory
//조건에 따라(0: 조회, 1: 이체가능, 2: 저축성, 4: 대출, 5: 신탁, 6: 기타)
function BT_MakeAcntList(val, selectedval,selname,condition) {
 var Acct = BT_KindAcct(val,condition);
 var tempAcct = Acct.split(",");
 
 var L_ListText = "<select name=\"" + selname + "\" id=\"" + selname + "\">";

 for(var i=0; i< tempAcct.length - 1; i++)  {
  if (tempAcct[i] == selectedval) {
   
   L_ListText += "\t<option value=\"" + tempAcct[i] + "\" selected>" + BT_makeAcntnoDash(tempAcct[i]) + "</option>\n";
  } else {
   L_ListText += "\t<option value=\"" + tempAcct[i] + "\">" + BT_makeAcntnoDash(tempAcct[i]) + "</option>\n";
  }
  
 }
 
 L_ListText += "</select>\n";

 return L_ListText;
}


//계좌종류별로계좌번호가져오기
/*[1] 11      -> /s100/bc101_r2.jsp  (1080) (계좌상세조회 결과와 같은 화면 사용)  
[2] 19 ~ 28 -> /s100/bc102_r3.jsp  (1020) (적립식예금)  
[3] 31 ~ 34 -> /s400/bc419_r1.jsp  (4190) (외화예금)  
[4] 40 ~ 49 -> /s100/bc102_r2.jsp  (1130) (대출계좌)  
[5] 50 ~ 73 -> /s100/bc102_r1.jsp  (1020) (기타)  
[6] 그 외   -> /s100/bc1090_r1.jsp (1090) (요구불예금)*/
function BT_KindAcct(Acct,kind)
{
 var tmpAcct = "";//new Array;
 var flag = new Array;

 flag = get_session("IGBGYEJI");

 for(i=0; i< Acct.length; i++){
  if(kind == ""){
   tmpAcct = tmpAcct + Acct[i] + ",";
  }else if(BT_StrToInt(kind) == BT_StrToInt(flag[i])) {
     tmpAcct = tmpAcct + Acct[i] + ",";
  }
 }
 return tmpAcct;
}

// "-" 삭제 - pansory
function BT_ReduceDash(val)
{
 var x, ch;
 var i=0;
 var newVal="";
 for(x=0; x <val.length ; x++){
  ch=val.substring(x,x+1);
  if(ch != "-")  newVal += ch;
 }
 return newVal;
}


//오늘 날짜열 만들기 - pansory
function BT_getToday() {
 var today = new Date();
 var yyyy, mm, dd;
 var Today;
 
 yyyy = today.getYear();
 mm = BT_make2Length(today.getMonth() + 1);
 dd = BT_make2Length(today.getDate());
 
 Today = yyyy.toString() + mm.toString() + dd.toString();
 
 return Today;
}


// 월일을 두자리로 만들기 - pansory
function BT_make2Length(val) {
 if(val < 10) {
  val = "0" + val;
 }
 return val;
}

//공백 없애기
function BT_TrimSpace(str) {
 var count = str.length;
 var len = count;                
 var st = 0;

 while ((st < len) && (str.charAt(st) <= ' ')) {
  st++;
 }
 while ((st < len) && (str.charAt(len - 1) <= ' ')) {
  len--;
 }                
 return ((st > 0) || (len < count)) ? str.substring(st, len) : str ;  
}

//공백 없애기
function BT_TrimSpace1(str) {
 var count = str.length;
 var len = count;                
 var st = 0;
 
 while ((st < len) && (toString(str.charAt(st)) <= toString(' '))) {
  st++;
 }
 while ((st < len) && (toString(str.charAt(len - 1)) <= toString(' '))) {
  len--;
 }       
 
 return ((st > 0) || (len < count)) ? str.substring(st, len) : str ;  
 
}

//session의 PAY_ACNTNO 와 ACNTNO(val) 값 비교
function BT_ComparePayAcntno(val) {
 
 var USR_AccsList = get_session("ACCS");
 var check = "";
 
 for (var i=0; i < USR_AccsList.length; i++) {
  if (USR_AccsList[i] == val) {
   check =  "1";
  }
 }
 return check;
}

// 금액 더하기 계산하기
function BT_calc(string,name)
{
 var sum = 0;
 BT_Del_comma(doc.form.REC_AMT);

 if (eval(string) == 0 ) {
  doc.form.REC_AMT.value = "";
 }
 else {
  sum = doc.form.REC_AMT.value;
  if (sum == "") {
   sum = 0;
  }
  sum = eval(sum) + eval(string);
  doc.form.REC_AMT.value = sum;
 } 

 doc.form.REC_AMT.focus();
}

// 금액 더하기 계산하기
function BT_Card_calc(string,name)
{
 var sum = 0;
 BT_Del_comma(doc.form.SEV_AMT);

 if (eval(string) == 0 ) {
  doc.form.SEV_AMT.value = "";
 }
 else {
  sum = doc.form.SEV_AMT.value;
  if (sum == "") {
   sum = 0;
  }
  sum = eval(sum) + eval(string);
  doc.form.SEV_AMT.value = sum;
 } 

 doc.form.SEV_AMT.focus();
}

 

//이체 비밀번호 체크
function BT_isTrsfPass(val)
{
// 이체비밀번호는 4자리이어야 한다.(자리수 검사는 따로 해야 한다.)
// 영문자가 1개이상 포함되어야 하며, 영문자와 숫자의 조합만 허락한다.
 var len = val.length;
 var i;
 var alphanum = 0;
 
 for(i=0;i<len;i++) {
  if ( BT_isAlphabet(val.charAt(i)) )
   alphanum = alphanum + 1;
  else if ( isNaN(val.charAt(i)) )
   return false;
 }
 if ( alphanum > 0 )
  return true;
 else
  return false;
}

//알파벳인지 체크 - 이체비밀번호 체크와 세트
function BT_isAlphabet(val)
{
 var alphaStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
 if ( alphaStr.indexOf(val) < 0 )
  return false;
 else
  return true;
}

//숫자만 있는지 체크
function BT_isNumber(val)
{
 var numberStr = "0123456789";
 
 if ( numberStr.indexOf(val) < 0 )
  return false;
 else
  return true;
}

function BT_number(val){

  if ((event.keyCode<48)||(event.keyCode>57 )){  
   if(event.keyCode != 8  ) // 백스페이스 제외
   {
    event.returnValue=false;
   }
  }
}


// 금액 입력시 "," 자동 입력 & 우측 정렬
function BT_Add_comma(REC_AMT)
{    

 if(!BT_isAllNumber(REC_AMT.value))
 {
  alert("금액을 숫자로 입력해 주세요.");
  REC_AMT.value = "";
  REC_AMT.focus();
  return false;
 }
 var src
     var i;
 var factor;
 var su;
 var Spacesize = 0;

 factor = REC_AMT.value.length % 3;
 su = (REC_AMT.value.length - factor) /3;
    src = REC_AMT.value.substring(0,factor);

   for(i=0; i<su ; i++)
 {
    if ((factor==0)&&(i==0))// " XXX "인 경우
  {
    src += REC_AMT.value.substring(factor+(3*i), factor+3+(3*i));
  }
  else
  {
   src +=",";
   src += REC_AMT.value.substring(factor+(3*i), factor+3+(3*i));
        }
    }
  REC_AMT.value=src;
  return true;
}

function BT_Add_NumberComma(val) {
 var Ret_val = BT_StrToInt(val);
 Ret_val = BT_Add_Displaycomma(Ret_val);
 
 return Ret_val;
}

function BT_Add_NumberComma1(val) {
 var Ret_val = BT_StrToInt(val);
 var Zerodel = Ret_val;
 if (Zerodel == "0")
  Ret_val = "";
  
 else
  Ret_val = BT_Add_Displaycomma(Ret_val);
 
 return Ret_val;
}

function BT_Add_Number(val) {
 var Ret_val = BT_StrToInt(val); 
 return Ret_val;
}
function BT_Add_Displaycomma(val) {

 var src;
    var i;
 var factor;
 var su;
 var Spacesize = 0;
 
 var String_val = val.toString();
 
 factor = String_val.length % 3;
 su = (String_val.length - factor) /3;
    src = String_val.substring(0,factor);

   for(i=0; i<su ; i++)
 {
    if ((factor==0)&&(i==0))// " XXX "인 경우
  {
    src += String_val.substring(factor+(3*i), factor+3+(3*i));
  }
  else
  {
   if ( String_val.substring(factor+(3*i) - 1, factor+(3*i)) != "-" ) src +=",";
   src += String_val.substring(factor+(3*i), factor+3+(3*i));
        }
    }
  return src;
}

function BT_Add_Card_comma(SEV_AMT)
{

 if(!BT_isAllNumber(form.SEV_AMT.value))
 {
  alert("금액을 숫자로 입력해 주세요.");
  form.SEV_AMT.value = "";
  form.SEV_AMT.focus();
  return false;
 }
 var src
     var i;
 var factor;
 var su;
 var Spacesize = 0;

 factor = SEV_AMT.value.length % 3;
 su = (SEV_AMT.value.length - factor) /3;
    src = SEV_AMT.value.substring(0,factor);

   for(i=0; i<su ; i++)
 {
    if ((factor==0)&&(i==0))// " XXX "인 경우
  {
    src += SEV_AMT.value.substring(factor+(3*i), factor+3+(3*i));
  }
  else
  {
   src +=",";
   src += SEV_AMT.value.substring(factor+(3*i), factor+3+(3*i));
        }
    }
  SEV_AMT.value=src;
  return true;
}

//전체 값이 숫자로만 되어 있는지 체크
function BT_isAllNumber(val)
{
 var len = val.length;
 var i=0;
 
 for(i=0;i<len;i++) {
  if ( isNaN(val.charAt(i)) ) return false;
 }
 return true;
}

// 컴마(",") 자동 삭제
function BT_Del_comma(REC_AMT)
{
 REC_AMT.value = BT_Reduce_comma(REC_AMT.value);
 return true;
}

// 컴마(",") 자동 삭제
function BT_Del_Card_comma(SEV_AMT)
{
 SEV_AMT.value = BT_Reduce_comma(SEV_AMT.value);
 return true;
}

function BT_Reduce_comma(account_number)
{
 var x, ch;
 var i=0;
 var newVal="";
 for(x=0; x <account_number.length ; x++){
  ch=account_number.substring(x,x+1);
  if(ch != ",")  newVal += ch;
 }
 return newVal;
}

function BTCom_Replace(originalString, findText, replaceText){

 var pos = 0
 var preStr = ""
 var postStr = ""

 pos = originalString.indexOf(findText)
 
 while (pos != -1) {
  preString = originalString.substr(0,pos)
  postString = originalString.substring(pos+findText.length)
  originalString = preString + replaceText + postString
  pos = originalString.indexOf(findText)
 }
 
 return originalString
}

function BT_getRandomNumber() { //씨크릿 카드 순차번호 생성
 k=Math.round(Math.random()*30)
 if(k == 0) k = k + 1
 var L_Number = BT_make2Length(k);
 return L_Number;
}


//9자리 고객번호에 하이픈 넣기 - pansory
function BT_makeCustnoDash (val) {
 DashedCustno = val.substring(0,3) + "-" + val.substring(3,9);
 return DashedCustno;
}

//주민번호에 하이픈 넣기
function BT_makeResnoDash (val) {
 if (val.length == 10) { //기업의 경우
  DashedResno = val.substring(0,3) + "-" + val.substring(3,5) + "-" + val.substring(5);
 } else { //개인의 경우
  if (val.substring(0,3) == "000"){
   DashedResno = val.substring(3,6) + "-" + val.substring(6,8) + "-" + val.substring(8,13);
  } else {
   DashedResno = val.substring(0,6) + "-" + val.substring(6,13);
  }
  
 }
 return DashedResno;
}


//카드번호에 하이픈 넣기 - pansory
function BT_makeCardnoDash (val) {
  DashedAcntno = val.substring(0,4) + "-" + val.substring(4,8) + "-" + val.substring(8,12) + "-" + val.substring(12,16);
 return DashedAcntno;
}

//회원번호(16자리)에 하이픈 넣기 - pansory
function BT_makeMembernoDash (val) {
  DashedAcntno = val.substring(0,4) + "-" + val.substring(4,8) + "-" + val.substring(8,12) + "-" + val.substring(12,16);
 return DashedAcntno;
}

//카드번호 Array를 셀렉트 박스로 변환 - pansory
function BT_MakeCardList(val, selectedval) {
 
 var L_ListText = "<select name=\"ACNTNO\" id=\"ACNTNO\">";

 for(var i=0; i< val.length; i++)  {
  if (val[i] == selectedval) {
   L_ListText += "\t<option value=\"" + val[i] + "\" selected>" + BT_makeCardnoDash(val[i]) + "</option>\n";
  } else {
   L_ListText += "\t<option value=\"" + val[i] + "\">" + BT_makeCardnoDash(val[i]) + "</option>\n";
  }
  
 }
 
 L_ListText += "</select>\n";

 return L_ListText;
}

//카드번호 Array를 셀렉트 박스로 변환 - pansory
function BT_makeCardnoList(val) {
 
 var L_ListText = "<select name=\"CARD_NO\" id=\"CARD_NO\">";

 for(var i=0; i< val.length; i++)  {
  if (i == 0) {
   L_ListText += "\t<option value=\"" + val[i] + "\" selected>" + BT_makeCardnoDash(val[i]) + "</option>\n";
  } else {
   L_ListText += "\t<option value=\"" + val[i] + "\">" + BT_makeCardnoDash(val[i]) + "</option>\n";
  }
  
 }
 
 L_ListText += "</select>\n";

 return L_ListText;
}

//카드번호 선택..
function BT_SelectCardno(USR_AccsList, NeedCardno) {
 
 var no = NeedCardno.length;
 var Cardno_No = USR_AccsList.length;
 var SelectedCardno = new Array();
 var k = 0;

 if (no == 0) {
  SelectedCardno = USR_AccsList;
  //나중에 확인 필요함.. 고른 것들 중에 첫번째 것을 세션에 넣기..????  
  save_session("CARD_NO", USR_AccsList[0]);
  save_flush();
  
 } else {
  for (var i=0; i < Cardno_No ; i++) {
   
   for(var j=0 ; j < no; j++) {
    
     if(USR_AccsList[i].length > 12) {
      stCode = USR_AccsList[i].substring( 9, 11);
      
     } else {
      stCode = "0"; //Case Error
     }
     
     if (stCode == NeedAcntno[j]) {
      SelectedCardno[k] = USR_AccsList[i];
      
      if (k == "0") {
       //나중에 확인 필요함.. 고른 것들 중에 첫번째 것을 세션에 넣기..????  
       save_session("CARD_NO", USR_AccsList[i]);
       save_flush();
      }
      k++;
     }
   }
  }
 }
 return SelectedCardno;
}

function BT_makeLoannoDash (val) {
  DashedLoanno = val.substring(0,3) + "-" + val.substring(3,9) + "-" + val.substring(9,11) + "-" + val.substring(11,16)
 return DashedLoanno;
}

function BT_makeDateDash (val) {
  if (val == "00000000")
   DashedDate = "";
  else
   DashedDate = val.substring(0,4) + "-" + val.substring(4,6) + "-" + val.substring(6,8);
   
 return DashedDate;
}

function BT_makeDateDash_1 (val) {
  if (val == "000000")
   DashedDate_1 = "";
  else
   DashedDate_1 = val.substring(0,4) + "-" + val.substring(4,6);
   
 return DashedDate_1;
}
function BT_makeDateDash_2 (val) {
  if (val == "00000")
   DashedDate_2 = "";
  else
   DashedDate_2 = val.substring(0,2) + "-" + val.substring(2);
   
 return DashedDate_2;
}

function BT_makeDateDash_3 (val) {
  if (val == "00000000")
   DashedDate_3 = "";
  else
   DashedDate_3 = val.substring(6,8);
   
 return DashedDate_3;
}

function BT_makeDateSlash (val) {
  if (val == "00000000")
   DashedDate_1 = "";
  else
   DashedDate_1 = val.substring(0,4) + "/" + val.substring(4,6);
   
 return DashedDate_1;
}

function BT_makeTime (val) {
  ReturnTime = val.substring(0,2) + ":" + val.substring(2,4) + ":" + val.substring(4,6);
 return ReturnTime;
}

//문자열을 10진수로 변환
function BT_StrToInt(val) {
 var Re_int = 0
 
 if (val != "")
  var Re_int = parseInt(val,10);
 else
  Re_int = 0;
 
 return Re_int;
}

 

function BT_makeDecimalPoint(val) {
 var ReturnVal = val.split(".");
 ReturnVal = BT_Add_NumberComma(ReturnVal[0]) + "." + ReturnVal[1];
 return ReturnVal;
 
}


//페이지 설정
function last_page(val){
 var last_page = val%10;
 var index = parseInt(val/10);

 if (last_page > 0){
  return(index+1);
 }else
  return(index);


//소수점이하 자리수 설정
function BT_makeCipherPoint(val,cipher)
{
 
 var PreCipher = val.length - cipher;

 var Preval = val.substring(0,PreCipher);
 var Nextval = val.substring(PreCipher);
 var Returnval = Preval + "." + Nextval;

 return Returnval
}

//숫자와 영어만 허용
function BT_EngNum(obj,isUP){

 //if (event.keyCode == 9 || event.keyCode == 37 || event.keyCode == 39) return;//에러 발생 수정함.
 var returnValue = "";
 for (var i = 0; i < obj.value.length; i++){
  if ((obj.value.charAt(i) >= "0" && obj.value.charAt(i) <= "9") || (obj.value.charAt(i) >= "a" &&  obj.value.charAt(i) <= "z") || (obj.value.charAt(i) >= "A" && obj.value.charAt(i) <= "Z") ){
   returnValue += obj.value.charAt(i);
  }else{
   returnValue += "";
  }
 }

 obj.value = returnValue;
 obj.focus();
}


//날짜 계산 -- jjung
function BT_getDate(Dnum, gubun, StaYer,StaMon,StaDay,EndYer,EndMon,EndDay){
 var cur_year, cur_month, cur_day, cur_hour, cur_min, cur_sec, d_cur_date
 var index=0;
 var Mnum=0;

 var theDate = btGetCurrTime(2,1);
 var now = new Date();
 var daynum = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
 var monname = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');

 if(theDate == ""){
     cur_year= now.getYear();
  cur_month =  now.getMonth() + 1;
  cur_month = new String(cur_month);
  cur_day = new String(now.getDate());
  cur_hour = theDate.substring(8,10);
  cur_min = theDate.substring(10,12);
  cur_sec = theDate.substring(12);
 } 
    else {
  cur_year = theDate.substring(0,4);
  cur_month = theDate.substring(4,6);
  cur_day = theDate.substring(6,8);
  cur_hour = theDate.substring(8,10);
  cur_min = theDate.substring(10,12);
  cur_sec = theDate.substring(12,14);
 }
   
 daynum[1] = cday(cur_year);
    change_month = monname[BT_StrToInt(cur_month) - 1];
 tmpDate = new String(change_month + cur_day + "," + cur_year + " " + cur_hour + ":" + cur_min + ":" + cur_sec );
 d_cur_date = new Date(tmpDate);

 if(gubun == "mon"){
  tmpMonth = BT_StrToInt(cur_month) + Dnum;
  if (tmpMonth == 0)
  {
   tmpMonth = "12";
   change_month = monname[BT_StrToInt(tmpMonth) - 1];
   tmpDate = new String(change_month + cur_day + "," + (cur_year - 1) + " " + cur_hour + ":" + cur_min + ":" + cur_sec );
  }
  else if (tmpMonth == -1)
  {
   tmpMonth = "11";
   change_month = monname[BT_StrToInt(tmpMonth) - 1];
   tmpDate = new String(change_month + cur_day + "," + (cur_year - 1) + " " + cur_hour + ":" + cur_min + ":" + cur_sec );
  }
  else {
   change_month = monname[BT_StrToInt(tmpMonth) - 1];
   tmpDate = new String(change_month + cur_day + "," + cur_year + " " + cur_hour + ":" + cur_min + ":" + cur_sec );
  }
  before_date = new Date(tmpDate);
 }else{
  var before_date = new Date(Date.parse(d_cur_date) + BT_StrToInt(Dnum)*1000*60*60*24);
 }

 if(theDate.length==0) {   
  var before_year = new String(before_date.getYear());
  var before_month = new String(before_date.getMonth() + 1);

 }else {
  var before_year = new String(before_date.getYear());
  var before_month = new String(before_date.getMonth() + 1);
 } 

 if(before_month.length==1){
  before_month = '0'+before_month;
 }
 var before_day = new String(before_date.getDate());

 if(before_day.length==1){
  before_day = '0'+before_day;
 }
 if(StaYer != "") eval("doc."+StaYer+".value=before_year");
 if(StaMon != "") eval("doc."+StaMon+".value=before_month");
 if(StaDay != "") eval("doc."+StaDay+".value=before_day");
 
 if(EndYer != "") eval("doc."+EndYer+".value=before_year");
 if(EndMon != "") eval("doc."+EndMon+".value=before_month");
 if(EndDay != "") eval("doc."+EndDay+".value=before_day");

 if(StaYer == "" && StaMon == "" && StaDay == "" && EndYer == "" && EndMon == "" &&  EndDay == "") return before_year + before_month + before_day;

}

//val : 전체 값, point : 소수점 앞에 자리수
function BT_toMoneyF(val,point,gubun){
 var foreVal = 0;
 var downVale = 0;
 if (BT_TrimSpace(val) == ""){
  return foreVal;
 } else {
  if(point == "" || point == "0") {
   foreVal = val;
   downVal = "";
  } else {
   if(gubun == "1"){
    foreVal = val.substring(0,point);
   } else{
    foreVal = BT_Add_NumberComma(val.substring(0,point));
   }
   downVal = val.substring(point);
  }
  return foreVal + "." + downVal;
 }
}


function BT_SetServiceCode(code){
 save_session("SERVICE_CODE",code);    
 save_flush();     
}

function BT_Setreturn_url(val){
 save_session("RETURN_URL",val);    
 save_flush();     
}

//2년 윤년처리
function cday(year)
{
 //2월달일때 윤년 처리
 if ((year % 4) == 0) { //윤년
  if ((year % 100) == 0) { //평년
   if ((year % 400) == 0) { //윤년
    daynum = 29;
   }
   else {//평년
    daynum = 28;
   }
  }
  else { //윤년
   daynum = 29;
  }
 }
 else {//평년
  daynum = 28;
 }
 return daynum;
}

/**
* 입력한 숫자 및 영문(조합도 상관없음)이 비밀번호로 사용 가능한지 다음과 같은 제한사항을
* 검사하는 메소드
* 1. 숫자의 경우 하나의 숫자 조합
* 2. 숫자의 경우 오름차순 및 내림차순
* 3. 영문의 경우 하나의 영문 조합(대소문자는 다른 문자로 인식함)
* 4. 영문의 경우 오름차순 및 내림차순
* @obj 입력폼의 object
* @return true : 비밀번호로 사용가능
* @return false : 비밀번호로 사용할 수 없음. 각 상황에 맞는 메시지가 내장되어 있음.
*/
function pwCheck(obj)
{
 var val = obj.value;
 var flag = false;

 for(i=0;i<val.length;i++)
 {
  if(!BT_isNumber(val.charAt(i)))//숫자 체크
  {
   flag = true;
   break;
  }
 }

 if(flag)//숫자와 영문 혼합 or 영문 조합으로 예상됨
 {
  for(i=0;i<val.length;i++)
  {
   if(!BT_isAlphabet(val.charAt(i)))//영문 체크
   {
    flag = false;
    break;
   }
  }

  if(flag)//*** 영문으로만 조합 연번 체크 요구됨 ***
  {
   if(check_alpha_all_dup(val))//*** 같은 영문자만의 조합 검사 ***
   {
    alert("입력하신 비밀번호가 모두 같은 영문입니다.");
    return false;
   }
   else//*** 오름 차순 및 내림 차순 검사 ***
   {
    if(check_alpha_asc(val))//*** 오름차순 검사 ***
    {
     alert("입력하신 비밀번호가 영문 오름차순입니다.");
     return false;
    }
    else//*** 내림차순 검사 ***
    {
     if(check_alpha_desc(val))
     {
      alert("입력하신 비밀번호가 영문 내림차순입니다.");
      return false;
     }
     else//*** 비밀번호로 사용가능 ***
     {
      return true;
     }
    }
   }
  }
  else//*** 영문 및 숫자 조합 - 비밀번호로 상용가능 ***
  {
   return true;
  }
 }
 else//*** 숫자로만 조합되었으므로 연번 체크 요구됨 ****
 {
  if(check_digit_all_dup(val))//*** 같은 숫자만의 조합 검사 ***
  {
   alert("입력하신 비밀번호가 모두 같은 숫자입니다.");
   return false;
  }
  else//*** 오름 차순 및 내림 차순 검사 ***
  {
   if(check_digit_asc(val))//*** 오름차순 검사 ***
   {
    alert("입력하신 비밀번호가 오름차순입니다.");
    return false;
   }
   else//*** 내림차순 검사 ***
   {
    if(check_digit_desc(val))
    {
     alert("입력하신 비밀번호가 내림차순입니다.");
     return false;
    }
    else//*** 비밀번호로 사용가능 ***
    {
     return true;
    }
   }
  }
 }
}


/**
* 숫자로만 구성된 문자열이 내림차순의 숫자들인지 확인
* @return true : 내림차순이다.
* @return false : 내림차순이 아니다.
*/
function check_digit_desc(val)
{
 var init_cnt = 0;
 var flag = true;

 for(i=0;i<numberArray.length;i++)
 {
  if(val.charAt(0) == numberArray[i])
  {
   init_cnt = i;
   break;
  }
 }

 var init_cnt_tmp = init_cnt;
 for(i=0;i<val.length;i++)
 {
  if(val.charAt(i) == numberArray[init_cnt_tmp])
  {
  }
  else
  {
   flag = false;
   break;
  }
  
  init_cnt_tmp -= 1;
  if(init_cnt_tmp == -1)
  {
   init_cnt_tmp = numberArray.length - 1;
  }
 }

 return flag;
}

/**
* 영문으로만 구성된 문자열이 내림차순인지 확인
* @return true : 내림차순이다.
* @return false : 내림차순이 아니다.
*/
function check_alpha_desc(val)
{
 var init_cnt = 0;
 var flag = true;

 for(i=0;i<alphaArray.length;i++)
 {
  if(val.charAt(0) == alphaArray[i])
  {
   init_cnt = i;
   break;
  }
 }

 var init_cnt_tmp = init_cnt;
 for(i=0;i<val.length;i++)
 {
  if(val.charAt(i) == alphaArray[init_cnt_tmp])
  {
  }
  else
  {
   flag = false;
   break;
  }
  
  init_cnt_tmp -= 1;
  if(init_cnt_tmp == -1)
  {
   init_cnt_tmp = alphaArray.length - 1;
  }
 }

 return flag;
}


/**
* 숫자로만 구성된 문자열이 오름차순의 숫자들인지 확인
* @return true : 오름차순이다.
* @return false : 오름차순이 아니다.
*/
function check_digit_asc(val)
{
 var init_cnt = 0;
 var flag = true;

 for(i=0;i<numberArray.length;i++)
 {
  if(val.charAt(0) == numberArray[i])
  {
   init_cnt = i;
   break;
  }
 }

 var init_cnt_tmp = init_cnt;
 for(i=0;i<val.length;i++)
 {
  if(val.charAt(i) == numberArray[init_cnt_tmp])
  {
  }
  else
  {
   flag = false;
   break;
  }
  
  init_cnt_tmp += 1;
  if(init_cnt_tmp == numberArray.length)
  {
   init_cnt_tmp = 0;
  }
 }

 return flag;
}

/**
* 영문으로만 구성된 문자열이 오름차순 인지 확인
* @return true : 오름차순이다.
* @return false : 오름차순이 아니다.
*/
function check_alpha_asc(val)
{
 var init_cnt = 0;
 var flag = true;

 for(i=0;i<alphaArray.length;i++)
 {
  if(val.charAt(0) == alphaArray[i])
  {
   init_cnt = i;
   break;
  }
 }

 var init_cnt_tmp = init_cnt;
 for(i=0;i<val.length;i++)
 {
  if(val.charAt(i) == alphaArray[init_cnt_tmp])
  {
  }
  else
  {
   flag = false;
   break;
  }
  
  init_cnt_tmp += 1;
  if(init_cnt_tmp == alphaArray.length)
  {
   init_cnt_tmp = 0;
  }
 }

 return flag;
}

/**
* 숫자로만 구성된 문자열이 같은 숫자로 조합되었는지 확인
* @return true : 동일한 값으로 조합. 비밀번호로 사용하면 안됨.
* @return false : 다른 숫자가 적어도 하나 존재. 비밀번호로 사용해도 괜찮음.
*/
function check_digit_all_dup(val)
{
 var flag = true;

 for(i=0;i<numberArray.length;i++)
 {
  flag = true;

  for(j=0;j<val.length;j++)
  {
   if(val.charAt(j) == numberArray[i])
   {
   }
   else
   {
    flag = false;
    break;
   }
  }

  if(flag)
  {
   break;
  }
  else
  {
   continue;
  }
 }

 return flag;
}

/**
* 영문으로만 구성된 문자열이 같은 영문으로 조합되었는지 확인
* @return true : 동일한 영문으로 조합. 비밀번호로 사용하면 안됨.
* @return false : 다른 영문이 적어도 하나 존재. 비밀번호로 사용해도 괜찮음.
*/
function check_alpha_all_dup(val)
{
 var flag = true;

 for(i=0;i<alphaArray.length;i++)
 {
  flag = true;

  for(j=0;j<val.length;j++)
  {
   if(val.charAt(j) == alphaArray[i])
   {
   }
   else
   {
    flag = false;
    break;
   }
  }

  if(flag)
  {
   break;
  }
  else
  {
   continue;
  }
 }

 return flag;
}

 

and

부록 A 라이브러리에 있는 C 언어 기능들

C 라이브러리에 의해서 실행되는 기능들 중 어떤 것은 C 언어 그 자체의 일부처럼 생각될 수 있다. 그 기능들은 라이브러리 매뉴얼이 아니라, C 언어 매뉴얼에서 문서화되어야 하지만, 우리는 아직 그 C 언어 매뉴얼을 만들지 않았기 때문에, 그 기능들을 이곳에서 설명할 것이다.


A. 1 내부적 일관성을 명백하게 테스트하기

당신이 프로그램을 만들 때, "불가능한" 에러나 기본 가설의 위반에 대해서 전략적인 위치에서 그들을 체크하는 것은 좋은 생각이다. 그 체크는 프로그램의 다른 부분들 사이에서 불화합으로 일어나는 문제들을 디버깅하는데 유익하다.

헤더파일 `assert. h'에 정의된, assert 매크로는 프로그램의 에러가 검출된 곳에서 에러메세지를 출력하는 동안 프로그램을 중지시키기에 편리한 방법을 제공한다. 당신이 당신의 프로그램을 디버깅하려할 때, 당신은 정의된 매크로 NDEBUG를 사용해서 재컴파일하면 assert 매크로에 의해서 수행됐던 에러 체크는 디버그하지 않을 수 있다. 이것은 기능무효(disable)로 체크된 프로그램 소스 코드는 변경하지 않는다는 것을 의미한다.

그러나 그 체크를 무효화(disabling)하는 것은 그 프로그램을 확연하게 느리게 만들지 않는 한 바람직하지 않다. 또한 누가 그 프로그램을 실행시키던지 발생할 수 있는 가능한 에러에 대해서 대비하도록 좀더 많이 에러를 체크해보는 것이 좋다. 똑똑한 사용자는 어떤 것 이 잘못되었을 때 그것을 지적하지 않고 무의미한 반환값을 갖는 것보다는 차라리 프로그램이 파손되는 것을 원할 것이다.

매크로 : void assert (int expression)

프로그램 안에서 그 expression이 그 순간 0이 아닌 값이 되는지를 검증한다. 만일 NDEBUG 가 정의되지 않으면, assert는 expression의 값을 테스트하는데, 그 값이 0이면, assert는 다음 형식처럼 메시지를 프린트한 후에 프로그램을 중지시킨다 (22. 3. 4절 [Aborting a Program]참조)
`file': linenum: Assertion `expression' failed.
표준 에러 스트림 stderr에 메시지를 프린트한다 (7. 2절 [Standard Streams]참조. ). 파일 이름과 라인 번호는 C 전처리기 매크로 __FILE__ 과 __LINE__ 어로부터 가져오고 그 파일이름과 라인번호는 assert가 쓰여져서 호출된 곳으로 정한다. 만일 전처리기 매크로 NDEBUG가 `assert. h'가 인클루드된 지점에서 정의되었다면, assert 매크로는 완전히 아무 것도 하지 않도록 정의된 것이 된다.
 주의 : 만일 NDEUG가 영향력을 발휘하는 곳에서는 인수 expression 표현식도 평가되지 않는다. 그러므로 부작용이 있을 법한 인수를 넣고 assert를 사용하지 말아라. 예를 들어, assert(++i>0); 은 만일 NDEBUG가 정의된 곳에서는 i변수가 증가되지 않기 때문에 좋지 않다. 사용법 노트: assert 기능은 내부적 모순을 검출하기 위해서 설계되었다; 그러므로 사용자에 의한 부적당한 사용이나 무효한 입력을 보고하는데는 적당하지 않다.

매크로 assert에 의해 프린트된 진단 메시지에 있는 정보는, 당신의 프로그램을 사용하는 사용자에게 왜 입력이 무효한지 또는 왜 명령이 실행되지 않았는지를 알리는데는 유용하지 않지만, 프로그래머나 당신이, 프로그램에서 나타난 버그를 추적하는데 도움이 된다. 그러므로 당신은 예측할 수 없는 사건에 대한 에러메세지를 출력하는데 assert를 사용할 수 없다.

더 말하자면, assert에 무효한 입력이 주어진다면 당신의 프로그램은 중지된다_그것은 에러메세지를 출력한 후에 0이 아닌 상황(22. 3. 2절 [Exit Status]참조. )으로 종료되거나, 또는 다른 명령을 읽거나 다음 입력 파일로 옮긴다. 프로그램에 나타나지 않은 버그 문제에 에러메세지를 출력하는 것에 대한 정보는2. 3절 [Error Messages]참조.


A. 2 가변인자 함수들

ANSI C 는 인수의 타입이나 개수를 다양하게 취할 수 있는 함수를 선언하기 위한 구문을 정의한다. ( 그와같은 함수들은 varargs 함수 또는 variadic 함수라고 부른다. ) 그렇지만, 언어 그 자체는 그와같은 함수들을 위한 메커니즘을 제공하지 않는다; 대신에, `stdarg. h'에 정의된 가변 인수 매크로들을 사용하는 것이다. 이 절은 어떻게 가변인자 함수들을 선언하고, 어떻게 사용하며, 호출할 것인지에 대해서 설명한다. 이식성 노트 : 많은 오래된 C 방언들은 `varargs. h'를 사용해서 다양한 개수의 변수를 정의하는 함수 메커니즘으로 유사한 것을 제공하지만, 호환성이 없다.

 

A. 2. 1 왜 가변인자 함수들이 사용되는가?

원래 C 함수들은 고정된 개수의 인수들을 취한다. 당신이 함수를 정의할 때, 당신은 각 인수의 데이터 타입을 정한다. 함수가 호출될때마다 그전에 정해진 예상된 개수의 인수들이 공급되는데, 그 인수들의 타입은 정해진 것으로 변경될 수 있는 것이다. 그래서, 만일 함수 `foo'가 foo(int, char *); 로 선언된다면, 하나는 숫자 다른 하나는 문자열 포인터인 두 개의 인수를 가지고 foo 함수를 호출해야만 한다.

그러나 어떤 함수들은 정해지지 않은 개수의 인수를 받아들일 수 있는 동작을 수행한다. 어떤 경우, 함수는 한 블록에 그들의 모두를 처리함으로써 여러 개수의 값들을 처리할 수 있다. 예를 들어, 정해진 값들의 집합을 저장하기 위해서 malloc으로 일차원 배열을 할당하는 함수를 고려해보자. 이 연산은 숫자에 해당하는 배열의 길이로써 어떤 개수의 값이 있다고 이해한다. 가변 인수 기능이 없다면, 당신은 가능한 배열 크기를 얻어내는 또 다른 한 개의 함수를 정의해야만 한다.

라이브러리 함수 printf (7. 9절 [Formatted Output]참조. )는 가변인수가 유용하게 쓰이는 다른 부류의 함수에 대한 예제가 된다. 이 함수는 규정된 템플리트 문자열의 제어 하에 인수들( 개수뿐만 아니라 다양한 형을 가질 수 있다)을 프린트한다. 가변인자 함수는 많은 인수들을 처리할 수 있다는 점에서 가변인수 함수를 정의하는 이유가 된다.

open과 같은 함수들은 고정된 개수의 인수들을 취하지만, 때때로 마지막 몇 개는 무시된다. ANSI C 는 그 함수를 가변으로 정의하도록 요구하지만; GNU C 컴파일러와 대부분 다른 C 컴파일러들을 고정된 인수를 취하는 함수처럼 정의하도록 허용하고 선언할 때만 가변으로써 그 함수를 선언한다 (또는 그 인수들을 전혀 선언하지 않는다. ).

 

A. 2. 2 어떻게 가변인자 함수를 정의하고 사용하는가?

가변인자 함수를 정의하고 사용하는 세 가지 단계이다.

인수리스트안에 생략표시 (`. . . ')를 사용하고, 가변 인수들을 억세스 하도록 특별한 매크로들을 사용하여서, 가변인수 함수를 정의하라.A. 2. 2. 2절 [Receving Arguments]참조.

그것을 호출하는 모든 파일에서, 생략표시 (`. . . ')와 함께 프로토타입을 사용해서 가변으로써 함수를 선언하라.A. 2. 2. 1절 [Varidic Prototypes]참조.

고정된 인수들 뒤에 가변인수들이 뒤에 나오도록 해서 함수를 호출하라.A. 2. 2. 4 [Calling Variadics]참조.

 

A. 2. 2. 1 가변 인수들을 위한 구문

가변 인수를 받아들이는 함수는 올바른 프로토타입으로 선언되어야만 한다. 당신은 보통 고정된 인수들을 사용하고 가변인수들의 가능성을 지적하기 위해서 `. . . '을 취한다. ANSI C 구문은 `. . . '가 나오기 전에 적어도 한 개의 고정 인수를 필요로 한다. 예를 들어,

int
func (const char *a, int b, . . . )
{
. . .
}

고정된 두 개의 인수로써, const char * 와 int 인수를 취하고 int형의 값을 반환하는 func 함수의 정의이다. 그 두 개의 고정인수 다음에 알려지지 않은 인수들이 몇 개가 따르게 된다. 이식성 노트 : 어떤 C 컴파일러에서, 함수 정의에서 가변인수는 형을 선언하여 등록될 수 없다. 좀더 자세히 말하면, 이 인수들의 타입은 자체-진행(self-promoting)이 되어야만 한다: 즉, 디폴트 진행은 그 타입들을 변경하지 않아야 한다. 이것은, float , char(부호가 있던지 없던지), 그리고 short int (부호가 있거나 없거나) 뿐만 아니라 배열과 함수들의 타입을 무시한다.

 

A. 2. 2. 2 인수 값들을 받기

보통 고정된 인수들은 개별적인 이름을 갖고, 당신은 그들의 값을 억세스하기 위해서 그들의 이름을 사용할 수 있다. 그러나 가변 인수들은 아무런 이름을 갖지 않는다. 어떻게 당신이 그들을 억세스 할 것인가? 그들을 억세스하기 위한 유일한 방법은 그들이 기록된 순서대로, 순차적으로 억세스하고 다음 세 가지 단계에서 있는 헤더파일 `stdarg. h'에 선언된 특별한 매크로들을 사용해야만 한다.

1. va_start를 사용해서 va_list 형의 포인터 변수를 인수로써 초기화한다. 초기화된 인수 포인터는 첫 번째 가변인수를 가리킨다.
2. va_arg를 호출함으로써 가변인수들을 억세스 한다. va_arg를 첫 번째 호출하면, 첫 번째 인수를 반환하게 되고, 다음 호출은 두 번째 인수를 반환하고. . . 그렇게 진행이 된다.

당신이 만일 남겨진 가변인수를 무시하기를 원한다면 언제든지 멈출 수 있다. 호출로 공급된 인수들보다는 소수의 인수들을 억세스 하는 함수를 위해서 아주 좋지만, 만일 당신이 너무 많은 인수들을 억세스 하려 시도한다면 당신은 쓰레기 값을 얻게 될 것이다.

3. va_end를 호출해서 포인터 변수인 인수를 끝냈음을 알려라.

(실제로, 대부분 C 컴파일러에서, va_end의 호출은 아무 일도 하지 않고 당신은 그것을 실제로 호출할 필요가 없다. 이것은 GNU C 컴파일러에서는 항상 참이다. 그러나 누군가 당신의 프로그램을 독특한 컴파일러에서 컴파일하는 경우라면 va_end를 호출해야만 할 것이다.

va_start, va_arg 그리고 va_end에 대한 완전한 정의는A. 2. 2. 5절 [Argument Macros]를 참조하라. 단계 1과 3은 가변 인수를 받아들이는 함수에서 반드시 수행되어야만 한다. 그렇지만, 당신은 다른 함수에 인수로써 va_list 변수를 줄 수 있고 전부 또는 단계 2를 수행할 수 있다.

당신은 단일한 함수 호출에서 여러 번 세 단계의 전부를 반복해서 수행 할 수 있다. 만일 당신이 가변 인수를 무시하기를 원한다면, 세단계를 하지 않을 수 있다. 만일 당신이 원한다면 포인터 변수인 한 개의 인수보다 더 많은 것을 가질 수 있다. 당신은 당신이 원할 때 va_start로 각 변수를 초기화 할 수 있고, 그러고 나면 당신이 원하는 각각의 포인터 인수를 추출할 수 있다. 각 포인터 변수인 인수는 인수 값들의 같은 집합을 통해서 진행되지만, 그것은 자신만의 페이스(pace)를 갖는다. 이식성 노트: 어떤 컴파일러로, 당신이 서브루틴(subroutine)에 인수로써 포인터 변수를 사용한다면, 당신은 서브루틴이 반환한 후에 같은 포인터 변수인 인수를 사용해서 기록하지 않아야만 한다. 완벽한 이식성을 위해서, 당신은 va_end에 그것을 주어야한다. 이것은 실제로 ANSI C의 권장사항이지만, 대부분 ANSI C 컴파일러는 다행이 상관없이 작업한다.

 

A. 2. 2. 3 어떻게 많은 인수들이 공급되는가?

가변 인수들의 타입과 개수를 알 수 있는 일반적인 방법은 없다. 그래서 누구든 그 가변인수가 얼마나 많은 인수들을 가졌고, 그것이 무슨 종류인지를 알아낼 수 있는 특별한 방법의 함수를 고안해야한다. 그것은 가변인수 함수의 호출 관습에 적당하게 정의되어야 하고, 그것에 근거하여 프로그램에서 가변 인수 함수를 호출해야 한다.

호출관습의 한가지는 한 개의 고정된 인수를 사용해서 가변인수의 개수를 공급하는 것이다. 이 방법은 공급된 가변 인수들이 모두 같은 형일 경우에 가능한 방법이다. 그와 유사한 방법으로는 가변인수가 공급될 가능성에 대한 정보를 한 비트에 담은, 비트 마스크가될 고정인수를 인수중에 하나로 갖는 것이다. 당신은 미리 선언된 시퀀스 안에 있는 비트들을 테스트할 수 있다; 만일 그 비트가 설정되면, 다음 인수의 값을 추출하는 것이고, 그렇지 않다면, 디폴트값을 사용하는 것이다. 고정된 인수는 가변 인수들의 개수와 타입, 이 둘을 지정하는 패턴으로써 사용될 수 있다. printf에서 형식화된 문자열 인수는 이것의 한 예가 된다. (7. 9. 7절 [Formatted Output Functions]참조.)

다른 가능성은 마지막 가변 인수로써 "끝 표시"값을 사용하는 것이다. 예를 들어, 예측할 수 없는 포인터 인수들의 개수를 처리하는 함수가 있다면, 널 포인터는 인수 리스트의 끝을 지적할 것이다. (이것은 널 포인터가 함수에게 의미 있는 값이 아니라고 가정한다. ) execl 함수는 이 방법으로 작업한다;23. 5절 [Executing a File]참조.

 

A. 2. 2. 4 가변인수 함수들을 호출하기

당신이 가변인수 함수를 호출할 때 특정한 어떤 것을 써서는 안된다. 단지 괄호안에 보통, 콤마에 의해 분리된 인수들(가변으로써, 요청된 인수)만 사용하라. 그러나 당신은 프로토타입으로 그 함수를 선언함으로써 준비하고, 그 인수의 값들이 어떻게 변환되는지를 알아야만 한다.

원칙적으로, 가변으로써 정의된 함수들은 당신이 그들을 호출할 때마다 함수 프로토타입을 사용해서 가변이 되도록 선언되어야한다. (A. 2. 2. 1 [Variadic Prototypes]참조. ) 이것은 함수가 가변 인수 또는 고정된 인수를 취하는지의 여부에 의존하여 함수에 인수 값들을 부여하는 다른 호출 관습을 가진 C 컴파일러 때문이다.

실제로, GNU C 컴파일러는 항상 당신이 가변인수 또는 요청된 인수를 사용하는지에 상관없이 같은 방법으로 인수형의 주어진 집합을 부여한다. 그래서, 인수들의 타입이 자체-진행인 동안, 당신은 그들의 선언을 안전하게 생략할 수 있다. 보통 가변함수를 위해서 인수의 형을 선언하는 것은 좋은 방법이고, 모든 함수들을 위해서는 물론 당연한 것이다. 그런데 몇 개의 함수는 그렇지 않은 경우가 있다_예를 들어, open과 printf

함수의 프로토타입이 가변인수들의 타입을 정하지 않았을 때, 가변인수 함수를 호출하면, 함수의 가변 인수 값들은 디폴트 인수 승급이 수행된다. 디폴트 인수 승급이란 char 또는 short int (부호가 있던지 없던지)의 형을 가진 오브젝트들은 int 나 unisgned int로 승급되고; float의 형을 가진 오브젝트들은 double로 승급되는 것을 말한다. 그래서, 가변인수에 char형의 값을 넣으면, 그것은 int로 승급되고, 그 함수는 va_arg(ap, int)과 함께 그것을 얻을 것이다.

고정 인수들은 보통 함수의 원형을 통해서 제어된다: 인수 표현식은 마치 그형의 변수로 할당되었던 것 선언된 인수의 형으로 변환된다.

 

A. 2. 2. 5 인수 억세스 매크로들

다음은 가변 인수들을 가져오기 위해서 사용되는 매크로에 대한 기술이다. 그 매크로들은 헤더파일 `stdarg. h'에 정의되어 있다.

데이터 타입 : va__list

va_list는 포인터 변수들인 인수를 위해서 사용된다.

매크로 : void va__start (va_list ap, last_required)

이 매크로는 현재 함수의 가변 인수들의 첫 번째를 가리키는 포인터 변수 ap를 초기화한다; lastrequired는 함수에 있는 마지막 고정인수가 되어야 한다. `varargs. h'에 있는 va_start의 정의를 변경하려면A. 2. 3. 1 [Old Varargs]를 참조하라.

매크로 : type va__arg (va_list ap, type)

va_arg 매크로는 다음 가변 인수의 값을 반환하고, 다음 인수를 가리키도록 ap의 값을 갱신한다. 그래서, va_arg의 성공적인 사용은 가변 인수들을 성공적으로 반환한다. va_arg에 의해 반환된 값의 타입은 호출에서 정했던 타입이다. type은 반드시 실제 인수의 타입과 매치되는 자체-승급 타입 (char나 short int 나 float가 아닌)이 되어야 한다.

매크로 : void va__end (va_list ap)

이것은 ap의 사용을 끝낸다. va_end 호출 후에, 다음에 같은 ap를 사용해서 va_arg를 호출하면 작업하지 않을 것이다. 당신은 같은 ap 인수를 사용하는 va_start를 호출했던 함수를 반환하기 전에 va_end를 호출해야만 한다. GNU C 라이브러리에서, va_end는 아무 일도 하지 않기 때문에 이식성의 이유가 아니라면 va_end를 호출할 필요가 없다.

 

A. 2. 3 가변인수 함수의 예제

다음은 인수들을 가변적인 개수로 받아들이는 함수에 대한 예이다. 함수의 첫 번째 인수는 반환된 결과와 합산된, 남겨진 인수들의 개수이다. 이 함수는 가변 인수 기능을 어떻게 사용하는지 설명하는데 충분하다.

#include <stdarg. h>
#include <stdio. h>
int
add_em_up (int count, . . . )
{
va_list ap;
int i, sum;
va_start (ap, count);
/* 인수 목록을 초기화하라. */
sum = 0;
for (i = 0; i < count; i++)
sum += va_arg (ap, int);
/* 다음 인수값을 얻어라. */
va_end (ap);
/* 정리하라. */
return sum;
}
int
main (void)
{
/* 이 호출은 16을 출력한다. */
printf ("%d\n", add_em_up (3, 5, 5, 6));
/* 이 호출은 55를 출력한다. */
printf ("%d\n", add_em_up (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
return 0;
}

 

A. 2. 3. 1 오래된-형태의 가변인수 함수들

ANSI C 이전에, 프로그래머들은 가변함수들을 쓰기 위해서 완전히 다른 기능을 사용했었다. GNU C 컴파일러는 여전히 그것을 지원한다; 현재, 그것은 ANSI C가 여전히 일반적이지 않기 때문에, ANSI C 기능보다는 더 이식성이 있다. 오래된-형태의 가변인수 함수를 정의하고 있는 헤더파일은 `varargs. h'라고 불린다.

`varargs. h'를 사용하는 것은 `stdarg. h'를 사용하는것과 거의 같다. 가변인수 함수를 어떻게 호출하는지에 대한 것은 거의 다름이 없다;A. 2. 2. 4절 [Calling Variadics]참조. 오직 유일한 차이는 그들을 정의하는 방법이다. 무엇보다도, 당신은 오래된 형태의 비- 프로토타입 구문을 사용해야만 한다. 다음처럼:

tree
build (va_alist)
va_dcl
{
두 번째로, va_start에 오직 한 개의 인수만을 넣어야한다. 다음처럼:
va_list p;
va_start (p);

오래된 형태의 가변인수 함수들을 정의하기 위해서는 특정한 매크로가 사용된다:

매크로 : va__alist

이 매크로는 가변인수 함수에 있는 고정 인수이름 목록을 나타낸다.

매크로 : va__decl

이 매크로는 가변인수 함수를 위한 인수들이나 또는 암묵적인 인수를 선언한다.

매크로 : void va__start (va_list ap)

`varargs. h'에 선언된 이 매크로는 현재 함수의 첫 번째 인수를 가리키는 포인터로 포인터 변수인 인수를 초기화한다.

 

다른 인수 매크로, va_arg 와 va_end는 `varargs. h' 와 `stdarg. h'의 것이 서로 같다;A. 2. 2. 5절 [Argument Macros]참조. 동일한 컴파일 단위에서 `varargs. h'와 `stdarg. h'가 둘다 인클루드 되어서는 안된다; va_start가 서로 충돌하게 된다.


A. 3 널 포인터 상수

널 포인터 상수는 어느 실제 오브젝트를 가리키고 있는 것이 아니라는 것을 말한다. 당신은 void * 형을 가진 포인터 변수로 그것을 할당할 수 있다. 널 포인터 상수를 사용하기 위한 좋은 방법은 NULL을 사용하는 것이다.

매크로 : void * NULL

이것은 널 포인터 상수이다.
널 포인터 상수로써 0 또는 (void *)0을 사용할 수 있지만, NULL을 사용하는 것이 상수를 사용하는 목적으로 좀더 분명하게 만들어졌기 때문에 더 깨끗하다. 만일 당신이 함수 인수로써 널 포인터 상수를 사용한다면, 그 함수가 가진 프로토타입 선언과 완전히 맞도록 널 포인터 상수를 사용해야한다. 그렇지 않다면, 만일 목표 머쉰(machin)이 두 개의 다른 포인터 표현을 가진다면, 컴파일러는 그 인수를 위해 사용할 표현이 무엇인지 알지 못한다.
당신은 적당한 포인터 타입으로 상수를 캐스트(cast)함으로써 그 문제를 피할 수 있지만, 우리는 그보다는 당신이 호출할 함수를 위해서 프로토타입에 더할 것을 권장한다.


A. 4 중요한 데이터 타입들

C에서 두 개의 포인터를 뺀 결과는 항상 정수이지만, 정밀한 데이터 타입은 C 컴파일러에 따라 다르다. . 그처럼 데이터 타입에 따라서, sizeof의 결과 또한 컴파일러에 따라서 다르다. ANSI 는 그 두 개의 데이터 타입을 위해서 표준 이름을 정의하기 때문에, 당신은 이식성을 위해서 그 데이터 타입을 사용할 수 있다. 그들은 헤더파일 `stddef. h'에 정의되어 있다.

데이터 타입 : ptrdiff__t

이것은 두 개 포인터를 빼서 나온 결과의 부호화된 정수 타입이다. 예를 들어, char *p1, *p2; 이렇게 선언되었다면; 표현식 p2 - p1은 ptrdiff_t 형의 결과값을 갖는다. 이것은 아마도 표준 부호형 정수 타입들(short int, int 또는 long int)중의 하나가 될 것이지만, 표준 부호형 정수 타입들이 이러한 목적으로 존재한다면 비표준 타입이 되어버릴 것이다.

데이터 타입 : size__t

이것은 오브젝트(objects)의 크기를 나타내는데 사용하는 비부호형 정수 타입이다. sizeof 연산의 결과가 이 타입을 갖고, malloc(3. 3절 [Unconstrained Allocation]참조. ) 그리고 memcpy(5. 4절 [Copying and Concatenation]참조. )와 같은 함수들은 오브젝트의 크기를 정하는데 이 타입의 인수를 받아들인다.
사용법 노트: size_t는 오브젝트의 크기를 저장하는 인수나 변수를 선언하는데 좋은 방법을 제공한다.

GNU 시스템에서 size_t는 unsigned int 또는 unsigned long int 와 동일하다. 그 타입들은 GNU 시스템상에서 동일한 특성을 갖고 있고, 그들을 사용할 때 대부분은 그들 사이를 서로 변경시키지 않고도 사용할 수 있다. 그렇지만, 그들은 어떤 구문들에서는 차이를 갖기 때문에 다른 데이터타입으로 구분된 것이다.

예를 들어, 함수의 원형으로 함수 인수의 타입을 정할 때, 당신이 사용하는 것은 차이가 있다. 만일 시스템 헤더파일이 size_t 타입의 인수를 갖는 malloc 함수를 선언하고, 당신이 unisgned int의 타입을 갖는 malloc를 선언했을 때, 만일 size_t가 당신의 시스템에서 unisgned long int형으로 발생한다면, 당신은 컴파일 에러를 얻을 것이다. 이러한 문제의 가능성을 피하기 위해서, 함수의 인수나 값은 다른 방법으로 그 형을 선언하기보다는 size_t의 타입을 갖도록 선언하라. 호환성 노트 : ANSI C가 나타나기 전에 C는 포인터 뺄셈의 결과를 나타내기 위해서 int를 사용하고 오브젝트의 크기를 표현하기 위해서 unsigned int를 사용했었다. 그들은 size_t나 ptrdiff_t를 정의할 필요가 없었다. 유닉스 시스템들은 `sys/types. h'에 size_t를 정의해놓았지만, 그 정의는 보통 signed 형을 말한다.


A. 5 데이터 타입 측정

당신이 당신의 프로그램에서 사용되는 오브젝트의 적당한 C 데이터 타입을 선정할 때 대부분 그것이 얼마나 많은 비트들을 사용하고 그 오브젝트가 어떻게 표현되는지에 관심을 가질 필요가 없다. 당신이 그와같은 정보를 필요로 할 때, C 언어 자체는 그것을 얻을 수 있는 방법을 제공하지 않는다.

헤더파일 `limits. h'와 `float. h'에 포함된 매크로들은 당신에게 그것에 관한 세심한 정보를 줄 것이다.

 

A. 5. 1 정수 데이터 타입의 너비 계산하기

정수 타입이 얼마나 많은 비트로 구성되었는지 알 필요가 있는 프로그램은 비트 벡터(bit vector)로써 lont int의 배열을 사용하는 경우가 일반적이다.

당신은 vector[n / LONGBITS] & (1 << (n % LONGBITS)) 로 인덱스 N을 구성하는 비트를 억세스할 수 있다. LONGBITS는 long int를 구성하는 비트의 개수로 당신이 정의해서 공급하라.

C 언어에서 정수 데이터 타입에 있는 비트의 수에 대한 정보를 당신에게 줄 수 있는 연산자는 없다. 그렇지만 헤더파일 `limits. h'에 정의된 매크로 CHAR_BIT를 사용해서 그것을 계산할 수 있다.

CHAR_BIT

이것은 한 개의 char안에 있는 비트의 개수를 말하는데, 대부분의 시스템에서 그 값은 8이다. 그 값은 int형을 갖는다. 당신은 다음처럼 어느 데이터 타입 type안의 비트의 개수를 계산할 수 있다

 

A. 5. 2 정수 타입의 범위

당신이 0에서 일 백만 사이에 있는 정수의 값을 저장할 필요가 있다고 가정해보자. 당신이 사용할 수 있는 가장 작은 타입은 무엇인가? 그것을 정하는데 일반적인 규칙은 없다; 그것은 C 컴파일러와 목표 머쉰(machine)에 의존한다. 당신은 타입을 결정하기 위해서 `limits. h'에 있는 매크로 `MIN'과 `MAX'를 사용할 수 있다.

각각의 부호가 있는 정수 타입은 그것이 저장할 수 있는 가장 작은 값과 가장 큰 값을 나타내는 한 쌍의 매크로를 갖는다. 부호가 없는 정수 타입은 최대의 값을 나타내는 한 개의 매크로를 갖는다; 최소값은 물론 0이다. 그 매크로들의 값은 모두 정수 상수 표현이다. 다른 타입들을 위한 `MAX' 와 `MIN' 매크로들은 매크로에 의해 설명된 같은 타입의 값을 갖는다_그래서 ULONG_MAX는 unisgned long int 의 타입을 갖는다.

SCHAR_MIN

signed char에 의해 표현될 수 있는 최소값.

SCHAR_MAX, UCHAR_MAX

signed char 와 unsigned char에 의해 표현될 수 있는 최댓값.

CHAR_MIN

char 에 의해 표현될 수 있는 최소값. 여기서 만일 char에 부호가 있는 것이라면 SCHAR_MIN과 같고, 그렇지 않다면 0이다.

CHAR_MAX

char 에 의해 표현될 수 있는 최댓값. char가 부호가 있는 것이라면 SCHAR_MAX와 같고, 그렇지 않다면 UCHAR_MAX와 같다.

SHRT_MIN

signed short int에 의해 표현될 수 있는 최소값. GNU C 라이브러리가 실행되는 대부분의 컴퓨터에서, short int 는 16-비트로 동일하다.

SHRT_MAX, USHRT_MAX

signed short int 와 unsigned short int에 의해 표현될 수 있는 최댓값.

INT_MIN

signed int 에 의해 표현될 수 있는 최소값. GNU C 시스템이 실행되는 대부분의 컴퓨터에서, int 32-비트로 동일하다.

INT_MAX, UINT_MAX

signed int 와 unsigned int 에 의해 각각 표현될 수 있는 최댓값들.

LONG_MIN

signed long int 에 의해 표현될 수 있는 최댓값. GNU C 시스템이 실행중인 대부분의 컴퓨터에서, long 정수들은 int와 동일한 크기를 갖고 32-비트로 동일하다.

LONG_MAX, ULONG_MAX

signed long int 와 unsigned long int에 의해 표현될 수 있는 최댓값.

LONG_LONG_MIN

signed long int에 의해서 표현될 수 있는 최소값. GNU C 시스템이 실행되는 대부분의 컴퓨터에서, long long 정수들은 64-비트로 동일하다.

LONG_LONG_MAX, ULONG_LONG_MAX

signed long long int 와 unsigned long long int에 의해 표현될 수 있는 최댓값.

WCHAR_MAX

wchar_t에 의해 표현될 수 있는 최댓값.18. 4절 [Wide Char Intro]참조.

헤더파일 `limits. h'는 또한 다양한 운영체제와 파일 시스템 제한들을 파라미터 화한 부가적인 상수들을 정의하고 있다. 그 상수들은27장[System Configuration]에 설명되어 있다.

 

A. 5. 3 부동형 매크로들

플로팅 포인트 수의 구체적인 표현 방법은 기계마다 다르다. 플로팅 포인트 수들은 내부적으로 근사치로써 표현되기 때문에, 플로팅 포인트 데이터를 다루기 위한 알고리즘은, 때때로 기계의 플로팅 포인트 표현에 대한 자세한 정밀도를 참작하는데 사용된다.

C 라이브러리에서 어떤 함수들은 이러한 정보를 필요로 한다; 예를 들어, 플로팅 포인트 숫자들을 읽거나 출력하는 알고리즘 (7장 [I/O on Stream]참조) 과 삼각함수를 계산하기 위한 알고리즘 그리고 무리수 함수들을 위한 알고리즘들은 정확성의 상실이나 반올림-에러를 피하기 위해서 그 정보를 사용한다. 수학적인 분석 기술을 다루는 프로그램들은 에러 경계를 계산하거나 최소화하기 위해서 이 정보를 필요로 한다. 헤더파일 `float. h'는 당신의 기계에 의해서 사용되는 형식을 설명한다.

 

A. 5. 3. 1 플로팅 포인트 표기 개념

이 절은 플로팅 포인트 표현법을 설명하기 위한 용어들을 설명한다. 당신은 플로팅 포인트 숫자들을 표현하는 과학적인 표기 또는 지수적인 표기의 개념에 대해서 이미 친숙할 것이다. 예를 들어, 숫자 123456. 0은 가수가 1. 23456이고 베이스가 10으로 5승임을 가리키는, 1. 234546e+05의 지수적 표기로써 표현될 수 있다. 더 형식적으로, 플로팅 포인트 수의 내부적 표현은 다음의 파라미터로써 특징을 나타낼 수 있다.

부호는 -1 또는 1 이다.

지수를 위한 베이스(base) 또는 기수(radix)는 1보다 큰 정수이다. 이것은 특정 표기에 따라서 다른 상수이다.

베이스에 몇 승인지를 나타내는 수가 지수이다. 지수의 상한과 하한은 특정한 표기에 따라서 다른 상수이다.

때때로, 플로팅 포인트 수를 표현하고 있는 실제 비트들에서, 지수를 항상 unsigned로 표현되도록 만들기 위해서 그것에 상수를 더하여 부호의 의미를 부여한다. 이것은 만일 당신이 직접 플로팅 포인트 수를 구성하고 있는 비트 영역들의 어떤 부분을 사용할 필요가 있는 경우에만 중요하다. GNU 라이브러리는 이러한 것을 지원하지 않는다. 그러므로 다음부터는 이것에 대한 논의는 무시될 것이다.

가수부(mantissa) 또는 유효수(significand)는 플로팅 포인트 숫자를 이루는 한 부분으로써 부호가 없는 정수이다.

가수부의 정밀도. 만일 어떤 플로팅 표현에서 베이스(base)가 b라고 했을 때, 정밀도는 베이스-b를 기반으로 가수부 안에 들어가 있는 숫자들의 개수이다. (즉. . 가수부가 몇 개의 비트로써 표현되느냐를 정밀도라고 한다. ) 이것은 특정한 표현에 따라 다른 상수이다.

많은 플로팅 포인트 표현들은 가수부 안에 암묵적으로 숨겨진 비트를 가지고 있다. 이것은 가수부 안에서 실질적으로는 표현되지만 그 값이 항상 1로 되어있기 때문에 메모리에는 저장되지 않는다. 정밀도 형태는(위를 보라) 숨겨진 비트들도 표함 한다. 다시, GNU 라이브러리는 플로팅 포인트 표현을 위해서 그와같은 저수준의 관점을 다루는 기능을 제공하지 않는다.

플로팅 포인트 수의 가수부는 지수의 몇 승을 가진 함축적인 소수부로써 표현된다. 그래서 가장 크게 표현할 수 있는 가수부가 이 정밀도보다 적은 것이면, 소수의 값은 항상 1보다 적다. 플로팅 포인트 수의 수학적인 값은 소수, 부호 그리고 베이스의 몇 승임을 나타내는 지수로써 만들어진다.

b가 베이스라고 했을 때, 소수가 적어도 1/b라면, 플로팅 포인트 수가 일반화되었다고 말한다. 바꾸어 말하면, 가수부에 지수승이 곱해지면 맞추기에 너무 크게 될 것이다. 비-일반화된 수들은 디노멀(denomal)이라고 부른다; 그들은 플로팅 포인트 수가 일반적으로 저장될 수 있는 정밀도 보다도 작은 정밀도를 갖고 있다.

만일 그 수가 일반화되지 않았다면, 가수부를 base로 나눈 다음 나온 지수로부터 1을 뺄 수 있고, 그러면 같은값을 가진 다른 표기형식의 플로팅 포인트를 얻게 된다. 그 수가 일반화될 때까지 반복적으로 위와 같은 일을 하면 일반화된 플로팅 포인트 수가 나오게 된다. 두 개의 다른 일반화된 플로팅 포인트 수들은 값이 같을 수 없다.

( 이 규칙에는 예외가 있다: 만일 가수부가 0이라면, 그것은 일반화된 것으로 간주된다. 특정한 기계에서 발생할 수 있는 예외상황이란, 지수부가 그 표기법으로 저장할 수 있기에는 너무 작은 경우이다. 그러면 지수부로부터 1을 빼는 것이 불가능하기 때문에 , 소수가 1/b보다 적은 소수부라면 일반화될 수 있을 것이다. )

 

A. 5. 3. 2 플로팅 포인트 파라미터들

다음 매크로 정의들은 헤더파일 `float. h'에 있다. `FLT_'로 시작하는 매크로들은 float 타입에 관한 것이고, `DBL_'로 시작되는 매크로들은 double 타입에 와 `LDBL_'로 시작되는 매크로들은 long double 타입에 관한 것이다. (현재 GCC 는 분리된 데이터형으로써 long double를 지원하지 않기 때문에, `LDBL_'상수들을 위한 값들은 double형을 위한 상수에 해당되는 값과 같다. )

그 매크로들 중에서, 오직 FLT_RADIX는 상수 표현식이 되도록 보증된다. 이곳에 설명된 다른 매크로들은 상수 표현식, `#if'와 같은 전처리 지시자 또는 정적 배열 안의 차원을 요구하는 곳에서 사용될 수 없다.

ANSI C 표준이 대부분의 파라미터들을 위한 최소값과 최댓값 정했다고 하더라도, GNU C는 목표 기계의 플로팅 포인트 표현에 따른 값을 사용한다. 그래서 GNU C는 목표 기계가 안정적이라면 ANSI C 요구를 만족시키게 되는 것이다. 실제로, 현재 지원되는 모든 기계들은 안정적이다.

FLT_ROUNDS

이 값은 반올림 형식을 지정하는 값이다. 다음 값은 표준 반올림 모드를 나타낸다.
-1 반올림하지 않는다.
0 소수점 뒤를 0으로 만든다.
1 가장 가까운 수로 반올림한다.
2 무한대로 양의 값을 향한다.
3 무한대로 음의 값을 향한다.
이 이외의 값은 기계_의존적인 비표준 반올림 모드를 나타낸다. 대부분의 기계에서, 그 값은 IEEE 표준에 따라서 1로 되어있다.
다음은 FLT_ROUNDS의 값에 따라서 값들이 어떻게 변하는지를 보여주는 테이블이다,
0 1 2 3
1. 00000003 1. 0 1. 0 1. 00000012 1. 0
1. 00000007 1. 0 1. 00000012 1. 00000012 1. 0
-1. 00000003 -1. 0 -1. 0 -1. 0 -1. 00000012
-1. 00000007 -1. 0 -1. 00000012 -1. 0 -1. 00000012

FLT_RADIX

이것은 지수부의 베이스(base) 또는 기수(radix)의 값이다. 이것은 이 절에 설명된 다른 매크로와는 달리 상수 표현식임이 보장된다. IBM 360과 그곳에서 파생된 제품을 제외하고는 모든 기계에서 2로 되어있다.

FLT_MANT_DIG

float형에서 가수부를 표현하는데 사용되는 비트수. 다음 표현식은 가수부 숫자들의 제한된 수 때문에 1. 0이 나온다(수학적으로는 그것이 될 수 없을 지라도):
float radix = FLT_RADIX;
1. 0f + 1. 0f / radix / radix / . . . / radix
여기서 radix는 FLT_MANT_DIG 번 나타난다.

DBL_MANT_DIG

LDBL_MANT_DIG

이것은 각각 double 과 long double형 각각이 가수부를 표현하는데 사용되는 비트수이다.

FLT_DIG

This is the number of decimal digits of precision for the float data type. Technically, if p and b are the precision and base (respectively) for the representation, then the decimal precision q is the maximum number of decimal digits such that any floating point number with q base 10 digits can be rounded to a floating point number with p base b digits and back again, without change to the q decimal digits.
float형에서 유효숫자의 최소개수.
이 매크로의 값은 ANSI C에서, 적어도 6으로 지원되고 있다.

DBL_DIG, LDBL_DIG

FLT_DIG와 유사하지만, double와 long double형을 위한 것이다. 그 매크로의 값은 적어도 10이 되도록 지원된다.

FLT_MIN_EXP

이것은 float형을 위해서 가능한 지수값으로 가장 작은 값이다. 더 자세하게는, FLT_RADIX에서 1을 뺀 값이 float형으로써 일반화된 플로팅 포인트 수로써 표현될 수 있는 최소 음의 정수이다.

DBL_MIN_EXP, LDBL_MIN_EXP

FLT_MIN_EXP와 유사하지만, double 와 long double를 위한 것이다.

FLT_MIN_10_EXP

This is the minimum negative integer such that 10 raised to this power minus 1 can be representedas a normalized floating point number of type float. This is supposed to be -37 or even less.
지수부의 최소범위. -37이거나 그보다 적다.

DBL_MIN_10_EXP, LDBL_MIN_10_EXP

double와 long double형 각각을 위한 것으로, 지수가 나타낼 수 있는 최소범위.

FLT_MAX_EXP

This is the largest possible exponent value for type float. More precisely, this is the maximum positive integer such that value FLT_RADIX raised to this power minus 1 can be represented as a floating point number of type float.
float이 표현할 수 있는 지수의 최댓값.

DBL_MAX_EXP, LDBL_MAX_EXP

double 와 long double형이 각각 표현할 수 있는 지수의 최댓값.

FLT_MAX_10_EXP

This is the maximum positive integer such that 10 raised to this power minus 1 can be represented as a normalized floating point number of type float. This is supposed to be at least 37.
float형에서 베이스가 10일 때 표현할 수 있는 지수의 최댓값. 이것은 적어도 37이다.

DBL_MAX_10_EXP, LDBL_MAX_10_EXP

double 와 long double형엣 베이스가 10일 때 각각이 표현할 수 있는 지수의 최댓값.

FLT_MAX

이 매크로의 값은 float형이 표현할 수 있는 최대 수를 의미한다. 이것은 적어도 1E+37이 된다. 값 자체도 float형을 갖는다. 표현 가능한 가장 작은 수는 -FLT_MAX가 된다.

DBL_MAX, LDBL_MAX

doble 와 long double 형 각각이 표현할 수 있는 최대 수를 의미한다. 이 매크로 값이 가지는 형은 그것을 설명하는 형과 동일하다.

FLT_MIN

이 매크로의 값은 float형이 표현할 수 있는 표준화된 양의 플로팅 포인트 수의 최소값. 그 값은 1E-37보다 크지 않다.

DBL_MIN, LDBL_MIN

double 과 long double형 각각을 위한 것으로 표준화된 양의 플로팅 포인트수의 최소값. 매크로의 값 자체가 가지는 형은 그것이 설명하는 형과 동일하다.

FLT_EPSILON

1. 0 + FLT_EPSILON != 1. 0이 참인 float형의 플로팅 포인트 수의 최소 양의 수이다. 1E-5보다 크지 않다.

DBL_EPSILON, LDBL_EPSILON

double 와 long double를 위한 것으로 의미는 FLT_EPSILON과 같다. 이 매크로 값 자체가 가지는 형은 그것이 설명하는 형과 동일하다. 그 값은 1E-9보다 크지 않다.

 

A. 5. 3. 3 IEEE 플로팅 포인트

다음은 이진 플로팅 포인트 연산을 위해서 IEEE 표준에서(ANSI/IEEE Std 754-1985) 정한, 대부분의 일반 플로팅 포인트 표기에서 산출된 float형의 대부분의 매크로 값을 보여주고 있다. 1980년대 이후에 디자인된 대부분의 컴퓨터는 이 형식을 사용한다.

IEEE 단정도(single-precision) float 표기법은 베이스로 2를 사용한다. 그것은 23비트에 한 개의 숨겨진 비트를 더해서(그래서 총 정밀도는 베이스를 2로 했을 때 24가 된다. ) 부호 비트와 가수부를 나타내고, 8-비트 지수부는 -125에서 128까지의 범위에 있는 값을 표현할 수 있다. 다음은, float형 데이터를 이 표기법을 사용할 경우, 그것에 연관된 파라미터의 적당한 값을 나타내고 있다.

FLT_RADIX 2
FLT_MANT_DIG 24
FLT_DIG 6
FLT_MIN_EXP -125
FLT_MIN_10_EXP -37
FLT_MAX_EXP 128
FLT_MAX_10_EXP +38
FLT_MIN 1. 17549435E-38F
FLT_MAX 3. 40282347E+38F
FLT_EPSILON 1. 19209290E-07F

다음은 double 데이터 타입을 위한 값들이다.

DBL_MANT_DIG 53
DBL_DIG 15
DBL_MIN_EXP -1021
DBL_MIN_10_EXP -307
DBL_MAX_EXP 1024
DBL_MAX_10_EXP 308
DBL_MAX 1. 7976931348623157E+308
DBL_MIN 2. 2250738585072014E-308
DBL_EPSILON 2. 2204460492503131E-016

A. 5. 4 구조체 필드 옵셋 ( offset ) 측정

구조체 형안에서 특정한 구조체멤버의 위치를 계산하기 위해서는 offsetof를 사용할 수 있다.

매크로 : size_t offsetof (type, member)

이것은 구조체형을 가진 type안에 있는 member라는 이름을 가진 구조체 멤버의 옵셋(offset)을 구하는 정수 상수 표현식이다. 예를 들어, offsetof(struct s, elem)은 struct s라는 구조체 안에 있는 멤버 elem의 오프셋(offset)이 된다. 이 매크로는 만일 멤버가 비트 필드가 아니면 작업하지 않는다; 당신은 그 경우에 컴파일러로부터 에러를 얻게 될 것이다.


목차이전 :27. 시스템 구성 파라미터다음 :B. 라이브러리 기능들의 요약

and