'develop'에 해당되는 글 84건
- 2007.08.07 c언어 가변 인자 va_list
- 2007.07.05 공개 프로젝트 모음
- 2007.03.22 [gcc] 라이브러리 오브젝트에 정적으로 링크하기
- 2007.01.02 [network] 기본 네트워크 함수 라이브러리
- 2007.01.02 리틀 엔디안 & 빅 엔디안
- 2007.01.02 MS source safe 사용법
- 2006.12.19 mysql 설치 문제
- 2006.12.19 mysql 관리용 툴
- 2006.12.19 mysql 사용자 추가하기
- 2006.12.19 mysql 외부에서 접속하기
c언어 가변 인자 va_list
develop 2007. 8. 7. 09:47
C언어 가변 인자 ¶
printf()와 scanf()와 같은 가변 인자를 받는 함수를 만들거나 이 함수를 덮어 쓰는 wrapper를 만들려면 가변 인자를 처리할 줄 알아야 합니다.
가변 인자를 처리할려면 다음과 같은 data type과 함수 (또는 매크로 함수)를 써야 합니다:
- va_list -- type
- va_start(va_list argptr, last-arg) -- 가변 인자 처리 시작.
- va_end(va_list argptr) -- 가변 인자 처리 끝.
- T va_arg(va_list argptr, T) -- 가변 인자 얻기.
#include <stdarg.h>
int
sum(int nargs, ...)
{
va_list argptr;
int i, total = 0;
va_start(argptr, nargs); /* 가변 인자 처리 시작 */
for (i = 0; i < nargs; i++)
total += va_arg(argptr, int); /* 하나씩 가변 인자 얻기 */
va_end(argptr); /* 가변 인자 처리 끝 */
return total;
}
위와 같이 정의해 놓고 다음과 같이 쓸 수 있습니다 (주의: sum()의 첫번째 인자는 두번째부터 나올 가변 인자의 갯수입니다):
int sum(int nargs, ...);
int
main(void)
{
int s1, s2;
/* 1부터 5까지 더한 값을 s1에 저장. */
s1 = sum(5, 1, 2, 3, 4, 5);
/* 100, 200, 300을 더한 값을 s2에 저장. */
s2 = sum(3, 100, 200, 300);
/* ... */
return 0;
}
일단 가변 인자를 처리하기 위해서는 va_list type의 변수 하나를 선언해야 합니다:
va_list argptr;
그리고, 가변 인자를 처리할 부분 앞에 다음과 같이 써 줍니다:
va_start(argptr, nargs);
va_start()의 첫번째 인자는 아까 선언한 va_list
type의 변수 이름이며, 두번째 인자는 이 함수에서 가변 인자를 받는 부분의 바로 앞의 (고정된) argument입니다. 즉,
위에서 예로 든 sum()의 경우, 가변 인자를 나타내는 "..." 앞에 오는 argument인 nargs가 두번째 인자가
됩니다.
눈치가 빠른 분은 이미 알고 있겠지만, 가변 인자를 받는 함수는 여러 가지 제한이 붙습니다. 첫째,
va_start()에 가변 인자 바로 앞의 고정된 인자를 주어야 하기 때문에, 가변 인자를 받는 함수는 고정된 인자가 하나 이상
나와야 합니다. 즉, void foo(...)와 같은 함수는 만들 수 없습니다. 둘째, 가변 인자를 나타내는 "..."는 반드시
마지막 인자이어야 합니다. "..."가 중간이나 처음에 나올 수는 없습니다. 예를 들어 void bar(..., int a)와
같은 함수는 만들 수 없습니다.
실제 함수에 전달된 가변 인자를 얻기 위해서는 va_arg()를 불러서 처리합니다. va_arg()는 가변 인자로 전달된 값을 호출할 때마다 하나씩 얻어서 줍니다. va_arg()는 첫번째 인자로, va_start()로 초기화한 va_list
type을 받으며, 두번째 인자로는 가변 인자로 전달된 값의 type을 써 주어야 합니다. 우리가 만든 sum()의 경우,
함수의 목적이 모든 int를 더한 값을 계산하는 것이므로, va_arg()의 두번째 인자로 int를 주면 됩니다.
va_arg(argptr, int);
눈치가 더욱 빠른 분이면 아시겠지만, 여기서 가변 인자를 받는 함수에 또다른 제한이 붙습니다. 아시겠지만, 가변 인자로
전달되는 값의 타입에는 제한이 없습니다 (printf(), scanf()를 생각해보세요). 아쉽게도 va_arg()는 전달되는
값의 타입을 알아낼 방법이 없습니다. 따라서 미리 그 type을 알아서 va_arg()의 두번째 인자로 전달해 주어야 합니다.
게다가, 가변 인자를 받는 함수의 입장에서, 몇개의 가변 인자가 전달되었는지 알아낼 방법은 없습니다. 따라서 고정된
인자로 가변 인자의 갯수를 전달하는 등 (위의 sum()의 예처럼)의 방법으로 va_arg()를 몇번을 불러야 되는가를 알아낼
방법을 제공해야 합니다.
더 골치 아픈 것은 va_arg()의 두번째 인자로 쓸 수 있는 type에 제한이 있다는 것입니다. 이건 뒤에서 다루겠습니다.
이제 가변 인자를 처리하는 함수를 만들어 보았으니, printf()의 wrapper를 만들어 봅시다.
노파심에서 말씀드리지만, 다음과 같은 함수는 동작하지 않습니다:
int
my_printf(const char *fmt, ...)
{
return printf(fmt, ...);
}
일단 먼저, printf의 계열에는 다음과 같은 함수들이 있다는 것을
알아둡시다:
int printf(const char *fmt, ...);
int fprintf(FILE fp, const char *fmt, ...);
int sprintf(char *sr, const char *fmt, ...);
int snprintf(char *s, size_t n, const char *fmt, ...);
/* 이상 <stdio.h>에 선언되어 있는 함수들 */
int vprintf(const char *fmt, va_list ap);
int vfprintf(FILE fp, const char *fmt, va_list ap);
int vsprintf(char *sr, const char *fmt, va_list ap);
int vsnprintf(char *s, size_t n, const char *fmt, va_list ap);
/* 이상 <stdarg.h>에 선언되어 있는 함수들 */
잘 보면 알겠지만, <stdarg.h>에 있는 함수들은 <stdio.h>에 있는 printf() 계열 함수 이름 앞에 'v'가 붙고, "..." 대신에 va_list
type의 인자가 온다는 것을 알 수 있습니다.
printf() style의 문자열을 받으려면, 이 v*printf() 계열의 함수를
쓰면 편리합니다. 예를 들어 단순히 printf()와 똑같은 기능을 하는 wrapper는 다음과 같이 만들 수 있습니다:
int
my_printf(const char *fmt, ...)
{
va_list argptr;
int ret;
va_start(argptr, fmt);
ret = vprintf(fmt, argptr);
va_end(argptr);
return ret;
}
즉, 가변 인자 처리를 위한 va_list type 변수를 선언, va_start()로 초기화한 다음에 v*printf() 계열의 함수에 이 va_list type의 변수를 넘겨주기만 하면 됩니다. (va_end()를 불러 끝내는 것을 잊지 마세요!!)
좀더 복잡하게 하나 더 만들어 봅시다. 함수는 error()입니다. 이 함수는 주어진 printf() style의 에러
메시지를 받아서 stderr로 출력하고, 필요하면 errno의 내용도 알려주고, 필요하면 exit()를 불러서 프로그램을
종료하는 함수입니다. 선언은 다음과 같습니다:
void error(int status, int ecode,
const char *fmt, ...);
이 함수는 ecode가 0이 아닌 경우, strerror(ecode) 값을 출력하고, fmt와 "..."으로 전달된
printf() style의 에러 메시지도 출력한 다음, status가 0이 아닌 경우, exit(status)를 호출합니다.
함수 정의는 다음과 같습니다.
void
error(int status, int ecode,
const char *fmt, ...)
{
va_list argptr;
fflush(stdout);
fprintf(stderr, "error: ");
if (ecode)
fprintf(stderr, "%s: ", strerror(ecode));
va_start(argptr, fmt);
vfprintf(stderr, fmt, argptr);
va_end(argptr);
fputc('\n', stderr);
fflush(stderr); /* redundant */
if (status)
exit(status);
}
다음과 같이 쓸 수 있습니다.
#include <stdio.h>
#include <errno.h>
...
void
foo(const char *filename)
{
FILE *fp;
fp = fopen(filename, "r");
if (!fp)
error(1, errno, "cannot open file %s.", filename);
...
}
참고로 여기서 예로 만든 error()는 GNU C library (glibc)에 이미
포함되어 있습니다. 여기에서는 좀더 간략화해서 만들어 본 것이죠. 실제 여러분의 코드에 <error.h>를 포함시키면
위 error()를 정의할 필요없이 바로 쓸 수 있습니다. (출력 형태는 약간 다를 수 있습니다만, 인자 type이나 갯수는
같으니 똑같이 쓸 수 있습니다)
마지막으로 아까 va_arg()의 두번째 인자로 나올 수 있는 type에 제한이 있다고 했는데 그 얘기를 해보겠습니다. 결론부터 말하면, va_arg()의 두번째 인자로 나올 수 있는 type은 다음과 같습니다:
- int 계열 (int, unsigned int, signed int)
- long 계열 (long, unsigned long, signed long)
- double 계열 (double, long double)
- 포인터 계열 (모든 타입의 포인터, 예를 들어 void *, char *, 등등)
- char 계열 (char, unsigned char, signed char)
- short 계열 (short, unsigned short, signed short)
- float 계열 (float)
- char -> int (unsigned char는 unsigned int, 이런 식으로)
- short -> int (unsigned short는 unsigned short, 이런 식으로)
- float -> double
double bar();
double bar(a, f, ch)
int a;
float f;
char ch;
{
...
}
이런 식으로 함수를 선언하게 되면, 컴파일러가 함수의 정의를 보기 전에는 이 함수에 전달되는 인자의 타입이 뭔지 알 수
없습니다. 만약 컴파일러가 bar()의 정의를 보기 전에 다음과 같은 코드를 컴파일해야 한다고 가정해 보기 바랍니다:
void
foo(void)
{
bar(4, 2.5, 9);
}
일단 bar()의 인자가 3개인데,, "void bar();"만 보고서는 인자가 적절하게 전달되었는지 알 방법이 없습니다. 따라서 인자 갯수 체크를 할 수 없습니다.
둘째로, 각각의 인자가 알맞는 타입인지도 알 수 없습니다. 함수 호출을 위해서는 (stack에 인자를 push해야
하는데), 인자의 type을 알 수 없으니, 첫번째 인자인 4를 push할 때, 8 bit로 4를 push할 것인지, 16
bit로 4를 push할 것인지, 32 bit로 할 것인지 알 수가 없습니다.
따라서 C 언어에는 "integral promotion"이란 용어? 개념?이 있고, 여기에 따라서 모든 인자는 다음과 같은 type으로 변환해서 stack에 push합니다.
- char, short -> int
- float -> double
- int -> int (그대로)
- long -> long (그대로)
- double -> double (그대로)
- long double -> long double (그대로)
- 모든 pointer type -> 모든 pointer type (그대로)
지금까지는 ANSI C에서 function prototype이 나오기 전의 old style (K&R) C에
필요한 내용입니다. 자, ANSI C에서는 function prototype이 있으니, 컴파일러가 함수 호출을 하기 전에 함수의
인자 갯수와 타입을 모두 알 수 있습니다.
double bar(int a, float f, char ch);
따라서 더 이상 이러한 변환이 필요없게 되었지만, 하나 예외가 있는데, 바로 가변 인자를 처리하는 함수입니다. 가변 인자는 실제로 어떠한 타입이 몇개가 들어올 지 알 수 없기 때문에, 이러한 변환이 계속 수행됩니다.
따라서 va_arg()로 이러한 타입을 받을려면 이 integral conversion을 미리 생각해 두어야 합니다.
예를 들어 문자 하나씩 받아서 출력하는 함수를 만들어 보겠습니다:
void
putchars(int nargs, ...)
{
va_list argptr;
char ch;
int i;
va_starg(argptr, nargs);
for (i = 0; i < nargs; i++) {
ch = (char) va_arg(argptr, int);
putchar(ch);
}
va_end(argptr);
}
즉, 들어올 가변 인자가 char type일지라도 va_arg()에서는 integral promotion에 따라 int type으로 받아서 처리해야 합니다.
잘 생각해 보면, 이미 여기에 대해 알고 있었을 것입니다. printf()와 scanf()를 생각해봅시다.
printf에서 float이나 double을 출력하기 위해서는 "%f"를 씁니다.
그런데, scanf에서는 float은 "%f", double은 "%lf"를 씁니다.
아래 예 참고:
double d;
float f;
printf("%f", d);
printf("%f", f);
scanf("%f", &f);
scanf("%lf", &d);
그 이유는, integral promotino에 의해 printf의 경우, float을
double로 받기 때문입니다. 따라서 float과 double에 관한 차이가 없습니다. 그러나 scanf의 경우에는 모든
인자가 pointer type이기 때문에 이러한 변환이 필요없습니다. 즉, 모든 타입을 각각 구별해야 하는 것이죠.
분명 printf 코드안에서 float을 출력하는 부분은 다음과 같은 코드를 포함합니다:
value = (float) va_arg(argptr, double);
또한 scanf 코드 안에서 float을 출력하는 부분은 다음과 같은 코드를 포함합니다:
ptr = va_arg(argptr, float *);
이 정도면 가변 인자를 처리하는 방법은 완벽히! 익히셨을 것입니다. 그럼 다음 기회에...
끝 -- cinsk
- TODO: Function prototype이 ANSI C에서 소개된 것 맞나?
- TODO: Integral Promotion이라고 했는데, 여기에 float, double이 포함되는 것이 확실한가?
함수 선언(정의가 아닌)의 경우에는 그 전에도 있었던 것 같습니다.
http://www.ifi.uio.no/forskning/grupper/dsb/Programvare/Xite/ProgrammersManual/node11.html#SECTION000111000000000000000
그러나 매개변수의 타입들을 함께 적어주는 function prototype은 ANSI C에서 새로이 나온 것인 듯 합니다.
![[http]](http://wiki.kldp.org/imgs/http.png)
가변인자 함수에서 적용되는 것은 Integral Promotion을 포함하는 Default argumnt promotion입니다.
C99 6.5.2.2 Function calls 중에서...
If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions.
그리고 표준문서에서는 Integer promotion이라는 용어를 씁니다.
Integer promotion은 이름 그대로 정수형의 승급만을 의미합니다. double이나 포인터는 포함되지 않습니다.
int형 또는 unsigned int형보다 rank가 낮은 (즉 표현범위가 좁은) 정수형에 한해서 int 형 또는 unsigned 형으로
승급됩니다. 해당되지 않는 데이터 형들은 그대로 남습니다.
6.3.1.1 Boolean, characters, and integers 중에서...
The following may be used in an expression wherever an int or unsigned int may
be used:
- An object or expression with an integer type whose integer conversion rank is less than the rank of int and unsigned int.
- A bit-field of type _Bool, int, signed int, or unsigned int.
[edit]
가변 인자 매크로 ¶
ISO C 표준을 지원하는 컴파일러에서는 가변 인자를 처리하는 매크로도 정의할 수 있습니다.
함수 형태의 매크로를 정의할 때, "..."을 써 주고, 매크로 정의 부분에서 VA_ARGS를
써주면 그 자리에 확장됩니다. 예를 들면, 다음과 같습니다:
#define debug(s, ...) fprintf(stderr, s, __VA_ARGS__)
void
foo(void)
{
debug("Entered the function, %s\n", __func__);
/* ... */
}
간단하게 printf 형태의 메시지를 입력받은 표준 함수 assert()와 비슷한 매크로 함수를
만들어 봅시다; 함수 이름은 ASSERT()로 하겠습니다. ASSERT()의 첫 인자는, assert()의
인자와 같습니다. 즉 거짓인 경우, abort()를 부릅니다. ASSERT()의 두번째 인자부터는
printf()와 같습니다. 또한 NDEBUG 매크로가 정의된 경우, ASSERT()는 아무런 영향을 주지
않습니다.
일단, ASSERT()가 쓰인 시점의 파일 이름, 줄 번호, 그리고 함수 이름을 자동으로 출력하게
합시다. 따라서 미리 정의된 매크로인 FILE과 LINE을 쓰며, 미리 정의된 이름,
func를 씁니다.
아래에 ASSERT()의 정의가 나갑니다:
#ifndef NDEBUG
# define ASSERT(exp, s, ...) assert_(__FILE__, __LINE__, __func__, \
#exp, s, __VA_ARGS__)
#else
# define ASSERT(exp, s, ...) ((void)0)
#endif /* NDEBUG */
간단합니다. NDEBUG가 정의된 경우에는 ASSERT()가 assert_()를 호출합니다.
assert_()의 처음 세 인자는 ASSERT()를 부른 곳의 파일 이름, 줄 번호, 함수 이름을
자동으로 전달하게 됩니다. 다음으로 ASSERT()의 첫 인자를 문자열로 바꾸어 전달하며(#exp),
그 다음으로, printf() 스타일의 format string인 s가 들어오며,
그 뒤에 가변 인자가 들어옵니다.
이제 assert_()의 정의가 나갑니다:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
void
assert_(const char *filename, int lineno, const char *func,
const char *exp, const char *fmt, ...)
{
va_list argptr;
fflush(stdout);
fprintf(stderr, "%s:%d: assertion, (%s) failed in function, %s\n",
filename, lineno, exp, func);
fprintf(stderr, "\treason: ");
va_start(argptr, fmt);
vfprintf(stderr, fmt, argptr);
va_end(argptr);
putc('\n', stderr);
abort();
}
예를 들어 다음 함수를 생각해 봅시다:
MIN_AGE는 10으로 정의된 매크로이고, 사용자가 set_age(3)을 호출했다면,
다음과 같은 ASSERTion이 발생합니다:
void
set_age(int age)
{
ASSERT(age > MIN_AGE, "age should be larger than %d.", MIN_AGE);
/* ... */
}
tmp.c:36: assertion, (age > minimum) failed in function, set_age
reason: age should be larger than 10.
Aborted
자, 그럭 저럭 ASSERT() 쓸만하죠? assert()보다는 좀 나을 것 같습니다.
참고: ISO C 표준 6.4.2.2, 6.10.3
'develop' 카테고리의 다른 글
[펌]MinGw, wxWidget, Code:Blocks를 이용한 C/C++ IDE 환경 구축 (0) | 2007.11.01 |
---|---|
readelf - 직접 ELF를 읽기 위한 툴 (0) | 2007.08.08 |
공개 프로젝트 모음 (0) | 2007.07.05 |
[gcc] 라이브러리 오브젝트에 정적으로 링크하기 (0) | 2007.03.22 |
[network] 기본 네트워크 함수 라이브러리 (0) | 2007.01.02 |
공개 프로젝트 모음
develop 2007. 7. 5. 14:23
|
'develop' 카테고리의 다른 글
readelf - 직접 ELF를 읽기 위한 툴 (0) | 2007.08.08 |
---|---|
c언어 가변 인자 va_list (0) | 2007.08.07 |
[gcc] 라이브러리 오브젝트에 정적으로 링크하기 (0) | 2007.03.22 |
[network] 기본 네트워크 함수 라이브러리 (0) | 2007.01.02 |
리틀 엔디안 & 빅 엔디안 (0) | 2007.01.02 |
[gcc] 라이브러리 오브젝트에 정적으로 링크하기
develop 2007. 3. 22. 14:50
gcc 옵션 "-static"을 사용하면, 비록 라이브러리가 없는 환경에서 실행한다고 해도 공유라이브러리 에러가 나지 않는다.
'develop' 카테고리의 다른 글
c언어 가변 인자 va_list (0) | 2007.08.07 |
---|---|
공개 프로젝트 모음 (0) | 2007.07.05 |
[network] 기본 네트워크 함수 라이브러리 (0) | 2007.01.02 |
리틀 엔디안 & 빅 엔디안 (0) | 2007.01.02 |
MS source safe 사용법 (0) | 2007.01.02 |
[network] 기본 네트워크 함수 라이브러리
develop 2007. 1. 2. 16:09
[listen]
int listen(int socket_fd/**<소켓의 파일 디스크립터*/, int queue_size/**<연결요청 대기 큐의 크기 지정*/);
@brief : 서버가 "연결 요청 대기 상태"로 들어갈 수 있게 도와준다.
(=> 서버가 대기 상태로 들어가야지만, 클라이언트가 connect를 해도 에러가 생기지 않는다.)
@return : 성공시 0, 실패 시 -1
[accept]
int accept(int socket_fd/**<서버 소켓의 파일 디스크립터*/,
struct sockaddr *addr/**<클라이언트의 주소정보를 저장할 변수의 포인터*/,
int *addrlen/**<addr 포인터가 가리키는 구조체의 크기를 저장하고 있는 변수의 포인터*/);
@brief : 연결 요청 대기 큐에 대기 중인 클라이언트의 연결 요청을 수락하는 함수
@return : 성공 시 새로운 소켓의 파일 디스크립터, 실패 시 : -1
int listen(int socket_fd/**<소켓의 파일 디스크립터*/, int queue_size/**<연결요청 대기 큐의 크기 지정*/);
@brief : 서버가 "연결 요청 대기 상태"로 들어갈 수 있게 도와준다.
(=> 서버가 대기 상태로 들어가야지만, 클라이언트가 connect를 해도 에러가 생기지 않는다.)
@return : 성공시 0, 실패 시 -1
[accept]
int accept(int socket_fd/**<서버 소켓의 파일 디스크립터*/,
struct sockaddr *addr/**<클라이언트의 주소정보를 저장할 변수의 포인터*/,
int *addrlen/**<addr 포인터가 가리키는 구조체의 크기를 저장하고 있는 변수의 포인터*/);
@brief : 연결 요청 대기 큐에 대기 중인 클라이언트의 연결 요청을 수락하는 함수
@return : 성공 시 새로운 소켓의 파일 디스크립터, 실패 시 : -1
'develop' 카테고리의 다른 글
c언어 가변 인자 va_list (0) | 2007.08.07 |
---|---|
공개 프로젝트 모음 (0) | 2007.07.05 |
[gcc] 라이브러리 오브젝트에 정적으로 링크하기 (0) | 2007.03.22 |
리틀 엔디안 & 빅 엔디안 (0) | 2007.01.02 |
MS source safe 사용법 (0) | 2007.01.02 |
리틀 엔디안 & 빅 엔디안
develop 2007. 1. 2. 15:34
엔디안
모든 플랫폼(유닉스, 리눅스, 윈도우)에서 공통적으로 binary파일을 읽고 쓰고 하고 싶습니다. 즉, double, int, char 등으로 이루어진 binary 파일을 여러가지 플랫폼에서 작성하고 그 파일을 window에서 읽을 수 있게 하고 싶습니다. 그런데 리틀엔디안, 빅엔디안 이란 것때문에 그게 쉽지만은 않은 작업같더군요. 1) 각 시스템에 해당 시스템이 어떤 엔디안인지 알 수 있는 API함수같은 것이 있는지요? 2) 제가 간단히 테스트해본 바로는 int나 캐릭터는 리틀엔디안/빅엔디안의 영향을 받지만 double은 상관없는 것 같더군요. (자신은 없습니다. -_-; 한달쯤 전에 아주 간단히만 체크해본 거라서.) 어떤 데이터 타입이 리틀/빅엔디안의 영향을 받는 지 알 수 있을까요? 3) 제가 생각하는 것은 예를 들어 무조건 '리틀엔디안'으로만 파일에 쓰이도록 하는 방식을 생각하고 있습니다. 혹시 더 좋은 방법이 있을런지요? 4) 더불어 예전에 올렸던 질문글에서 한 분이 다음과 같은 방식을 알려주시더군요. 다음 매크로를 쓰려고 합니다.
1. 어떤 엔디안인지 파악하는 방법은
(1) 컴파일러 또는 시스템 고유의 매크로가 정의되어있는지 판단하는 방법 : 예를 들어 __sparc__ 가 정의되어 있다면 big endian (2) 실시간에 판단하는 방법 :
2. 2바이트 이상의 값이면 모두 엔디안의 영향을 받습니다. 그러므로, double 도 예외가 아닙니다. 3. 무조건 고정된 엔디안을 사용하는 것보다는 선택할 수 있게 하는 게 더 좋겠죠. 제 경우는 읽어올 때 : 입력측 엔디안을 지정해서 시스템 엔디안으로 변경 쓸 때 : 출력측 엔디안을 지정해서 시스템 엔디안을 출력측 엔디안으로 변경 하는 입출력 함수를 새로 정의했습니다. coral.kldp.net 라이브러리에 정의되어 있는 함수입니다. 4. 값을 뒤집는 방법은 효율적으로 잘 뒤집으면 됩니다. 그리고 현존하는 기계들의 엔디안에는 little, big, pdp 엔디안이 있습니다. pdp 엔디안은 잘 쓰이지는 않지요. |
'develop' 카테고리의 다른 글
c언어 가변 인자 va_list (0) | 2007.08.07 |
---|---|
공개 프로젝트 모음 (0) | 2007.07.05 |
[gcc] 라이브러리 오브젝트에 정적으로 링크하기 (0) | 2007.03.22 |
[network] 기본 네트워크 함수 라이브러리 (0) | 2007.01.02 |
MS source safe 사용법 (0) | 2007.01.02 |
MS source safe 사용법
develop 2007. 1. 2. 15:12
[펀글- MS Source Safe]
sourcesafe(이하 소세) 사용법에 대해서 간단하게 말씀드리죠.
저도 프로젝트를 수행하면서 소세를 사용하였는데 좀 까다롭더라구요.
저도 프로젝트를 수행하면서 소세를 사용하였는데 좀 까다롭더라구요.
정식으로 메뉴얼을 보고 익힌것이 아니라 시행착오를 여러번 거치면서 배웠지요
먼저, 서버에 프로젝트 소스를 데이타베이스로 만들어야 됩니다.
하나의 서버를 설정하여야 되고 다른 조원들의 컴퓨터가 이 서버에 물려야 되죠(당연하죠?)
먼저, 서버에 프로젝트 소스를 데이타베이스로 만들어야 됩니다.
하나의 서버를 설정하여야 되고 다른 조원들의 컴퓨터가 이 서버에 물려야 되죠(당연하죠?)
1. 그 서버용 컴에서 시작>프로그램>MS Visual Studio>MS Visual
Sourcesafe>Visual sourcesafe 6.0 Admin을 실행하여 물릴 소스 파일의 디비를 설정합니다
2. 메뉴를 보시면 'Tools'밑에 'Create Database...'가 있는데 실행합니다.
3. 'Create New VSS Database'라는 대화창이 뜨면 'Browse..'버튼을 눌러 소스가 있는 폴더를
지정
그러면 디비로 자동 물립니다. 이때 그 소스폴더에는 'data','temp', 'users'폴더와 함께 'users.txt'그리고 가장 중요한 'srcsafe.ini'이 생성됩니다.
그러면 디비로 자동 물립니다. 이때 그 소스폴더에는 'data','temp', 'users'폴더와 함께 'users.txt'그리고 가장 중요한 'srcsafe.ini'이 생성됩니다.
다음으로 프로젝트에 참가하는 조원을 서버에 계정시킵니다.
4. 메뉴에서 'users'에 'Open Sourcesafe Database'를 실행 아마 방금전에 등록시킨 데이타베이스가
없을것입니다. 그럼 찾아줘야지요.
5. Open Sourcesafe Database 대화창에서 'Browse...'버튼을 눌러서 방금전에 등록한 폴더에 생성된
'srcsafe.ini'를 찾아 엽니다.
6. 그러면 디비 페스와 그 디비 이름란의 대화상자가 나오는데 앞으로 계속 사용될 디비의 이름을
적당히 줍니다. 자동설정값으론 그 폴더이름이 되어있을 것입니다. 아무렇게나 주십시오.
확인키를 누르면 다시 Open Sourcesafe Database 대화창으로 가고 그곳에 지금 방금 등록한 디비가
생성됩니다.
적당히 줍니다. 자동설정값으론 그 폴더이름이 되어있을 것입니다. 아무렇게나 주십시오.
확인키를 누르면 다시 Open Sourcesafe Database 대화창으로 가고 그곳에 지금 방금 등록한 디비가
생성됩니다.
바로 그 디비를 열어주면 현재 관리자(Admin)의 비밀번호가 없다고 하는 메세지가 나오고
확인키를 누르면 O.K.!
확인키를 누르면 O.K.!
7. Admin의 패스워드와 소세를 사용할 사람들에게 ID와 Password를 주면 등록끝!!!
이제 막바로 소세를 이용하여 프로그래밍을 하면 되냐고요? 아닙니다.
다음으로 소스폴더를 디비로 물리면 그 디비폴더에 함께 사용할 소스를 등록시켜야 됩니다.
다음으로 소스폴더를 디비로 물리면 그 디비폴더에 함께 사용할 소스를 등록시켜야 됩니다.
8. 시작>프로그램>MS Visual Studio>MS Visual Sourcesafe>Microsoft
Visual Souresafe 6.0을 실행
9. Visual SourceSafe Explorer 가 실행되고 윈타이틀 옆에 현재 Open되어 있는 디비가 나오는데
사용할 디비를 Open합니다. (아마 이 순서로 하면 자동으로 오픈되어 있을꺼예요)
사용할 디비를 Open합니다. (아마 이 순서로 하면 자동으로 오픈되어 있을꺼예요)
10. 메뉴의 File>Create Project..를 실행하고 폴더명(예로 chichi)을 주고 그 폴더에서 사용할 소스를
File>Add Files..로 등록합니다.
File>Add Files..로 등록합니다.
res폴더 또한 소세에 물려서 사용하려면 생성된 폴더(chichi) 밑에 다시 res폴더를 만들어 주고 그 폴더에도 사용할 resource파일을 Add Files..로 등록합니다.
이렇게 하면 소세를 이용한 프로그래밍의 setup작업 끝!!!
이젠 실제로 하나의 소스를 이용하여 프로그래밍하는 방법을 설명하겠습니다.
11. VC++를 실행하고 File>Open Workspace..를 실행하면 대화창에 'Source Cotrol'키
누름
12. Visual SourceSafe Login 창이 나오면서 등록된 사용자의 ID와 Password를 입력합니다.
물론 Database는 전에 설정한 디비를 선택하셔야 되겠죠? 한번에 여러개의 프로젝트를 하면 분명
여러개의 디비가 있을것입니다. 그러니 사용하고자 하는 디비를 잘 선택하여야 됩니다.
물론 Database는 전에 설정한 디비를 선택하셔야 되겠죠? 한번에 여러개의 프로젝트를 하면 분명
여러개의 디비가 있을것입니다. 그러니 사용하고자 하는 디비를 잘 선택하여야 됩니다.
13. Create local project from SourceSafe창이 나오면 로컬컴에 작업할 소스를 서버에서 다운로드하여야
하므로 작업할 폴더를 설정합니다.
그러면 서버에 있는 소스가 그 폴더로 옮겨 오게 됩니다.
14. 그러면 Open Workspace창이 다시 나와 서버의 xxxxx.dsw를 오픈하면 된다.
간혹 Class View가 에러가 날수 있다. 이유는 다운받은 소스가 읽기전용이라 그럴것입니다.
그러니 소스 모두 등록정보에서 읽기전용의 체크마크을 없애주길 바랍니다.
간혹 Class View가 에러가 날수 있다. 이유는 다운받은 소스가 읽기전용이라 그럴것입니다.
그러니 소스 모두 등록정보에서 읽기전용의 체크마크을 없애주길 바랍니다.
제대로 오픈이 되었을텐데 역시 속도가 많이 느릴것입니다.
소세를 사용할때는 주의하여야 할 몇가지가 있습니다.
먼저 계속해서 소스를 최신버전으로 바꿔야됩니다.
소세를 사용할때는 주의하여야 할 몇가지가 있습니다.
먼저 계속해서 소스를 최신버전으로 바꿔야됩니다.
VC++ 메뉴에서 project>Source Control..>Get Latest Version으로 최신버전을 받고
한사람이 소스의 한 부분을 고치려면 서버에 체크아웃(Check Out)을 요청해서 코딩하고 작업을 끝마치면 반드시 서버에 자신이 손댄 부분을 체크인(Check In) 해주어야 됩니다.
(project>Source Control..>Check Out & project>Source Control..>Check In)
한사람이 체크아웃해서 변경중인 코드는 다른사람의 접근이 불가하게 됩니다.
계속해서 체크인, 체크아웃을 물어보는 대화창이 나오므로 무시하지 말고 자세히 드려다 보고
시키는데로 따라하면 별 문제가 없을 것입니다.
참고로 resource 부분을 무시하지 말길 바랍니다.
리소스를 건드렸다가 체크인을 하지 않아서 고치기 전의 기존파일로 다시 엎어쓴 경험이 있습니다.
소세는 편리함을 주기위해 만들어졌는데 사용미숙으로 많은 낭패를 보는 프로그램 중 하나입니다.
그렇다고 프로젝트를 클래스별로 나눠서 코딩하고 나중에 합쳐 사용하면 되겠지만 리소스에서
충돌이 나는 경우가 종종있어 그것도 유의하여야 합니다.
소세는 편리함을 주기위해 만들어졌는데 사용미숙으로 많은 낭패를 보는 프로그램 중 하나입니다.
그렇다고 프로젝트를 클래스별로 나눠서 코딩하고 나중에 합쳐 사용하면 되겠지만 리소스에서
충돌이 나는 경우가 종종있어 그것도 유의하여야 합니다.
이 메뉴얼은 기존의 메뉴얼의 도움없이 많은 시행착오로 얻은 지식을 적어놓은 것이라 틀린 부분이
있을 수 있습니다. 그러니 틀린부분이나 도움이 될만한 내용이 있으면 연락 주시기 바랍니다.
지식공유가 발전된 한국을 만듭니다.
'develop' 카테고리의 다른 글
c언어 가변 인자 va_list (0) | 2007.08.07 |
---|---|
공개 프로젝트 모음 (0) | 2007.07.05 |
[gcc] 라이브러리 오브젝트에 정적으로 링크하기 (0) | 2007.03.22 |
[network] 기본 네트워크 함수 라이브러리 (0) | 2007.01.02 |
리틀 엔디안 & 빅 엔디안 (0) | 2007.01.02 |
mysql 설치 문제
develop/sql 2006. 12. 19. 15:42
|
'develop > sql' 카테고리의 다른 글
mysql 관리용 툴 (0) | 2006.12.19 |
---|---|
mysql 사용자 추가하기 (0) | 2006.12.19 |
mysql 외부에서 접속하기 (0) | 2006.12.19 |
mysql 새로운 사용자 계정 설정법 (0) | 2006.12.19 |
mysql 관리용 툴
develop/sql 2006. 12. 19. 15:42
|
'develop > sql' 카테고리의 다른 글
mysql 설치 문제 (0) | 2006.12.19 |
---|---|
mysql 사용자 추가하기 (0) | 2006.12.19 |
mysql 외부에서 접속하기 (0) | 2006.12.19 |
mysql 새로운 사용자 계정 설정법 (0) | 2006.12.19 |
mysql 사용자 추가하기
develop/sql 2006. 12. 19. 15:40
|
'develop > sql' 카테고리의 다른 글
mysql 설치 문제 (0) | 2006.12.19 |
---|---|
mysql 관리용 툴 (0) | 2006.12.19 |
mysql 외부에서 접속하기 (0) | 2006.12.19 |
mysql 새로운 사용자 계정 설정법 (0) | 2006.12.19 |
mysql 외부에서 접속하기
develop/sql 2006. 12. 19. 15:40
|
'develop > sql' 카테고리의 다른 글
mysql 설치 문제 (0) | 2006.12.19 |
---|---|
mysql 관리용 툴 (0) | 2006.12.19 |
mysql 사용자 추가하기 (0) | 2006.12.19 |
mysql 새로운 사용자 계정 설정법 (0) | 2006.12.19 |