[C언어] remove() , rename()
COM2008. 2. 15. 01:06 |remove() 는 파일을 삭제한다.
int remove ( const char * )
rename() 는 파일명을 변경한다.
int rename ( const char * , const char * )
앞쪽의 인자는 변경할 파일이고, 두번재 인자는 바꿔줄이름이다.
remove() 는 파일을 삭제한다.
int remove ( const char * )
rename() 는 파일명을 변경한다.
int rename ( const char * , const char * )
앞쪽의 인자는 변경할 파일이고, 두번재 인자는 바꿔줄이름이다.
fread() 와 fwrite() 는 이진파일을 읽고 쓰기 위해 사용된다. 이때 변환은 수행되지 않는다. 포함된 헤더파일은 stdio.h 이다.
fwite()
프로토타입은 다음과 같다.
size_t fwrite( const void* , size_t , size_t , FILE* );
( size_t 형은 보통 4바이트 워드 컴퓨터에서 unsinged 이다. )
사용구문
fwrite( ptr , el_size , el_number , fp );
ptr 의 주소로가서, el_size 바이트를 단위로 해서, el_number 개 만큼 읽어서 fp 에 쓴다.
리턴값은 성공적으로 기록한 원소의 수이다.
fread()
프로토타입은 다음과 같다.
size_t fread( void* , size_t , size_t , FILE* );
사용구문
fread( ptr , el_size , el_number , fp );
인자는 fwrite() 와 동일하다. 대신 반대로 fp 에서 el_size 단위로, el_number 읽어서, ptr 에 넣는다.
fprintf()
fprintf() 는 출력(print)함수이다. 첫번째 f 의 의미는 file 이고, 마지막 f의 의미는 formatted 이다.
conversion specification (또는 format) 을 통해 출력을 제어할 수 있다. 이러한 문자열을 제어 문자열이라고 한다. 또 이함수는 임의의 개수의 인자를 출력할수 있다.
다음은 fprintf() 의 함수 프로토타입이다.
int fprintf( FILE* , const char* , ... );
리턴값은 출력된 문자의 수이다. 에러시 EOF 를 리턴한다.
구문형식은 다음과 같다.
fprintf ( file_pointer , control_string , other_arguments ) ;
첫번째 파라미터인 파일포인터는 출력장소를 가리킨다. 출력방식은 텍스트 형식이다.
예. fprintf( fp, "She sells %d %s for $%f", 99, "sea shells", 3.77);
여기서 control_string 은 "She sells %d %s for $%f" 이고,
other_arguments 는 99, "sea shells", 3.77이다.
ohter_arguments의 수식은 평가되고, 제어 문자열의 대응되는 변환명세에 따라 변환되어 출력스트림에 놓여진다.
% 기호는 변환 명세의 시작을 나타내고, 변환문자로 끝난다. %와 변환 문자 사이에는 그밖에 부가적인 정보들이 올 수 있다.
fprintf() 의 변환문자
c char 형
d, i int 형 , 10진수
u unsigned 형, 10진수
o unsigned 형, 8진수
x, X unsigned 형, 16진수
e, E 지수형 e 또는 E
f float 형
g, G e 형식과 f 형식중 또는 E 형식과 f 형식중 짧은 쪽
s 문자열
p void* 형, 16진수
n 정수형 포인터, 현재까지 출력된 문자의 개수.
% %%형식으로 %을 출력스트림에 쓴다. 대응인자 없음
변환명세의 시작인 % 와 끝인 변환문자 사이에 다음과 같은 것들이 순서대로 올 수 있다.
flag 문자
- 좌측정렬, -가 없다면 디폴트는 우측임
+ singned 변환에서, 음이 아닌수 앞에 + 기호 붙임
공백 signed 변환에서, 음아닌 수앞에 공백을 붙임, + 플래그와 동시에 사용되면 공백플래그는 무시됨
# 변환문자에 따라 선택적인 형태로 변환시킨다.
#o(8진수) : 앞에 0 을 붙인다.
#x(16진수) : 앞에 0x 를 붙인다.
#g, 또는 #G : 소숫점 뒤에 0 들을 출력시킨다.
0 공백대신 0 으로 채운다. 0x 나 0X 는 이러한 0 앞에 출력된다.
* 필드폭과 정밀도를 대응인자로 받을 수 있다.
*.* 라고 하면, 첫번째 * 플래그 는 필드폭과 정렬을 받는다. 음수가 오면 - 플래그를 받은것과 같다.
두번째 * 플래그는 정밀도이다. 양수가 입력되어야 하고, 음수이면 정밀도를 명시하지 않은것과 같다.
예. fprintf(fp,"%*.*d",-4,10,n); 라고 하면, 이것은 fprintf(fp,"%-4.10d",n); 와 같다.
field width (필드폭)
필드폭은 양의 정수로 입력한다.
precision (정밀도)
정밀도는 점(.) 과 양의정수로 입력된다.
정수형 변환에서는, 최대자릿수이고,
실수형 변환에서는, 소숫점 이하 자리수를 표시한다
h 또는 l 또는 L
h는 short , l 은 long , L 도 long 인데 실수형에만 쓰인다.
참고로 long float 는 double 을 나타냄
----------------------------------------------------------------------------------------------------
fscanf()
fscanf()는 fprintf() 에 대응되는 입력함수로, 상당히 유사하다.
다음은 fscanf() 의 함수 프로토타입이다.
int fscanf( FILE* , const char* , ... );
리턴값은 성공적으로 입력받은 개수이다. 파일 끝을 만나면 EOF 를 리턴한다.
구문형식은 다음과 같다.
fscanf ( file_pointer , control_string , other_arguments ) ;
변환명세의 시작은 fprintf() 와 마찬가지로 %로 시작한다. 이때, 변환명세의 대응인자들은 포인터가 온다.
fscanf() 의 변환문자
c char 형
d, i int 형, 10진수
u unsigned 형, 10진수
o unsigned 형, 8진수
x, X unsigned 형, 16진수
f float 형, e,E,g,G 도 마찬가지
s 공백없는문자열, 공백이 있으면 멈추고, 공백전까지 끊어서 한 문자열로 받음
단, 처음에 나오는 공백은 무시한다. 즉, 시작은 공백이 아닌 문자가 나올때 부터 시작한다.
p void* 형, 보통 부호없는 16진수로 받음
n Nothing is expected; instead, the number of characters consumed thus far from the input
is stored through the next pointer, which must be a pointer to int.
This is not a conversion, although it can be suppressed with the * flag.
The C standard says: `Execution of a %n directive does not increment the assignment
count returned at the completion of execution' but the Corrigendum seems to contradict this.
Probably it is wise not to make any assumptions on the effect of %n conversions
on the return value.
%n 은 입력스트림으로 부터 받는것이 아니라, 지금까지 읽어드린 문자의 개수를 저장한다.
대응되는 인자는 정수형 포인터이다.
( scanf 의 인자는 어차피 포인터이니, 실제적으로는 정수형 변수와 대응된다고 생각하면 된다. )
% 입력스트림에서 하나의 %와 짝을 이룬다. 대응인자는 없다.
[...] 조건 문자열.
%[ ] 에서, [ ] 안에 들어가는 문자들을 scan set 이라고 한다. 스캔 집합 내의 문자들만 받아들인다.
반대로, ^ 로 시작하면, 스캔 집합 내의 문자들 외의 문자들만 받아들인다. 이 규칙을 벗어나면 멈추고
그 전까지 끊어서 문자열을 받는다.
가령 %[AB \n\t] 라고 하면, 'A', 'B', 공백, 줄바꿈, 탭 만 받아들이고, 이를 어겨야 중단된다.
사용 가능한 flag들. %와 변환문자 사이에 온다.
h 또는 l 또는 L h 는 short 를 l 과 L 은 long 인데, L은 실수형에만 가능하다.
scan filed 폭 %와 변환문자 사이에 양의 정수를 넣으면, 최대스캔폭을 지정할 수 있다.
폭을 넘어가는 입력은 무시한다.
* 입력무시
예. scanf("%*d%s",str);
제어문자열이 포함하는 문자.
여백 입력스트림에 있을수 있는 공백과 대응된다.
문자 ( 공백아니고, %도 아닌) 입력스트림의 문자와 대응되어야 한다.
----------------------------------------------------------------------------------------------------
stdio.h 에는 다음과 같은 파일 포인터가 정의되어있다. 모두 FILE* 형이다.
stdout 표준출력 화면으로 연결됨
stdin 표준입력 키보드로 연결됨
stderr 표준에러 화면으로 연결됨
따라서, fprintf( stdout , ... , ... ) 으로 하면 화면에 찍는다. 이것은 printf() 와 같다.
printf()
printf( ... ) = fprintf( stdout , ... )
마찬가지로, fscanf( stdin , ... , ... ) 으로 하면 키보드로 부터 입력을 받는다. 이것은 scanf() 와 같다.
scanf()
scanf( ... ) = fscanf( stdin , ... )
----------------------------------------------------------------------------------------------------
파일을 입출력 받을 장소가 문자열일때는 sprintf() 와 sscanf() 쓸 수 있다.
sprintf()
프로토타입은 다음과 같다.
int sprintf( char * , const char * , ... ) ;
첫번째 인자가 가리키는 문자열에 쓴다.
scanf()
프로토타입은 다음과 같다.
int sscanf( const char* , const char * , ... );
첫번째 인자가 가리키는 문자열로 부터 입력을 받는다.
fopen() 이나 fclose() 로 파일을 열고 닫을때, 꼭 등장하는 녀석이 바로 FILE 형 포인터 일 것이다. 대문자로 쓴것에서도 냄새가 물씬 풍기듯이 keyword 가 아니고, stdio.h 에 정의되어있는 구조체이다. 구조체는 데이터형을 묶어놓은 패키지로 대상의 속성에 맞게 세팅해서 사용하는 것이라고 할 수 있겠다. 구조체는 나중에 살펴보도록 하고...
FILE 은 stdio.h 에 정의되어 있는데, 기본적으로 machine dependent 이다.
struct _iobuf {
char *_ptr ;
int _cnt ;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
위의 상황에서는 FILE은 typedef 로 이름만 다시 붙인것이므로, FILE 대신 struct _iobuf 을 써도 똑같지만, 기본적으로 machine dependent 하고, typedef 가 machine dependency 를 다소 해결하는데 도움을 주므로, 그냥 FILE 을 쓰는것이 좋다.
fopen() 함수는 stdio.h 에 정의되어 있으며, 프로토타입은 다음과 같다.
FILE* fopen( const char * , const char * );
즉, 두개의 문자열 상수를 받고, FILE 형 포인터를 리턴한다.
첫번째 인자는 대상 파일명 이고, 두번째 인자는 open mode 이다.
open mode 는 읽기(r) , 쓰기(w) , 첨부(a) 의 세가지 기본적인 모드가 있다.
기본3가지 모드에 대해서 살펴보면...
"r" read
파일이 있으면 열고 없거나 접근할 수 없으면 NULL 포인터를 리턴한다.
"w" write
파일이 있거나 말거나 걍 새로 쓴다. 즉, 있으면 지워버리고 새로 쓴다. 역시 접근할수 없으면 NULL 포인터를 리턴한다.
"a" append
파일뒤에 덧붙여 쓰는 모드이다. 없으면 새로 쓰고, 있으면 덧붙여 쓴다. 접근할수 없으면 NULL 포인터를 리턴한다.
이진처리 옵션은 b 이다.
"rb" read binary
"wb" write binary
"ab" append binary
또다른 옵션으로 + 이 있다. 이것에 대해서는 생략한다.
파일은 동시에 여러개를 열어놓을수 있다. 가령 어떤 파일을 열어서, 다른파일 하나를 더 연다음 뒤에 덧붙여 쓰거나 할 수 있다. 이때는 두개의 파일을 연것이다.
파일처리가 끝나면 파일을 닫아준다. 물고있는 파일을 놓는것이다. 물론, 대부분의 컴파일러는 프로그램이 종료되면 자동으로 처리중인 파일을 닫아주지만, 그래도 확실하게 닫아주는것이 좋다.
이때 사용되는 함수가 fclose() 이다. 프로토 타입은 다음과 같다.
int fclose( FILE* );
인자는 닫고자 하는 파일을 가리키는 파일포인터이다.
리턴값은 성공적으로 닫았을경우 0 을 리턴하고, 에러 발생시 EOF 를 반환한다.
참고로 EOF 는 end of file 을 의미하고, int 값으로 -1 이다.
예.
#include<stdio.h>
void main(void)
{
FILE *fp; /* FILE 대신 struct _iobuf 를 써도 똑같이 돌아간다. */
fp = fopen("test.txt","w"); /* 쓰기모드로 열고, 파일포인터가 test.txt 를 가리키도록... */
fprintf(fp,"Hello!"); /* test.txt 에 Hello! 를 기록한다. */
fclose(fp); /* fclose() 가 인자로 파일포인터를 갖는데 반해, _fcloseall() 은 인자가없다.*/
return;
}
컴파일후 실행해보면, test.txt 파일을 생성한다. 그 파일을 열어보면, Hello! 라고 기록이 되어있는 것을 확인할 수 있다.
배열로 메모리를 요청할 경우, 싸이즈를 미리 예측해서 요청을 해야 한다. 그러나 많은 경우, 가변적으로 메모리를 요청해야 하는데, 이와 같이 동적으로 메모리를 요청하는 함수로 malloc() 과 calloc() 이 있다.
malloc() memory allocation
헤더파일: stdlib.h
프로토타입: void* malloc( size_t )
파라메터: 요청할 메모리의 바이트 크기 (size_t 는 보통 unsigned int )
리턴값: 메모리 요청 성공시에 그 메모리의 시작주소, 실패시 NULL
char *ptr;
ptr = malloc(6); // 6바이트를 요청하고, 포인터를 그 시작주소에 포인팅함
---------------------------------------------------------------------------------------------------
calloc() contiguous allocation
헤더파일: stdlib.h
프로토타입: void* malloc( size_t , size_t )
첫번째 파라메터: 요청할 메모리 단위의 개수
두번째 파라메터: 요청할 메모리 단위의 바이트 크기
즉, 단위크기 곱하기 개수 만큼 요청함
리턴값: 메모리 요청 성공시에 그 메모리의 시작주소, 실패시 NULL
결과적으로, calloc( n , m ) 은 malloc ( n * m ) 과 유사하다.
차이점은 calloc 은 요청한 메모리를 모두 0 으로 초기화 시킨다는 것이다.
자주 쓰이는 형태는
ptr = calloc( n , sizeof(double) ) ;
꼴인데, 이때, 기본데이터타입을 typedef 로 쓰면, 나중에 프로그램 수정시 용이하다. 즉,
typedef double entry;
ptr = calloc( n , sizeof(entry));
typedef 에 대해서는 나중에 자세히 설명하겠다.
------------------------------------------------------------------------------------------------------
요청한 메모리를 다시 시스템에 돌려줄때는 free() 를 사용하면 된다.
free()
헤더파일: stdlib.h
프로토타입: void free( void* )
파라메터: 이전에 할당된 메모리의 시작주소
만약 그 포인터가 NULL 이면, 아무짓도 안한다.
함수가 자기자신을 호출하는 것을 재귀호출이라 한다.
이때 호출중간에 또 자기자신을 호출하면, 이전에 호출했던 함수는 새로자신을 호출한 함수값을 받아야 리턴할수 있으므로 리턴을 미루고, 새호출의 리턴값을 받을때까지 대기한다.
이런젼차로, 새로 자신을 호출한 함수가 리턴값을 주면, 연쇄적으로 바로 이전의 호출이 리턴값을 갖게되어, 호출의 역순으로 리턴을 하게된다. 즉, 호출순으로 스택에 쌓였다가 역으로 꺼내면서 리턴한다.
당연한 얘기지만, 한번의 호출은 연속적인 호출로 이어지고 무한호출이 안되기 위해선, 언젠가는 리턴을 주는 상황이 와야된다.
예제로, 문자열을 하나 받아서 까꾸로 출력하는 함수를 (물론, 그냥 문자열 받아서 포인터로 거꾸로 돌리면서 찍어도 되지만) 재귀적으로 만들어보자.
계획.(재귀적으로...)
문자열에서 문자하나를 받아서 인쇄하는 함수를 만든다. 인쇄직전에 자신을 호출하면 인쇄를 뒤로 미루고 차례대로 뒷글자를 받아서 쌓아둔다. newline \n 을 받으면 재귀적 호출을 중지하도록 한다. 그러면 바로 직전에 미뤄둔 인쇄를 행하고, 연속적으로 거꾸로 인쇄한다.
#include<stdio.h>
#include<conio.h> /* for getch() in MS Windows Console. If Not MS platform, remove this and do not use getch() */
void backward(void);
void main(void)
{
printf("Input a string : ");
backward();
putchar('\n');
getch();
return ;
}
void backward(void)
{
char c;
if((c=getchar()) != '\n' )
backward();
putchar(c);
}
메모리(memory)와 주소(address)
어떤 변수를 선언(declare)하든, 시스템에게 해당 사이즈 만큼의 메모리를 요청할 것이다. 그리고 큰 문제가 없는한 메모리를 할당(allocate)받게 될 것이다. int 형으로 요청하면 4바이트를 요청할 것이고, 뭐 데이터형마다 각기 싸이즈를 요구할 것이다. 그리고 각각의 메모리는 주소를 갖는다. 즉, 데이터와 주소는 쌍을 이룬다. 그 주소를 알면 그 저장된 값도 언제든지 가져다가 쓸 수 있는 것이다.
포인터(pointer)
포인터는 주소값을 저장하는 녀석이다.
포인터에 저장된 주소값을 이용하면 원하는 대상이 저장되어있는 메모리를 직접 찾아갈수가 있다.
포인터의 이동단위
C에서는 포인터의 연산이 가능하다. 어떤 포인터가 어떤 주소값을 갖고 있는 상태에서 +1 해서 포인터의 값을 1 증가 시키면 포인터는 다음 주소의 값을 갖게 되는데, 이때 이동단위는 해당 포인터의 데이터형에 의존한다. 가령 char 형은 1바이트씩 이동하고, int 형은 4바이트씩 이동한다.
실제 메모리는 비트들로 되어있으므로, char 형은 8 비트씩 뛰어다니느 것이고, int형은 32비트씩 뛰게 되는 것이다.
주소연산자 (address-of operator) &
어떤 대상에 대해서, 저장된 값이 아닌, 메모리 주소(address)값을 반환한다. address-of 라고 읽기도 한다.
Reference (참조)
Reference 한다는 것은, 기본적으로, 어떠한 대상을 가리키는 것을 의미한다. C 에서는 어떠한 대상을 가리키는 방법으로 주소값을 사용한다. 주소값은 & 를 통해서 얻을수 있고, pointer variable 에 값으로 저장해두었다가 사용할 수도 있다. 참고로, C++ 에서는 주소를 직접 pointer variable 에 저장하지 않고도, 메모리에 저장된 대상을 직접 참조하는 참조변수(reference variable) 를 선언할 수 있지만, C 에는 그런게 없다. 따라서, C 의 context 에서는 reference 한다는 표현은 결국, 주소값 ( &를 통해 구한 값이든, pointer 에 저장된 값이든) 을 통해서 대상을 가리킨다는 말과 같다.
역참조 연산자 (dereference operator) *
어떠한 대상이 다른 대상의 주소를 가지고 있을때, 그 주소를 따라가서, 다른 대상의 값을 취하는 것을 dereference (역참조) 라고 한다.
역참조연산자는 * 를 사용한다.
그러므로, 주소연산과 역참조연산은 역연산 관계이다. 따라서, 이경우, & 와 * 는 약분된다.
예를 들어. 어떤 p 가 포인터라고 하자. 그러면, &*p 는 p 가 된다. 어떤 a 가 일반 변수라고 하자. 그러면 *&a 는 a 가 된다.
오프셋 역참조(dereference w/ offset) []
어떤 주소에서 부터 지정된 스텝을 오프셋시킨 상태에서 역참조하는 연산자이다.
즉, arr[ i ] 와 * ( arr + i ) 가 같은 뜻이 된다.
포인터 변수 선언
포인터 변수의 선언 구문은 다음과 같다.
type * variable1 , * variable2 , ... ;
* 연산자는 토큰으로, 독립적으로 쪼개질수 있으므로 붙여쓰던 띄어쓰던 그건 중요하지 않다. 다만 우선순위에서 밀릴때는 다른 연산자들과 마찬가지로, 결합우선순위를 확보하기위해 괄호를 쓰면된다. 참고로, * 연산자의 우선순위는 [] 에도 밀리고, 함수의 () 에도 밀린다.
예. int (*arr)[2];
int (*FnPick(int PickNum))(int, int) { ... }
매개변수로 포인터 변수를 넘길수 있고, 프로토타입의 경우 변수명이 필요없으므로 다음과 같은 형태가 된다.
return_type function_name ( type * , type * , ... ) ;
예. int func( int * , int* );
배열(array)명
배열명은 포인터 상수이다. 즉, char 형 배열을 선언했다면 그 배열명은 char형 포인터가 된다. 즉, 그 배열의 시작 주소를 값으로 갖고 있는 것이다. 보통의 포인터가 포인터변수(pointer variable) 임에 비해 배열명은 상수이다. 변경이 안된다. 즉, 포인터는 배정문을 통해 어디든 원하는 주소를 가리킬 수 있겠지만, 배열명에 배정문을 써서 값을 변경하는 것은 불가한다.
배열문이 포인터상수인것은 어찌보면 당연하다. 배열을 선언과 동시에, 배열명은 배열의 시작주소를 가리키는 일종의 고유명사화된 주소라고 할 수 있다.
배열의 인덱스(index)
배열의 인덱스를 나타내는 연산자 [ ] 는 사실은 오프셋 역참조 연산자이다. 배열명을 기점으로 몇칸이동(데이터형에 의존)한 지점을 역참조 할 것인가 를 나타낸다. 어떤 아파트가 있는데, 그 배열명이 그 아파트의 첫버째 집을 나타낸다면, 인덱스는 몇호에 사는 사람을 나타낸다. 즉, 호수까지 이동을 한 상태에서 역참조를 하는 것이다.
예.
char Apartment[100] = "Hi, everyone. How are you? I'm Fine, Thank you. And you? -_-" ;
printf( "%s" , &Apartment[15] ) ;
여기서, &Aprtment[15] 부분을 보자. Apartment 는 배열명이다. 이 아파트의 첫번째 번지수를 나타낸다. 즉, 첫번째 번지에는 H가 살고 있다. 즉, 저 H의 주소가 되는 것이다. 그런데 Apartment[15] 라고 했다. 15번째 다음의 집에는 누가 살고 있나를 말한다. 참고로 [0] 은 H가 되겠다. 0 만큼 다음 집이니까 첫집 그대로이다.
[0] : H
[1] : i
[2] : ,
[3] : 공백
[4] : e
[5] : v
...
[15] : o
즉, o 가 된다. 이때 이것은 주소가 아니라 이미 역참조( 해당주소에 살고있는 사람) 임을 상기하자.
따라서, 앞에 &를 붙여서, &Apartment[15] 라고 하게되면, o 의 번지수를 가리키게 된다.
참고로. printf() 의 %s 는 char 형 주소를 만나면 반복적으로 주소를 다음으로 넘겨가면서 null 문자를 만날때까지 찍어댄다. 따라서 결과는 다음과 같다.
ow are you? I'm Fine, Thank you. And you? -_-
이것은 printf( "%s" , Apartment + 15 ) ; 와 같다.
포인터 오프셋(offset)
벡터 스타일의 데이터를 쓰고 싶을때, 보통의 습관은 인덱스를 1부터 쓰는것을 선호한다. 그러나 [] 연산자를 인덱스처럼 사용하게 되면, 인덱스가 0 부터 출발하기 때문에 우리의 습관과 일치하지 않는다.
이럴때 포인터를 앞으로 하나 땡겨주면, 뒤로 한칸 옮겼을때 요청한 메모리의 첫 칸을 사용하기 때문에 [1] 이 첫 주소가 된다.
따라서...
입력장치로 부터 양의정수 n을 받아서, 원하는 형태의 데이터 타입으로 n 개의 메모리를 요청하고, 첫번째 인덱스 1부터 n번째 인덱스 까지 사용하는 방법은 다음과 같다.
typedef double entry;
...
int n;
entry *a;
... // get n somewhere
a = calloc( n , sizeof(entry) );
--a;
이런식으로 하면, a[1] 부터 a[n] 까지 쓸수있다.
대신 이 경우, a[0] 는, 우리가 요청한 곳도 아니고 접근해도 되는곳인지도 알수없으므로 절대로 써서는 안된다.
메모리를 반납할때도 free(a+1); 과 같은 식으로 반납해야 한다.
열거형 ( enum )
The enumeration data type (enum) sets up a different use of an integral data type.
열거형 데이타 타입은 그니까 정수형이다.
열거형 타입의 선언은 유한집합을 명명하고, 그 집합의 원소로서 식별자를 선언한다.
집합명은 tag 라고 하고, 집합은 { } 로 나타내며, 각 원소는 comma(,) 로 구분한다. 각 원소들은 int형 "상수" 이다.
enum tag { element_list } ;
, 로 구분된 원소들은 indentifier 들이며, 값을 assign해서 초기화 할 수 있다. 공용체의 원소들은 같은 값을 갖을 수 있다.
초기화 되지 않은 원소들은, 자신의 바로 앞(왼쪽) 원소의 값보다 1큰 수를 부여받는다. 첫번째 원소를 초기화하지 않으면 0 으로 초기화한다.
예. enum boolean { FALSE , TRUE } ; // FALSE = 0 , TRUE = 1
enum day { sun=1, mon, tue=1, wed, thu, fri=9, sat }; // mon=2, wed=2, thu=3, sat=10
enum 은 데이타 타입이 아니라, 열거형임을 나타내는 키워드이고, enum tag 를 묶어서 type 으로 분류한다.
열거형 "변수"의 선언은 다음과 같은 형식이다.
type variables; // where type:= enum tag_name
예. enum boolean a, b; // enum boolean 타입의 변수 a, b 선언
열거형으로 선언된 변수들은 집합의 원소들을 값으로 갖을수 있다.
예. a = FALSE ;
열거형 타입의 선언과 변수선언을 한번에 할 수 있다.
예. enum day { sun=1, mon, tue, wed, thu, fri, sat } a , b ;
a 와 b 는 enum day 타입의 변수들.
tag 는 생략가능하다(anonymous tag). 대신, tag가 없기 때문에, 처음 변수선언을 제외하고는 enum tag 의 타입으로 새로운 변수를 선언할 수 없다. (단, typedef 를 이용하면 가능하다.)
예. enum { beer, cocktail, soju } a, b ; //같은 타입의 변수를 더이상 선언할 수 없음.
열거형 변수 선언시, 매번 열거형임을 나타내는 키워드 enum 을 쳐야되므로, typedef 를 이용해서 좀더 간단하게 하는 것이 보통이다.
예. enum alcohol { beer, cocktail, soju };
typedef enum alcohol alcohol; // enum alcohol 을 alcohol 로...
alcohol a , b ;
열거형의 선언과 typedef를 한번에 쓸수도 있다.
예. typedef enum alcohol { beer, cocktail, soju } alcohol;
alcohol a , b ;
tag 가 없는 경우라도, typedef 를 사용하여 그 자체를 renaming 해버리면, 이후에도 자유롭게 사용할 수 있다.
typedef enum { element_list } identifier; // 해당 identifier 는 tag 없는 enum { ... } 타입의 새이름이 된다.
구조체 (structure)
배열은 동종의 자료들을 표현하기 위해 파생된 타입이다. 반면, 구조체는 서로다른 타입의 자료들을 표현하는데 사용된다. 구조체를 나타내는 C의 키워드는 struct 이다.
즉, 구조체는 다양한 타입의 데이타형의 묶음이다. ( 각 타입 요소를 member 라고 한다. )
C의 struct 는 C++ 의 출발점이다. C의 struct 개념은 이후에 C++에서 Class 개념으로 확장된다.
structure의 선언은 형식상 enumeration data type 과 유사하다.
열거형은... enum tag { element_list }; 와 같은 꼴이었다.
구조체는 ... struct tag { declaration_statements }; 와 같은 꼴이다.
열거형에서 {} 안에 들어가는 원소들의 구분은 콤마(,) 로 했었지만 ( 집합의 원소들이니까... )
스트럭쳐에서는 {} 안에 들어가는게 선언문들 이므로, 당연히 세미콜론(;) 으로 구분한다. (문장들 이니까...)
예. struct menu { char *item; int price; char rate; double tax, calories; } ;
열거형에서는 {} 안에 들어간것이 단순히 "정수상수"가 될 원소들이었다면, 구조체에서는 그것들이 "변수선언문"으로 대치된것이다.
구조체형 변수의 선언은 열거형과 마찬가지다.
struct tag variables ;
예. struct menu { char *item; int price; char rate; double tax, calories; } ;
struct menu a, b, c ;
열거형과 마찬가지로, 구조체형 선언시 변수 선언도 동시에 할 수 있다.
예. struct menu { char *item; int price; char rate; double tax, calories; } a, b, c ;
tag 의 생략도 열거형과 마찬가지다.
예. struct { char *item; int price; char rate; double tax, calories; } a, b, c ;
열거형임을 명시해주는것이 귀찮아 typedef 를 쓴것과 같이, 구조체임을 명시하는 것이 귀찮아 typedef 를 많이 쓴다.
예. struct menu { char *item; int price; char rate; double tax, calories; } ;
typedef struct menu menu;
menu a, b, c ;
열거형때와 마찬가지로, 구조체형 선언과 typedef를 같이 할 수 있다.
예. typedef struct menu { char *item; int price; char rate; double tax, calories; } menu ;
menu a, b, c ;
tag 가 없는 경우도 마찬가지다.
예. typedef struct { char *item; int price; char rate; double tax, calories; } menu ;
menu a, b, c ;
구조체 타입의 배열을 선언할 수도 있고, 배열이 구조체 내부의 멤버가 될 수도 있다. 심지어 구조체의 멤버가 구조체가 될 수도 있다.
구조체 형 변수도 보통의 변수와 마찬가지로 배열을 선언할 수 있다. 즉, 이것은 동종의 구조체형 자료들을 다루기 위함이다.
예. struct menu { char *item; int price; char rate; double tax, calories; } ;
typedef struct menu menu;
menu a[10] ;
-----------------------------------------------------------------------------------------------
구조체 타입은 사용자가 임의로 조합해서 만든 사용자 정의 타입이라고 할 수 있다.
하지만 그것들도 전히, int 나 double 와 같은 데이터 타입이다. 물론 C에서는 키워드와 함께해야하고 연산등에 있어서 차별을 두지만, C++ 에서는 정말 차별을 두지 않는다. (적어도 C++의 철학은 그렇다.)
따라서, 그 형에 맞는 포인터를 생각할 수 있다.
가령 위의 구조체 menu 형에 대해서 그것의 포인터 형 구조체 menu 포인터 형을 생각할 수 있다.
즉, struct menu 타입의 포인터 타입은 struct menu * 타입이다. 이 포인터는 struct menu 타입 데이타들의 주소를 가리킨다.
가령, 위에서 typedef 로 간단하게 struct를 안써도 되도록 menu 타입을 셋팅했으므로 menu 타입의 포인터 타입은 menu* 타입이다.
예. menu a;
menu *mpt;
mpt = &a;
------------------------------------------------------------------------------------------------
열거형에서 집합의 각 원소가 정수상수였던 것과는 달리, 구조체에서는 멤버들이 변수들이므로, 값을 자유롭게 변경하게 된다. 따라서, 멤버에 접근을 할 수 있어야한다.
구조체형의 변수는 멤버라고 하는 변수들을 포함하므로, 위계구조를 갖는 변수형이라고 할 수 있다. 이러한 위계구조는 구조체의 멤버가 또다시 구조체일때 여러단계로 나타난다.
위의 예에서, 구조체 menu 형으로 선언된 변수들 a, b, c 들은 각각 멤버로 item, price, rate, tax, calories 를 갖는다.
구조체 변수명에서 멤버에 접근할때는 . 연산자를 쓴다. C++ 클래스에서 멤버에 접근할때 . 을 쓰는것도 이와 같다.
구조체형 포인터에서 멤버에 접근할때는 포인터를 해당 구조체 변수에 포인팅한후에, -> 를 사용한다.
가령 다음과 같이 선언이 되어있다고 할때...
struct menu { char *item; int price; char rate; double tax, calories; } ;
typedef struct menu menu;
menu a ;
menu *strptr;
strptr = &a; // menu형 포인터 strptr 이 menu형 변수 a의 주소를 가리킴
다음과 같이 멤버접근 연산자 . 을 이용해서 a의 멤버에 접근해서 값을 할당할 수 있다.
a.item = "chiken salad";
a.price = 9800;
a.rate = 'A';
a.tax = a.price * 0.05 ;
a.calories = 480.7 ;
또한, menu형 포인터 strptr 이 menu형 변수 a의 주소를 가리키고 있으므로...
다음과 같이 포인터의 멤버접근연산자 -> 를 이용해서 a의 멤버에 접근해 값을 할당할 수 있다.
연산자 -> 는 * 와 . 의 짬뽕이다. p -> = ( *p ).
strptr -> item = "chiken salad"; // strptr 이 가리키고있는 구조체 변수에서 멤버 item 으로 접근 후 배정
strptr -> price = 9800;
strptr -> rate = 'A';
strptr -> tax = strptr -> price * 0.05 ; // ( strptr -> price ) * 0.05
strptr -> calories = 480.7 ;
구조체 포인터 strptr 가 현재 구조체형 변수 a의 주소이므로, 그것을 역참조하면 a 이다. 즉, (*strptr) 은 a
따라서, (*strptr).rate 는 a.rate 가 된다. 이와 같이 구조체도 포인터와 역참조 연산자 * , [] 따위의 관계를 그대로 사용할 수 있다.
또한, 구조체의 멤버가 구조체인경우에는 멤버접근 연산자 . 이나 -> 를 여러단계로 사용하면 된다.
구조체 변수의 초기화
배열은 동일자료형의 패키지이고, 구조체는 다른 자료형의 패키지라는 점에서 둘은 유사하다.
또한, 초기화 구문도 배열의 그것과 유사하다.
구조체 변수의 초기화에서도 배열과 마찬가지로, 중괄호 {} 와 콤마를 이용해 상수목록을 전달한다.
멤버들의 초기화를 위해 전달되는 상수목록은 멤버의 선언순서와 대응된다.
배열에서 상수목록의 개수가 부족하면 나머지 모든 원소가 0 으로 초기화 되었던것과 마찬가지로,
구조체에서도 상수목록의 개수가 부족하면 나머지에 대해서는 0 으로 초기화시킨다.
예.
#include <stdio.h>
int main(void){
typedef struct { int no; char *item; int price; char rate; double tax, calories; } menu;
menu a = { 17, "Salad", 5600 , 'A' , 560.0 , 1769.56 }; // 구조체 변수 a의 초기화
printf("Menu No.%d\nname: %s\nprice: %d\nConsumer's rate: %c\nTax: %g\nCalories: %g\n",
a.no, a.item, a.price, a.rate, a.tax, a.calories);
return 0;
}
만약, 위의 초기화 구문을 다음과 같이, 첫번째 목록만 남기고 나머지를 생략하면....
menu a = { 17 }; // 첫번재 멤버는 17로, 나머지는 모두 0 으로...
첫번째 멤버를 제외한 모든멤버가 0 으로 초기화된다. 포인터인경우 NULL 포인터로 초기화된다.
따라서, 구조체의 모든 멤버를 0으로 초기화 하는 것은, 위와같은 방식으로 초기화시키되 첫번째 멤버를 0 으로 초기화시키면 된다.
예. menu a = { 0 } ;
--------------------------------------------------------------------------------------------------
구조체 변수들 사이의 대입
구조체 형도 한번 선언이 된 후에는 int 나 double 과 마찬가지로 자료형으로서의 기능을 다 하므로, 구조체형 변수들도 마찬가지로 대입문을 사용할 수가 있다.
가령, fopen() 으로 파일을 열어서, FILE* 형 변수에 배정했던것도 마찬가지다. 이때 fopen()의 리턴타입이 구조체 FILE* 형 이므로, FILE* 형 변수에 배정할 수 있었던 것이다.
즉, 같은 타입의 구조체 변수들 사이에는 = 를 이용한 대입문이 가능하다.
이때, 대입의 의미는 대응되는 모든 멤버들에 대한 대입과 같다.
예.
#include <stdio.h>
int main(void){
typedef struct { int no; char *item; int price; char rate; double tax, calories; } menu;
menu a = { 17, "Salad", 5600 , 'A' , 560.0 , 1769.56 };
menu b = a ; // 구조체 변수 b의 선언과 배정을 동시에
printf("Menu No.%d\nname: %s\nprice: %d\nConsumer's rate: %c\nTax: %g\nCalories: %g\n",
b.no, b.item, b.price, b.rate, b.tax, b.calories);
return 0;
}
구조체 값들 사이의 연산은 C++ 에서 operator 들을 overloading 함으로써 가능하나, C 에서는 그와같은 간단한 방법은 없다.
하위 개념의 구조체를 멤버로 갖는 구조체를 구성함으로써 계층적인 데이터 구조를 형상화 시킬 수 있다.
다음의 예제는 구조체를 어떻게 계층적으로 사용할 수 있는지를 보여준다. ( 물론, 겉으로는 아무것도 실행되지 않지만... )
예.
#include<stdio.h>
typedef struct { int month, date, year; } DATE;
typedef struct { char *name; int month, date, year; } SDATE;
typedef struct { DATE birth, marriage, death; SDATE specialEvent; } EVENTS;
typedef struct { char *name; EVENTS event;} PERSON;
int main(void){
PERSON a;
a.name = "Greenwood";
a.event.birth.year = 1903;
a.event.birth.month = 3;
a.event.birth.date = 19;
a.event.marriage.year = 1942;
a.event.marriage.month = 1;
a.event.marriage.date = 15;
a.event.specialEvent.name = "Invention of the Perpetual Mobile";
a.event.specialEvent.year = 1951;
a.event.specialEvent.month = 3;
a.event.specialEvent.date = 27;
a.event.death.year = 2009;
a.event.death.month = 11;
a.event.death.date = 6;
return 0;
}
위에서 보듯이, 다른 구조체에서 같은 멤버명을 사용할수 있고, 또한, 구조체의 멤버로 구조체가 들어가면서 멤버구조체의 멤버에 연소적으로 접근함으로써 계층적 구조를 만들 수 있다.
------------------------------------------------------------------
공용체 ( union )
공용체는 구조체와 같은 구문형식을 갖지만, 멤버들마다 다른 메모리를 잡는것이 아니라, 멤버중 가장 큰 메모리 크기를 잡은후에, 그것을 공유한다.
즉, 다시말해서, 같은 장소에 저장된 정보를 다른형식으로 읽어들일수 있다는 말이다.
자세한 얘기는 비트단위 연산을 살펴보아야 하므로 여기서는 간단히 개념만 살펴보도록 한다.
C의 공용체 키워드는 union 이고, 형 선언 구문은 아래와 같다. 구조체와 키워드 union 만 다르다.
union tag { declaration_statements };
이외에, 변수 선언이나, 태그없는 공용체, typedef 문의 활용 등은 모두 구조체나 열거체와 마찬가지 이므로, 생략하고, 바로 예를 살펴보겠다.
union integreal { int integer; double real; }; // union integreal 형의 정의, 메모리 8바이트 할당
union integreal x ; // union integreal 형 변수 x 선언
----------------------------------------------------------------------------------------------
공용체의 멤버접근도 역시 구조체와 같은 구문을 같는다.
union integreal x, *p;
x.integer = 10 ; // 공용체 integreal 형 변수 x 의 멤버 integer 에 접근하여 10을 배정함.
p = &x ; // 공용체 integreal 형 포인터 p로 x의 주소를 포인팅
p->real = 1.542354 ; // 포인터로부터 멤버접근후 대입
공용체의 멤버들에 각각 접근하여 배정을 하였을때, 메모리를 공유하므로 이후의 선언에 의해 앞의 선언이 덮어씌워지며 데이타의 손실이 일어날 수 있다. 그러한 것이 어떻게 진행되는지에 대한 판단과 계산은 프로그래머의 몫이다. 우리는 나중에 비트단위 연산을 통해, 그것을 좀더 자세히 살펴보도록 할 것이다.
예.
#include<stdio.h>
int main(void)
{
union integreal{ int integer; double real;};
union integreal x,*p=&x;
x.integer = 10;
p->real = 1.542354;
printf("%d\n%lf\n",x.integer,x.real);
p->real = 1.542354;
x.integer = 10;
printf("\n%d\n%lf\n\n",x.integer,x.real);
return 0;
}
실행결과는 다음과 같다.
1666034994
1.542354
10
1.542354
공용체를 이용하면, 같은 데이터를 다른 싸이즈 단위로 접근하는 것이 가능하므로, 가령, 바이트단위나 비트단위로 선택적으로 접근하는 것 등을 가능케 한다.
어떠한 데이터형으로 int를 가정해서 프로그래밍을 했다고 할때, 그 컨셉과 관련해 수없이 많은 int 타입의 변수를 도입하게 된다. 그런데 프로그래밍이 한참 진행되던 도중, int 가 아니라 long 으로 수정해고자 할 때, 일일이 찾아다니며 바꿔야하는 번거로움이 생긴다.
사실 위와같이 int 와 long 의 선택은 단지 컴퓨터적인 개념에 의해서 나눠졌을뿐, 컨셉적으로는 integer 로 동일하다. 이는 프로그램을 구상하는 관점에서도 int 를 쓸지 long 을 쓸지는 본질적인 문제가 아니다.
typedef 를 이용하면, 좀 더 컨셉적인 부분에 집중할 수 있다. typedef 는 #define 과 마찬가지로, 이식성과 수정효율성 역시 높여준다.
구문은 다음과 같다.
typedef type identifier;
#define 과는 달리, 문장이다. ( 따라서 ; 를 찍어준다.)
#define 과는 순서가 반대이다. typedef 는 일종의 data type renaming 으로 볼 수 있다.
#define 는 define A as B
typedef 는 rename A as B
따라서, 코드내에서는 #define 의 경우, B를 새로 정의한 A를 사용하고, typedef 는 A 를 새로 이름붙인 B를 사용한다.
type 은 단순히 int 뿐만 아니라 이미 어떠한 정의된 타입이 될 수 있으며, 한정자로 쓰이는 각종 키워드나 tag 도 포함하고, 하부구조를 갖는경우 그것도 허용한다.
--------------------------------------------
예.
typedef int element;
...
element x,y; // 변수 x, y 를 int 형으로 선언
...
나중에 typedef int element; 에서 int 만 double 로 바꾸면, 자동으로 x, y의 선언은 double형이 된다.
----------------------------------------------------------------------------------------------
포인터형의 재정의
앞의 포인터 내용에서, 어떤 type의 포인터형을 type* 형으로 부른다고 했었다. 이것을 이용하면, 포인터 형도 typedef 를 사용해서 * 없이 선언할 수 있다.
예.
typedef int* intPtr ; // int 형 포인터 형의 재정의
intPtr p, q ; // int형 포인터변수 p , q 의 선언
-------------------------
마찬가지로, 어떠한 한정자나 tag를 매번 붙이는 것이 귀찮을 경우에도 typedef 는 유용하다.
라인단위 처리에 있어 어떠한 경우 해당 라인에서 복잡한 구조를 갖는 처리를 해야할 수 있다. 이러한 요청으로 많은 프로그래밍 언어들은 라인단위를 블락단위로 확장시키는 방식을 가능케한다. C 에서는 중괄호 { } 를 사용한다.
블락은 해당라인에 포함된 내부구조로 인식될 수 있으며, 보통 중첩(nest) 을 허용하고, 내부스코프를 갖으며, 해당라인에 귀속된다.
가령, 아래와 같은 상황에서 라인 8,9,10 은 라인 8 에 귀속되는 하위 혹은 내부구조라고 볼 수 있다.
5 a line
6 a line
7 a line
8 { a subline
9 a subline
10 a subline }
11 a line
12 a line
블락은 기본적으로 자체 스코프를 갖는다. 즉, 중괄호 내의 영역은 일차적으로 같은 스코프 영역이다. 중첩된 경우 하위의 블락을 벗어나는 스코프는 우선적으로 바로 자신의 상위에 있는 블락을 바라보며 (scoping), 이는 계층에 대해 연쇄적으로 작용한다.
{
상위블락영역
{
하위블락영역
}
상위블락영역
}
프로그래머가 코드를 짜면, 컴파일러는 일차적으로 해석을 해야한다. 그러나 컴퓨터는 계산기일 뿐이므로 "이해"라는 것을 하는것은 불가능하다.
물론, AI 에 대한 논란의 여지는 있다. 그리고 그것은 결국 인간의 인텔리젼스는 무엇인가로 귀착된다. 단지 뇌의 신경물질 전달의 결과로 볼것이냐 혹은 더 상위의 개념 ( 가령 영혼이라든가... ) 을 전제하느냐의 문제이다. 전자라면, AI는 매우 가능한 것이며, 이미 진행되고 있는 그것이다. 반면에 후자라면, AI는 이해라기 보다는 이해를 하는 것처럼 보이게 만들기위한 인간의 노력일 뿐이다.
궁극에가서는 같은 것을 놓고, 혹자는 AI라고 칭하고, 혹자는 조금더 진보된 계산기일 뿐이라고 칭할 것이다. 그러면 AI 라고 칭하는 자들은 , 인간이 그 이상도 그 이하도 아니므로 당신말도 맞다 라고 할 것이다.
여튼, 컴파일러는 소스코드를 모종의 기본단위로 쪼개고, 오브젝트 코드로 바꾸는데, 그러한 쪼갬의 단위를 token 이라고 한다.
ANSI C 에서는 6가지 종류가 있고, 다음과 같다.
keyword
identifier
constant
string
operator
punctuation
-----------------------------------------------------------------------------------------------------------------------
키워드는 C언어에서 미리 예약된 토큰이고, ANSI C 에서는 32 개가 있고, 다음과 같다.
auto , break , case , char , const , continue , default , do , double , else , enum , extern , float , for , goto , if , int , long , register , return , short , signed , sizeof , static , struct , switch , typedef , union , unsigned , void , volatile , while
여기에 컴파일러마다 추가적으로 지정할 수 있다. 그러나 주의할 것은 키워드 외에도 실제적으로 사용할 수 없는 단어들은 많다는 거다. 특히, 자주쓰는 라이브러리에 정의된 상수나 함수들은 피해야 한다.
----------------------------------------------------------------------------------------------------------------------
identifier 는 어떤 대상을 identify 해주는 녀석이다. 가령, 함수명이라든가, 변수명 등을 예로 들 수 있다. 우리말로는 식별자 라고 한다.
예를들면...
...
int a , b , sum ;
callback();
...
에서, a 도 identifier 이고, b 도 , sum 도 , callback 도 identifier 이다. 반면, int 는 keyword 이다.
() 는 operator 이고, ; 는 punctuation 이다.
identifier 에 사용가능한 문자는 문자, 숫자, 그리고 underscore ( _ ) 이다.
identifier 의 구문규칙을 살펴보면 다음과 같다.
identifier ::= { letter | underscore }1 { letter | underscore | digit }0+
underscore ::= _
즉, 숫자로 시작하는 것은 허용하지 않는다.
underscore 로 시작하는것도 구문상 문제가 되지는 않으나, 시스템에서 정의된 많은 식별자들이 _ 로 시작하도록 셋팅되어있기때문에, 시스템 프로그래머가 아닌경우, _ 로 시작하는것은 권장하지 않는다.
stdio.h 안에 _iobuf 등을 예로 들수 있다.
Backus-Naur Form (BNF) 은 고급언어를 기술하는 표준형식이다. (나도 잘 모르므로.. ) 간단히 언급하면...
::= 는 definition 정도의 의미가 되겠다.
| 는 OR 정도의 의미가 되겠다.
예를 들면...
digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
와 같이 digit 를 정의할 수 있다.
{ } 를 이용하여 대상에 대해 subscript 로 몇 몇 부가설명을 덧붙일 수 있다.
{ }1 : 하나만 선택
{ }0+ : 0 번 이상 선택 또는 반복.
{ }1+ : 1번 이상 선택 또는 반복.
{ }opt : 옵션 항목
예.
alphanumeric_string ::= { letter_or_digit }0+
letter_or_digit ::= letter | digit
letter ::= lowercase_letter | uppercase_letter
lowercase_letter ::= a | b | c | ... | y | z
uppercase_letter ::= A | B | C | ... | Y | Z
digit ::= 0 | 1 | 2 | ... | 9
위와같이 alphanumeric_string 을 정의하면, 숫자와 문자로 이루어진 문자열을 정의를 할 수 있다.
이 정의는 아무것도 없는 즉, 널문자열도 포함한다. 왜냐면 { }0+ 이기 때문이다.
참고로, 정의하고자 하는 것은 이탤릭체로 쓰고, 더이상 정의가 불가능한 최하위개념은 그냥체로 쓴다.
Visual Studio 2008 의 MSDN 을 쓸때, 검색어가 파랗게 반전되어 있는게 짜증날땐, 옵션에서 해제하면 된다.
만약, 그래도 안되면 이럴때 다음 파일(출처: 어디서 퍼왔던건지는 기억이 안남, 검색해보면 나옴)을 C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98\Bin 에 덮어씌우면 일단 해결된다. 대신 군데군데서 약간의 문자깨짐이 보이지만 별 문제는 안됨.
바흐, 이탈리안 콘체르토. BWV 971. F장조. 3악장. 프레스토.
이번 대회는 레베루가 -_- 높군요... ( 과제곡인 모차르트 K310 대신 바흐를 ... )
전악장 듣기 (굴드)
모짜르트 피아노 소나타 8번 K.310. A단조. 3악장. 프레스토.
슈헤이 : 뭐하는 거지?
왜 멈춘거야, 한군데도 틀리지 않았는데...
카이 : 같이가자! 숲에.
아줌마 : 뭐야 이건 모차르트가 아니지 않아 !?
이거 녹음을 아쉬케나쥐가 했다던데...
EISA (Extended Industry Standard Architecture) 1988년, 32비트
EISA는 PC/AT 호환기 용에 개발한 32비트 컴퓨터 버스 방식이다.
|
|
이것으로부터, 호환기 제조회사 각 사에게 있어서 하위 호환성의 단절이라고 하는 리스크를 치르지 않고, 버스의 고속화를 도모하는 이득이 있었다. 그 때문에, IBM도 몇 년이 지나서 일부 기종에 채택을 했다. 하지만, 호환성에 너무 중시했기 때문에, 데이터 전송 대역이 절대적으로 부족해, VL버스나, PCI가 보급되는 중, 점차 시장에서 물러났다
VESA(Video Electronics Standards Association) Local Bus. 1992년
VESA 에서 표준화한 로컬 버스 규격. VESA 로컬 버스라고도 한다.
중앙 처리 장치(CPU)와 주변 장치를 직접 연결하여 고속으로 데이터를 전달하는 데이터 통로를 제공하는 로컬 버스의 일종으로, PC/AT 호환기의 표준 버스인 ISA 버스나 EISA 버스에 비해 데이터 전송 속도가 매우 빠른 것이 특징이다.
VL 버스는 CPU의 동작 주파수가 i836에서 시작하여 수십 MHz로 가속화되면서부터 CPU의 속도로 동작하는 버스로 1992년에 표준화되었다.
확장 보드를 접속하기 위한 VL 버스 슬롯을 PC 주기판에 3개까지 내장하게 되어 있다. 하나의 VL 버스 확장 슬롯은 표준 ISA 버스 확장 슬롯 또는 EISA 버스 확장 슬롯과 인접해 있는 하나의 32비트 MCA 버스 슬롯으로 구성된다.
PCI (Peripheral Component Interconnect ) Bus , 1993년 중반
mainboard에 주변 장치를 부착하기 위해서 사용되는 컴퓨터 버스의 일종이다.
개인용 컴퓨터(PC)의 CPU와 주변 장치를 연결하는 ISA나 EISA, VESA의 후속으로 개발된 로컬 버스 규격.
PCI 버스 또는 PCI 로컬 버스로 널리 알려져 있으며, 이 규격의 PCI 슬롯이 대부분의 펜티엄 PC에 장착되어 있다.
장치는 다음 두 가지 형태를 가질 수 있다.
1. 메인보드 위에 바로 붙는 IC 형태 - PCI 스펙에서는 이러한 형태를 평면 장치(planar device)라고 부른다.
2. 소켓(슬롯)에 꽂아 쓰는 확장 카드 형태 - 사용자 입장에서 흔히 눈에 띄는 형태이다.
AGP ( Accelerated Graphics Port ) 가속 그래픽 포트, 1997년
인텔사가 DVD시대에 대비해 내놓은 차세대 버스 규격.
PCI-E ( PCI Express) 2007년 1.0 , ... , 2007년 3.0
종래의 PCI 병렬 버스와 소프트웨어적인 호환성을 유지하며, 스위치 패브릭(switched fabric) 구조와 점 대 점(point-to-point) 패킷 연결망 방식의 채용으로 성능과 확장성이 향상된 개인용 컴퓨터(PC)용 고속 직렬 버스 규격.
2.5Gbps의 차동 신호를 사용하며 최대 80Gbps까지 다양한 속도를 지원한다.
PCI 익스프레스는 인텔이 3 GIO(The 3rd Generation I/O)의 명칭으로 개발하였으며 2002년 PCI-SIG에서 규격화 되었고, 2005년 PCI Express Base Specification Revision 1.1로 발표되었다.
그림) 위에서 부터 차례대로 PCI-E x4 (4배속) , x16 ( 16배속 ) , x1 ( 1배속) , 다시 x16 (16배속) ,
맨 아래는 그냥 PCI
Agustin Barrios Mangore - Maxixe ( 막시스 , 머시셔 )