매번 SQL을 만드시느라 고생하셨죠?

토드에는 sql을 자동으로 만들어주는 기능이 있습니다.


file아래 schema browser버튼을 클릭합니다.


쿼리를 생성할 테이블을 선택합니다.


테이블에서 오른쪽버튼 클릭 후

generate statement 후 select 등 필요한 쿼리를 선택합니다.



테이블에서 오른쪽버튼 클릭 후

generate statement 후 select 등 필요한 쿼리를 선택합니다.


insert, merge, select, update 등 쿼리를 자동으로 생성해줘서

편리하게 사용할 수 있습니다.



동적할당을 사용하는 이유는 무엇일까?

c언어의 메모리 구조에 대해서 알고 있다면 동적할당을 사용하는 이유를 쉽게 알 것이다.


2014/06/26 - [Programming/C언어] - [C] 스택(Stack), 힙(Heap), 데이터(Data)영역


malloc 함수

 - 동적으로 메모리를 할당하는 함수 (힙 영역에 메모리를 할당)


#include <stdlib.h>

void* malloc(size_t size)	// malloc 함수의 원형

함수 호출시 할당하고자 하는 메모리의 크기를 바이트 단위로 전달하면 그 크기만큼 메모리를 할당하게 된다.

그리고 할당한 메모리의 주소(첫 번째 바이트의 주소)를 리턴한다.

메모리 할당에 실패하면 NULL이 리턴된다.


리턴형이 void*(void 포인터) ??


malloc은 단순히 메모리만 할당하는 함수이기 때문에 개발자가 어떠한 데이터 형을 저장하는지 예측할 수 없다.

예를들어 4바이트를 할당하였을 경우 int형 데이터를 저장하기 위해서 사용하는지, float형 데이터를 사용하는지 예측할 수 없기 때문에 void포인터를 반환하여 개발자가 알맞은 용도로 변환하여 사용할 수 있도록 만든것이다.


예를들어 int형 데이터를 저장하기 위해서는 리턴되는 void*을 int*로 변환해야 한다.

int *i = (int*) malloc (sizeof(int));



위의 그림은 포인터 변수 i에 4바이트를 할당하는 그림이다.


1. sizeof(int)의 값은 4이다. 4라는 값을 전달하면서 malloc 함수를 호출한다.

2. 할당된 메모리의 주소가 void*형으로 리턴된다. 리턴되는 void*를 사용하려는 int*형으로 변환한다.

3. 포인터 변수 i에 대입한다.


malloc함수 사용 예

동적 할당을 사용하여 arr_1의 배열의 값을 대입하는 소스를 보며 malloc함수 사용법을 이해해보자.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	return 0;
}

free 함수

 - 힙 영역에 할당된 메모리를 해제하는 함수


메모리를 할당만 하고 해제해 주지 않는다면, 언젠가는 메모리가 부족한 현상이 발생 할 것이다.

할당된 메모리가 더 이상 필요하지 않을경우 free함수를 이용하여 메모리를 해제시켜 줘야한다.


#include <stdlib.h>

void free(void* ptr)	// free 함수의 원형


free함수 사용 예

위의 예제에서 free함수만 추가시켰다.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}

calloc 함수

 - calloc함수는 malloc함수와 같은 기능을 지니고 있다. 다만 사용하는 형태가 조금 다를 뿐이다.


#include <stdlib.h>

void* calloc(size_t elt_count, size_t elt_size)	// calloc 함수 원형

calloc 함수는 elt_size 크기의 변수를 elt_count 개 만큼 저장할 수 있는 메모리 공간을 할당하라는 의미를 갖는다.


calloc함수 사용 예

위의 예제에서 malloc 함수대신 calloc 함수를 사용하였다.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	//arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함
	arr_2 = (int*) calloc(5, sizeof(int));	// sizoe(int)크기의 변수를 5개 저장할 수 있는 공간할당

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}

malloc함수와 calloc함수의 차이점!

malloc은 할당된 공간의 값을은 바꾸지 않는다.

calloc은 할당된 공간의 값을 모두 0으로 바꾼다.

배열을 할당하고 모두 0으로 초기화할 필요가 있을경우에는 calloc을 쓰면 편하다.


realloc 함수

 - 이미 할당한 공간의 크기를 바꿀 때 realloc 함수를 사용한다.


#include <stdlib.h>

void* realloc(void* memblock, size_t size);	// realloc 함수의 원형

이미 할당한 포인터 변수를 memblock에 넣고, 바꾸고 싶은 공간의 크기를 size에 입력하여 사용한다.


realloc함수 사용 예

malloc함수를 사용한 예제에서 realloc 함수를 사용하여 변경하였다.


#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[10];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 10; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	printf("\n");

	// sizeof(int) = 4바이트
	realloc(arr_2, sizeof(int)*10);	// arr_2의 메모리를 40바이트로 재 할당
	// arr_2의 메모리 크기 : 20바이트 -> 40바이트

	for(i = 0; i < 10; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}



크리에이티브 커먼즈 라이선스
Creative Commons License
  1. 미소2015.03.05 22:35 신고

    잘보고갑니다

  2. sms2015.09.29 16:32 신고

    정리를 정말 깔끔하게 해두셨네요. 잘 보고 갑니다.

  3. 눈작은나2015.10.03 11:44 신고

    디테일한 부분까지~~ 엔지니어의 숨결이..

  4. HightSchool Strudent2015.12.23 00:04 신고

    안녕하세요, C언어를 공부하고 있는 고등학생입니다.

    malloc을 통한 동적할당을 쓰고 싶은 곳이 있어 공부하고 있는 중인데,
    자꾸 return이 실행되기 직전에 프로그램이 중지되네요...;;

    짠 코드는,

    int *seque, size;
    scanf("%d", &size);

    seque = (int *)malloc(size);
    int i;
    for(i=0; i<size; i++){
    seque[i]=i;
    }
    for(i=0; i<size; i++){
    printf("%d ", seque[i]);
    }
    printf("\n");
    printf("TheEnd_________________EndLine_________________\n");
    free(seque);

    return 0;
    }

    입니다. 16, 32와 같은 수를 입력해주면 분명히 그 전 수 까지 다 출력하고 

    TheEnd_________________EndLine_________________ 도 문제 없이 출력합니다.
    그런데, 그 직후에 프로그램 실행 성공에 대한 정보(IDE는 Dev Cpp를 사용하고 있습니다.)를 띄우지 못하고 중지되었다는 알림이 뜹니다.

    같은 코드를 int형으로 하지 않고, char 형으로 바꾸어 하면 문제가 없이 실행 종료까지 되고요.

    뭐가 문제이고 어떻게 해야하나요?



동적할당이란?


 우선 c언어에서는 메모리 할당 방식의 종류가 2가지로 나뉘는데 한가지는 정적인 방식과 동적인 방식으로 나뉜다.

정적인 방식 : 프로그램 실행 전 변수의 저장공간을 먼저 할당 후 프로그램 종료시 해제.

Ex) int a[10] = 0;


동적인 방식 : 프로그램 실행 중 필요한만큼의 메모리를 할당 후 원할때 해제.


동적할당은 언제, 왜 필요할까?

  동적할당은 상당이 많은 상황에서 꼭 필요합니다. 예를 들어 영어이름을 저장하는 char배열을 만든다고 가정할때 char 배열의 크기를 얼마나 크게 잡아야 할까? 대충 15글자로 정했다고 하면 아래와 같이 배열을 만들것이다.

char name[15];


             □ 


그렇다면 만약에 name에다가Hong Gil dong 이라는 이름을 넣으면 name

 이라는 배열에 총 13글자가 들어간다. 


■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ □ 


하지만 이 배열의 크기는 15인데 13만들어가면 2라는 

크기의 메모리가 낭비된다.


그리고 만약에 Hong Myeong seong 이라는 이름을 넣으면 이 배열에

17글자가 들어간다. 즉 15라는 배열의 크기를 초과했다.


■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■   ■ 


이처럼 오버플로우 위험 또한 생긴다.


그래서 이럴때 사용하는 것이 동적할당이다.



동적할당의 사용방법


 malloc 함수로 동적할당함. (C 언어의 표준 라이브러리로 stdlib.h에 정의)



malloc함수


예를 들어 malloc(4)를 하면 4byte 만큼의 메모리를 할당받는다.

사용의 예를 들어보자면 다음과 같다.


Ex) int *i = (int*) malloc (sizeof(int));




위와 같이 i라는 포인터 변수를 선언하고 i값에다가 메모리를 할당하는 것이다.

sizeof(int)는 4와 같으니 malloc함수에 4byte의 메모리를 할당 받고 int 형으로 바꾸어 주는 것이다.

만약에 malloc(sizeof(int))는 그대로 하고 앞에 int*을 int형이 아니라 short형으로 바꾼다면 i라는 변수는 short형의크기의 배열 2개가 선언됩니다.


예를들어 input을 받아 input 만큼 배열의 크기를 할당받고 싶다면

int *arr = (int*) malloc(sizeof(int)*input);

이런식으로 하면 된다.


free함수


할당된 메모리를 해제하는 함수이다.

메모리를 할당 받고 메모리가 해제되지 않으면 할당받은 메모리는 사용이 끝났음에도 불구하고 컴퓨터가 종료되기 전까직 해당 메모리를 차지하게 된다.

따라서 메모리를 할당만 하고 해제를 안하면 언젠가는 메모리가 부족해지는 현상이 발생하게된다. 때문에 동적할당 후 더 사용할 이상 필요가 없다면 꼭 free함수로 메모리를 해제시켜주어야 한다.


다음 같이 메모리를 할당을 해주었으면 


int *arr = (int*) malloc(sizeof(int)*input);



마지막에는 꼭 다음과 같이 메모리를 꼭 해제 시켜 주어야 한다.

 free(arr_2); 







동적할당의 장점과 단점


-장점 상황에 따라 원하는 크기만큼의 메모리가 할당되므로 경제적이며, 이미 할당된 메모리라도 언제든지 크기를 조절할 수 있다.


-단점 : 더 이상 사용하지 않을 때 명시적으로 메모리를 해제해 주어야 한다.


MySQL 기본 문법

Mysql은 대화식 db입니다. 주로 서버에 연결을 하고 질문을 실행하며 결과를 화면에 출력해주는 일을 합니다. 무엇보다 아파치와 php를 연동했을 때 그 진가를 발휘합니다. 여기서는 리눅스용 mysql을 위주로 설명할 것이며 다른 db의 함수도 비교하겠습니다.

1. Mysql을 시작하기 전에

사용 가능한 이름

mysql을 한글 지원으로 컴파일했다면 한글 이름을 줘도 되지만 이것은 사용하지 않는 편이 좋습니다.
그리고 이름엔 "." 문자를 사용할 수 없고 첫 글자는 영문으로 사용해야 합니다.
또, 최대 64자까지 지원되며, 별명은 최대 256자까지 지원됩니다.
이름의 구성은 영문,숫자, "_" , "$"로 이루어져야 합니다. Mysql을 유닉스나 리눅스
계열에서 사용한다면 그 운영체제의 환경을 따르기 때문에 대소문자를 구별해서 데이터베이스 이름, table
이름이 저장됩니다.
Win32계열의 경우는 대소문자를 구분하지 않지만 하나의 문에서 대소문자를 같이 사용할 수는 없습니다.
하지만 별명은 대소문자를 구분해 줍니다.


그림.9-1 필자의 mysql 작업 환경

2. 주석의 사용

각 라인의 끝에 오는 "#"과 여러 라인을 사용할 수 있는 "/*  */"가 있습니다.

3. 컬럼 타입

mysql의 컬럼은 종류가 다양합니다. 그래서 보기 쉽게 아래 표를 만들어 보았습니다.


Type                     Option   (B)-최대표시, (F)-소수점이하자릿수
---------------------------------------------------------------------------------------------------------------------
TINYINT                  정수형(-128~127), (B), [UNSIGNED]-정수형(0~255)

SMALLINT                 정수형(-32768~32767), (B), [UNSIGNED]-정수형(0~65535)

MEDIUMINT                정수형(-8388606~8388607), (B), [UNSIGNED]-정수형(0~16777215)

INT                      정수형(-2147483648~2147483647), (B), [UNSIGNED]-정수형(0~4294967295)

INTEGER                  INT와 동일

BIGINT                   정수형(-9223372036854775808~9223372036854775807), (B),
                         [UNSIGNED]-정수형(0~18446744073709551615)

FLOAT(정밀도)            부동소수점실수, (정밀도)-"(4,8)", (4)-단정도
                         부동소수점실수, (8)-배정도 부동소수점실수, 범위는 FLOAT, DOUBLE과 같습니다.

FLOAT(L, F)              단정도 부동소수점 실수, (B,F), (-3.402823466E+38 ~ 1.175494351E-38, 0,
                                                          1.175494351E-38 ~ 3.402823466E+38)
DOUBLE                   배정도 부동소수점 실수, (B,F), (-1.7976931348623157E+308 ~ -2.2250738585072014E-308,0,
                                                          2.2250738585072014E-308 ~ 1.7976931348623157E+308)

DOUBLE PRECISION~REAL    PRECISION(B,F), REAL(B,F) DOUBLE와 동일

DECIMAL                  부동 소수점 실수 CHAR 형태로 동작, (B,F), F가 0이면 소수점이하는 저장되지 않습니다.
                         범위는 DOUBLE와 같습니다.

NUMERIC                  ECIMAL과 동일

DATE                     날짜형(1000-01-01 ~ 9999-12-31)
                         기본타입 - YYYY-MM-DD

DATETIME                 날짜와 시간형(1000-01-01 00:00:00 ~ 9999-12-31 23:59:59)
                         기본타입 - YYYY-MM-DD HH:MM:SS

TIMESTAMP                타임스템프형(1970-01-01 ~ 2037년 임의 시간),
                         (B) - (14,12,8,6) B 값이 없을 경우 INSERT, UPDATE시 동작된 시간으로 자동적으로 저장됩니다.
                         기본형식 - YYYYMMDDHHMMSS, YYMMDDHHMMSS, YYYYMMDD,YYMMDD

TIME                     시간형(-838:59:59 ~ 838:59:59) 기본형식 - HH:MM:SS

YEAR                     년도형(1901 ~ 2155, 0000)

CHAR                     고정폭 문자열, (B) - (1~255) B 만큼 오른쪽으로 공백 채워 저장 출력 시 공백은 출력안됨,
                         [BINARY] - 검색 시 대소문자 구분

VARCHAR                  가변폭 문자열, (L) - (1 ~ 255) 문자열 공백이 제거된 후 저장,
                         [BINARY] - 검색 시 대소문자 구분

TINYBLOB / TINYTEXT      BOLB, TEXT형, 최대길이 255문자

BLOB / TEXT              BOLB, TEXT형, 최대길이 65535문자

MEDIUMBLOB / MEDIUMTEXT  BOLB, TEXT형, 최대길이 16777215문자

LONGBLOB / LONGTEXT      BOLB, TEXT형, 최대길이 4294967295문자

ENUM                     문자열 목록형, 최대 65535개, 저장된 문자열 목록 중에 오직 한가지만 얻을 수 있습니다.

SET                      문자열 목록형, 최대 64개, 저장된 문자열 목록 중에 0, 1개 이상을 얻을 수 있습니다.
---------------------------------------------------------------------------------------------------------------------

4. 접속 하기

./mysql -h 호스트명 -u 유저 -p   ("mysql>" 프롬프트가 나타나고 쿼리를 실행하면 됩니다.)
디폴트 인스톨을 했다면 "/usr/local/mysql/bin" 디렉토리에서 접속 명령을 실행합니다.
물론 -p 옵션은 유저에 해당하는 비밀 번호입니다.
위 명령을 실행하고 엔터를 치면 passw 입력 란이 뜨고 거기에 passw를 입력하면 됩니다.

5. 접속 끊기

"mysql>"에서 "quit" 또는 " ctrl + d "를 누르면 됩니다.

여기서 "quit" 명령은 " ; " 없이 실행합니다. ("quit" , " use")


그림. 9-2 mysql 접속하기

6. mysql 버전과 지금 날짜 알아보기

mysql> select version( ), current_date( );


그림. 9-3 mysql 버전과 지금 날짜 알아보기
위 그림에서 1 row는 하나의 쿼리 결과를 말하며 (0.03 sec)는 쿼리 시간을 말합니다.


그림. 9-4 now( ) 함수
위 그림에서 각 명령의 연결은 " , "로 하며 select 실행 때 명령의 마지막을 " ; "로

표시합니다.   " ; " 이 없을 경우 위와 같이 명령을 계속 입력하게끔 합니다.

7. 데이터베이스 만들기(database와 table 만들기)

그럼 이제 직접 database를 만들고 여러 가지 사용하는 방법을 알아보겠습니다.    먼저 만들기 전에 기본적으로 만들어진 database를 확인해 봅니다.

데이터베이스 보기

mysql>show databases ;


그림. 9-5 show databases
(해당 데이터베이스의 테이블을 확인하려면 "show tables;" 명령으로 확인 가능합니다.)
위 그림을 보면 현재 database 항목이 여러 개 나오는 것을 볼 수 있는데 디폴트 값은"mysql" 과 "test"
값만이 나올 것입니다.   사용자에 따라 다르게 나올 수도 있습니다.

"mysql" db  -  사용자 접근권한 정보를 가지고 있습니다.
"test" db  -  말 그대로 test를 해볼 수 있는 db 입니다.

(지금부터 하는 모든 명령은 root 계정으로 실행합니다. 뒷 부분에 계정의 설정과 권한 등을 설명 하겠습니다.)

database 선택해서 사용하기

mysql>use database명;

test를 선택하고 실행하면 test database를 선택하고 changed 되는 것을 볼 수 있습니다.

특정 database로 바로 접속하기

"./mysql -h 호스트명 -u 유저명 -p 데이터베이스명"

이제 database를 만들어 보겠습니다. 일단 만들기 전에 중요한 부분은 설계를 해봐야 한다는 것입니다.
이 데이터베이스를 어디에 사용할 것이며 어떤 항목들을 넣어야 하나 여러 가지를 시험한 후 작성하는게
안전합니다.   물론 만들고 난 후 수정할 수도 있습니다.
만들 database명은 "work"로 임의로 정했습니다. 이 데이터베이스의 역할은 어느 누가 맡은 일을
몇번 했나 알아보는 간단한 database입니다.

데이터베이스 만들기

mysql>create database work ;


그림. 9-6 데이터베이스 만들기
"show" 명령으로 확인하면 work가 만들어진 것을 볼 수 있습니다.


그림. 9-7 데이터베이스 확인
work 데이터베이스의 사용을 위해 use 명령을 실행하고 다음 테이블을 확인합니다.
물론 새로 만든 데이터베이스이기 때문에 테이블은 비었다고 나옵니다.
(자! 그럼 table를 만들어 보겠습니다. 다른 방법도 있지만 여기선 직접하는 방법을 택했습니다.
다른 방법은 txt 파일을 만들어 파일을 실행하는 방법입니다. table명은 "works"로 하겠습니다.)

테이블 만들기

mysql>create table 테이블명 ( 열이름 자료형, 열이름 자료형, ... ) ;


그림. 9-8 테이블 만들기
테이블을 만들 때의 자료형은 앞 부분의 표를 확인해 보기 바랍니다.
자료형의 형태는 alter table를 사용해서 바꾸어줄 수 있습니다.
그럼 지금까지 만든 테이블의 자료형과 열의 이름을 확인하는 방법을 알아보겠습니다.

테이블 자료형 알아 보기

mysql>describe 테이블명 ;


그림. 9-9 테이블 자료형 알아보기
(여기서 name은 이름, hab는 취미, own은 담당, sex는 성 , work는 마지막 작업일, no는 작업할 횟수로 표현했습니다.)

8. 테이블에 데이터 입력하기

이제 테이블에 데이터를 입력할 차례입니다.    이것 또한 직접 하나씩 입력하는 방법과 파일로 한꺼번에 입력하는 방법이 있습니다.

테이블에 데이터 입력하기(직접)

mysql>insert into 테이블명 values(‘자료명’ , ‘ ..’ , ‘..’) ;
이것은 해당 테이블을 새롭게 생성했을 경우 처음 입력할 때 주로 사용 합니다.
자료의  입력 순서는 각 열에 해당하는 인자 순으로 나열하면 되고, 만약 해당 열에 자료를 넣지  않으려면
"null" 을 사용합니다.

테이블에 데이터 입력하기(파일)

mysql>load data local infile "파일명" into table 테이블명 ;

모든 자료 입력은 테이블 열의 이름순으로 하면 됩니다. 그리고 중요한 것은 열과 열 사이는 tab 키로 해야
하는 것입니다.   어떠한 에디터를 사용하건 확장자나 이름은 중요하지 않습니다. 또, 빈 공간이 없이 입력을
해야 합니다.   열의 항목을 비워 두려면 " /n "을 사용하면 됩니다.


그림. 9-10 works_table.sql
위 파일을 보면 공백이 없는 것이 확인 될 것입니다.
이 파일은 "works_table.sql"로 만들었고 2) 번의 방법으로 해당 테이블에 입력했습니다.


그림. 9-11 테이블에 데이터 입력하기(파일)
위 그림에서 해당 파일의 내용을 테이블에 입력하고 테이블 내용을 다시 확인했습니다.

9. 테이블에서 정보 검색하기

select문을 사용해서 다양한 방법으로 검색을 할 수 있습니다.

기본 형식

mysql>select 검색명 from 테이블명 where 검색조건;

검색명은 여러 개 나열할 수도 있습니다. 가령 name,work, …  이런 식으로 ","를 사용합니다.
"*"를 사용해서 전부를 검색할 수도 있습니다.
그리고 검색조건은 사용하지 않아도 무관 합니다.
그럼 where의 여러 가지 형태를 보기로 하겠습니다.

where 검색 조건

mysql>select 검색명 from 테이블명 where 검색조건(and , or, like , regexp)

(다른 부분은 뒷 부분 주요함수 부분에서 다루겠습니다.)

where and 사용하기

mysql>select * from works where (no = "4" and sex = "f");

and일 경우 (둘 다 참이어야 합니다.)


그림. 9-12 where and 예 1
위 그림은 여자 중에 일을 4번 한 사람을 찾는 것입니다.


mysql>select * from works where (own = "청소" and hab = "잠자기");


그림. 9-13 where and 예 2

where ( own = "청소" and hab = "잠자기" ) 이것은 맡은 일이 청소이면서 취미가 잠자기인
사람을 찾는 것입니다.   여기까지는 검색명을 "*"로 사용했기 때문에 열 전부를 보여 주었습니다.
특정한 열만 보려면 그 열의 이름을 적어주면 됩니다.

where or 사용 하기

mysql>select * from works where (own = "청소" or hab = "농구");

or일 경우는 (둘 중 적어도 하나는 참이어야 합니다)


그림. 9-14 where or 예
그리고 위 방법 말고도 and와 or을 같이 사용하는 방법과 한 개 이상을 사용하는 방법 여러 가지가 있습니다.

where and, or 사용하기

mysql>select * from works where (sex = "m" and work >= "2000-01-20") or
                                (sex= "m" and hab= "잠자기");


그림. 9-15 where and, or 예
그림을 보면 "where( 문장 and 문장 ) or ( 문장 and 문장 )" 사용한걸 볼 수 있습니다.



자, 그럼 이제 특정한 문자의 패턴 일치를 비교해서 검사하는 방법을 살펴보겠습니다.
두 가지 방법이 있는데 like와 regexp 입니다. 각각의 패턴을 검사하는 방식을 살펴보겠습니다.

where like 사용하기

mysql>select * from work where 열명 like "찾을문자%", "%찾을문자", "%찾을문자%", "____" ;

여기서 like 다음 나오는 형식은 차례로 찾을 문자로 시작하는 단어, 찾을 문자로 끝나는 단어, 찾을 문자를
중간에 포함한 단어, 4개로 이루어진 문자 식으로 해석하면 됩니다.
이 명령은 주로 게시판의 검색에 사용하면 편합니다. 뒤에 게시판 구현에서 잘 살펴 보기 바랍니다.

where regexp 사용하기

mysql>select * from work where 열명 regexp "^[jJ]" , "^.{5}" ;

regexp 형식을 보면 좀 생소한 기호가 있을 것입니다. 이 기호는 이 표현식(정규 표현식)에 사용하는 몇가지
기호입니다.   그리고, regexp 표현은 대소문자를 가리기 때문에 검색 조건을 입력할 때 주의를 요합니다.
위 형식은 차례로 문자 처음이 소문자 j 대문자 J로 시작하는 조건을 검색, 5개의 문자로 이루어진 단어
검색을 의미합니다.



Regexp 기호

    . - 문자 하나를 나타냅니다.
    *   - 앞에 나온 문자의 0개 이상 반복합니다.
    ^   - 문자열의 처음을 나타냅니다.
    $   - 문자열의 끝을 나타냅니다.
    [,] - 괄호 안의 문자열 일치를 확인합니다.
    {,} - 반복을 나타냅니다.


그림. 9-16 regexp 예
위 그림은 works 테이블에서 필드가 name인 항목 중 알파벳 b로 시작하는 사람을 찾아 줍니다.

order by 사용하기

mysql>select 열명 from 테이블명 order by 열명;

order by는 인자 순으로 열명에 해당하는 자료를 정렬해 주는 명령입니다. 자료를 순서대로 보기위한 좋은
방법입니다.   게시판을 만들 경우 이 명령을 꼭 한번은 사용해야 할 것입니다.


그림. 9-17 order by 예
위 그림을 보면, work 순으로 차례로 정렬한 모습을 볼 수 있습니다.

제일 처음 일을 그만 둔 사람을 위 명령으로 쉽게 찾을 수 있습니다.

order by DESC 사용하기

mysql>select 열명 from 테이블명 order by 열명 desc ;

desc 옵션을 사용하면 해당 열명을 역순으로 정렬해 줍니다. 뒤 경매 게시판에서도 볼 수 있지만 이 옵션은
유용하게 쓰입니다.   게시판의 경우 사람들은 순서대로 보다 역순으로 봐야지만 제일 최근 자료를 볼수 있는
것입니다.   이 명령은 제일 최근까지 일했던 사람을 역순으로 제일 위에 오게 출력해 줍니다.
mysql의 경우 이 명령이 순차적인 것보다 조금 느리게 출력될 경우도 있습니다. 물론 자료가 많을 경우입니다.
하지만 한번 실행하고 난 뒤는 거의 속도 차이가 없다고 보면 됩니다.
여기서 주의 할 부분은 desc 인자 앞의 열명만 적용이 된다는 것입니다.
그 앞에 다른 열명을 적어 줘도 역순으로 정렬되지 않습니다.


그림. 9-18 order by DESC 예
위에서 볼 수 있듯이 desc 인자의 역할은 되지 않고 있습니다.

레코드 수 보기

mysql>select count(*) from work ;

총 레코드 수를 계산해 줍니다.


그림. 9-19 count( ) 예
위 명령과 group by 명령을 같이 사용할 경우 더 강력한 기능을 합니다.

group by 사용하기

mysql>select 열명,count(*) from 테이블명 group by 열명 ;

해당 그룹의 레코드에 관해 숫자를 파악해서 출력해주는 역할을 합니다. (count(*)와 같이 사용할 경우)


그림. 9-20 group by 예 1
출력의 결과를 보면 일의 종류에 따라 배치된 사람의 수를 파악할 수 있습니다.


그림. 9-21 group by 예 2
위 그림을 보면 jun이 물주기 역할을 두개나 받았다는 걸 알 수 있습니다. 또, 총 맡은 일을 볼 수도 있습니다.
이렇게 여러 가지를 그룹으로 출력해 주는 명령은 유용하게 쓰일 수 있습니다.


그림. 9-22 한 사람이 맡은 일 수

10. 테이블 지우기와 수정

만들어진 테이블을 지우거나 다시 수정하는 방법은 몇 가지가 있습니다. 게시판의 사용 시에 이 부분은 꼭 들어갑니다.    데이터의 삭제와 추가 등에 사용됩니다.

테이블 지우기

mysql>delete from 테이블명 ;

해당 테이블을 삭제합니다.

테이블에 필드(열) 추가하기

mysql>alter table 테이블명 add 열명 자료형태 ;

테이블에 다른 열을 추가시킵니다.
만약 게시판의 admin 툴을 만든다면 이 방법을 적용할 수 있습니다. 지금까지 말한 모든 설명들은 데이터베이스
admin 툴을 만들 때 사용하는 중요한 기본 질의들입니다.


그림. 9-23 테이블에 필드(열) 추가하기
위 그림에서 tel 필드가 새롭게 추가된 것을 볼 수 있습니다.

테이블의 특정 필드(열) 삭제하기

mysql>alter table 테이블명 drop 열명 ;

해당 테이블의 특정 필드(열)를 삭제합니다.


그림. 9-24 테이블의 특정 필드(열) 삭제하기
위 그림에서 tel 항목이 삭제된 것을 볼 수 있습니다.

테이블 특정 레코드 삭제하기

mysql>delete from 테이블명 where 열명 = ‘레코드명’(데이터명);

해당 테이블에서 특정한 레코드만 삭제합니다.


그림. 9-25 특정 레코드 삭제하기
위 그림에서 name이 glee라는 레코드만 지워진 것을 볼 수 있습니다.

테이블 특정 레코드 수정하기

mysql>update 테이블명 set 열명= ‘레코드명’(데이터명) where 열명= ‘레코드명’;

이 방법은 해당 테이블의 특정 레코드의 내용만 수정할 때 사용합니다.


그림. 9-26 특정 레코드 수정하기
위 그림에서 name가 bian인 레코드 중 hab가 "?" 에서 컴고치기로 고쳐진 것을 볼 수 있습니다.

11. mysql 접근과 권한설정

이제 mysql에 대한 접근하는 방법은 익숙하리라 생각합니다.    그러면 이제 각 데이터베이스 별 접근과 권한에 관한 설정들을 알아 보겠습니다.    뒤의 게시판 등의 소스에는 그냥 root 권한으로 설정했지만 여러분들이 이 부분을 수정하고 여러 가지 다른 방법으로 데이터베이스 접근을 하도록 만들 수 있습니다.

mysql 권한을 설정하기 전에

먼저 mysql 클라이언트에 접속을 합니다. 그리고 디폴트로 설치된 mysql의 데이터베이스 항목을 살펴 보겠습니다.


그림. 9-27 데이터베이스 보기
위 그림은 아직 데이터베이스를 추가로 만들지 않은 여러분들과는 조금 다릅니다.
디폴트는 mysql과 test 데이터베이스만 있습니다.
그럼 mysql의 전반적인 설정들이 들어 있는 mysql 데이터베이스를 선택하겠습니다.


그림. 9-28 mysql 데이터베이스 내용
위 그림에서 나열된 테이블 중 db와 user을 살펴 보겠습니다.

db 테이블 - 각 데이터베이스의 이름과 호스트, 사용자 등의 권한을 설정합니다.
user 테이블 - mysql을 사용할 수 있는 유저와 해당 유저의 권한을 설정합니다.


그림. 9-29 db 테이블 속성
위 그림은 db 테이블의 속성들입니다. 이 테이블에 해당 유저들이 사용할 데이터베이스를 설정하고 권한을 줍니다.

데이터베이스 사용 user 등록하기

여기서는 test 데이터베이스의 사용자(cry)를 등록해 보겠습니다.


그림. 9-30 db 테이블 쿼리 (Host,Db,User)
위 그림에서 볼 수 있듯이 db 테이블은 데이터베이스의 소유자를 등록하는 곳입니다.
(Test 데이터베이스의 소유자는 지금 아무도 설정되지 않았습니다.)
그리고 user 테이블은 데이터베이스를 사용하는 user을 등록하는 곳입니다. 즉, db 테이블에 소유자가 등록되어
있더라도 user 테이블에 그 소유자(사용자)가 존재하지 않으면 안됩니다.
처음 mysql의 설정에서 root를 설정했습니다. 이 root 계정은 뭐든지 다 할 수 있는 계정이므로 데이터베이스별로
관리를 할 수 있고 모든 권한이 없는 해당 데이터베이스만 관리 하는 계정이 필요로 한 것입니다.
db 테이블의 속성을 보면 여러 가지 데이터베이스에서 질의하는 속성 들을 볼 수 있을 것입니다.
그 중 사용하게 할 질의 들은 ‘y’ 그렇지 못하게 할 경우는 ‘n’ 등을 줌으로 해서 데이터베이스 관리자의 권한을
정할 수 있습니다. 해당 질의 권한을 설정하는 부분은 총 10개 항목이 있습니다.
앞에서 insert 문과 update 문을 보았기 때문에 테이블을 다루는 방법은 이제 습득했을 줄 압니다.
여기서는 기존에 존재하는 test 테이블을 수정하는 것이기 때문에 update문을 사용 했습니다.
만약 새로운 데이터베이스를 db 테이블에 추가할 경우는 당연히 insert 문을 이용하면 되겠습니다.


그림. 9-31 test 데이터베이스 사용자 등록

insert into 문을 이용한 새로운 데이터베이스 등록 역시 다음과 같은 식으로 해주면 됩니다.


insert into db values("해당 열" , "해당 열" …….);

Update 방법은 기존에 있던 행을 말 그대로 업데이트 한 것 입니다.
필자의 경우 update가 조금 헛갈리는 경우가 있어 여러분들도 혹 그런 분이 있을 거 같아 insert 대신
update문을 사용해 봤습니다.


그럼, 해당 항목들이 업데이트 되었는지를 select 문을 이용해 확인해 보겠습니다.


select Host,Db, User from db ;


그림. 9-32 db 테이블의 test 데이터베이스 사용자 추가 확인

Mysql 사용자 등록하기

user 테이블의(mysql 데이터베이스에서) test 데이터베이스를 사용할 cry 사용자를 등록해 보겠습니다.
우선 user 테이블의 속성들을 확인합니다.
user 테이블 역시 해당 질의에 관한 권한이 있습니다. 총 14개 항목이 존재합니다.
여기서는 insert into 문을 사용해서 추가를 하겠습니다.


그림. 9-33 user 테이블 속성
위 그림에서 유저를 등록하기 전에 상기 할 부분은 각각의 질의에 관한 권한 설정을 명확히 정한 다음
등록하는 것입니다.   cry라는 사용자가 test 데이터베이스 만을 관리하는 것이라면 질의 모두 "n"으로
해야 될 것입니다.   하지만 cry가 다른 데이터베이스도 관리하길 원한다면 질의 중 필요한 항목만 "y"로
선택하면 됩니다.

그럼  다음을 입력해서 user 테이블의 등록 상태를 확인해 보도록 하겠습니다.

select Host, User, Password from user ;


그림. 9-34 user 테이블 쿼리(Host,User,Password)
위 그림에서 Password 항목을 보면 알 수 없는 숫자와 알파벳으로 되어진 것을 볼 수 있습니다.
이것은 mysql이 passwd을 저장할 때 암호화해서 저장하기 때문입니다.
그런 이유로 insert into 문으로 암호 열을 입력할 때는 password() 함수를 사용해서 입력해야 합니다.

insert into user (Host,User,Password) values("192.168.0.1", "cry", password("cry98"));

이렇게 실행하면 됩니다.(passwd 부분은 여러분들이 정하는 곳입니다.)
여기서는 Host, User, Password 항목만 추가했습니다. 그것은 나머지 열의 질의 권한 항목은 디폴트가 "n"이기
때문에 입력하지 않아도 됩니다.


그림. 9-35 user 테이블에 cry 사용자 등록
위 그림에서 192.168.0.1의 Host와 cry 유저 그리고 암호가 등록된걸 확인할 수 있습니다.
이 설정들은 mysql을 재시동하고 다시 접속할 때부터 적용됩니다.

mysql 사용자 테스트

cry 계정으로 192.168.0.1 호스트로 접속해 보겠습니다.


그림. 9-36 mysql 추가 사용자 접속 확인
물론 앞에서 설정한 "cry98" 암호로 접속해야 됩니다.


그림. 9-37 cry 계정 데이터베이스 사용 가능 테스트
위 그림에서 cry 계정의 처음 권한 설정에 의해 test 데이터베이스만 사용 가능함을 볼 수 있습니다.


여기까지 대략적인 mysql의 사용자 권한 설정과 추가에 대해 살펴 보았습니다.
이러한 방법들을 이용해서 나중에 게시판에 적용하면 많은 도움이 될 것입니다.
참고로, 뒷 장의 게시판 부분과 나머지 소스들의 mysql 연동은 root 권한으로 설정되어 있습니다.
하지만 여러분들이 원하는 계정을 추가한 다음 입 맛에 맞게 얼마든지 수정할 수 있습니다.

12. mysql 데이터의 복구와 backup

데이터의 백업은 정말로 중요한 일 중에 하나입니다.    필자는 컴퓨터를 통째로 날린 적이 있기 때문에 더욱 백업에 열을 올립니다.    Mysql 뿐아니라 php 역시 tar로 반드시 백업하는 습관을 가져야 합니다.    요즘의 에디터들은 대부분 작업하다가 종료한 파일에 대해 자동적으로 백업 파일을 생성해 저장합니다.    그 덕분에 필자가 이렇게 소스를 여러분들에게 보여 줄 수 있었던 것 같습니다.    하지만 방심은 금물이듯 하루라도 백업하지 않으면 손가락이 마비될 정도로 백업해도 후회는 안 할 것입니다.    Mysql의 복구는 /usr/local/mysql/bin 디렉토리에 존재하는 isamchk를 이용해서 합니다.    isamchk의 경우 상당히 세밀한 방법으로 데이터베이스의 파손 여부를 점검하고 복구합니다.

isamchk 사용하기

#isamchk 옵션 테이블명


Mysql의 경우 대부분 isamchk를 돌릴 정도의 에러 발생은 적은 편입니다. 하지만, 기계는 얼마든지 예고없이

정지하거나 말썽 일으킬 소지를 가지고 있기 때문에 꼭 필요한 유틸리티입니다.


isamchk 옵션

    #isamchk --help로 다양한 옵션을 확인하기 바랍니다.
    #isamchk 테이블명 - 해당 테이블의 에러를 점검합니다. 상당한 부분까지 에러를 점검해 줍니다.
    #isamchk -e 테이블명 - 해당 테이블의 모든 데이터를 점검해 줍니다.  -i 옵션을 추가할경우 통계도 보여 줍니다.
    #isamchk -r -q 테이블명 - 해당 테이블의 에러를 쉽고 빠르게 복구해 줍니다.


이 같이 isamchk를 사용함으로써 안전하게 복구할 수 있습니다. 더 상세한 방법은 --help를 사용해서 확인하기 바랍니다.

mysql의 백업하기

#mysqldump -h 호스트명 -u 유저명 -p 데이터베이스명 > 백업되어질 파일명

Mysql 백업의 경우 /usr/local/mysql/bin 디렉토리에 존재하는 mysqldump를 이용하는 방법입니다.


그림. 9-38 mysqldump 예
    위의 test 데이터베이스를 test_bak.sql 파일로 저장했습니다. 물론 저장된 파일은 /bin 디렉토리에 생성됩니다.

저장된 파일 복구하기

#mysql -h 호스트명 -u 유저명 -p 데이터베이스명 < 백업된 파일명

해당 백업 파일을 다시 원상 복구 시킵니다.


참고로 mysql의 데이터베이스를 삭제하는 방법은 다음과 같습니다.

drop database 데이터베이스 명;

간혹 테이블 지우는 명령은 아는데 데이터베이스 지우는 명령을 모르는 분들이 많더군요.


다음 장에서는 Mysql 함수를 설명하겠습니다.


http://blog.naver.com/sharonichoya/220495410111

순차 정렬(Sequential Sort) 알고리즘 

이번에는 반복적인 방법으로 해결하는 순차 정렬(Sequential Sort) 알고리즘을 살펴볼게요. 

정렬 알고리즘은 배열의 자료를 원하는 순으로 배치하는 알고리즘을 말해요. 정렬 알고리즘은 입력 인자로 정렬할 자료들이 있는 배열의 시작 주소와 원소 개수, 비교 알고리즘이 필요합니다. 그리고 수행 후에는 배열 내의 자료들은 원하는 순서로 배치한 상태여야 합니다. 

순차 정렬은 맨 앞에서부터 제일 작은 원소를 배치하게 만들어 나가는 알고리즘이예요. 이를 위해 배치할 자리에 있는 원소를 뒤쪽에 있는 원소들과 비교하면서 작은 것을 발견하면 배치할 위치의 원소와 교환해요.

순차 정렬(base:배열의 시작 주소, n: 원소 개수, compare:비교 논리)

    반복(i:=0->n)

        반복(j:=i+1->n)

            조건(compare(base[i], base[j]) > 0)

                교환(base[i],base[j])

순차 정렬 알고리즘 바로가기


버블 정렬 (Bubble Sort) 알고리즘

이번에는 반복적인 방법으로 해결하는 버블 정렬 알고리즘을 살펴봅시다. 

 정렬 알고리즘은 배열의 자료를 원하는 순으로 배치하는 것을 말합니다. 이를 위해 입력 인자로 정렬할 자료들이 있는 배열의 시작 주소와 원소 개수, 비교 알고리즘을 전달합니다. 그리고 수행 후에는 배열 내의 자료들이 원하는 순서로 보관한 상태여야 합니다. 

 이 중에 버블 정렬은 앞에서부터 이웃하는 원소의 값을 비교하여 위치를 교환하는 것을 반복합니다. 이를 끝까지 수행하면 제일 큰 값이 맨 뒤에 위치합니다. 그리고 정렬할 개수를 1 줄인 후에 다시 반복합니다. 정렬할 원소의 개수가 1이면 모든 작업을 완료합니다. 

버블 정렬(base:배열의 시작 주소, n: 원소 개수, compare:비교 논리)

    반복(i:=n;  i>1  ; i:= i-1)

        반복(j:=1; j<i ; j:=j+1)

            조건(compare(base[j-1], base[j]) > 0)

                교환(base[j-1],base[j]) 

버블 정렬 알고리즘 바로가기

선택 정렬 (Selection Sort) 

 이번에는 반복 알고리즘일 이용하는 선택 정렬 알고리즘을 알아봅시다. 

 선택 정렬 알고리즘은 제일 큰 값을 찾아 맨 뒤의 요소와 교체하는 방법을 반복하여 전체를 정렬하는 알고리즘입니다. 물론 제일 작은 값을 찾아 맨 앞의 요소와 교체하는 방법을 반복할 수도 있습니다. 

 선택 정렬 알고리즘을 의사코드(pseudo code: 논리적인 수행 흐름을 이해할 수 있게 작성한 코드)는 다음과 같습니다. 

선택 정렬(base:컬렉션,n:원소 개수,compare:비교 논리)

    반복(i:=n;  i>1  ; i:= i-1)

        반복(max=0,j:=1; j<i ; j:=j+1)

            조건(compare(base[max], base[j]) < 0)

                max := j

        temp: = base[i-1]

        base[i-1] = base[max]

        base[max] = temp

선택 정렬 알고리즘 바로가기


삽입 정렬 (Insertion Sort) 

이번에는 반복 알고리즘 중에 삽입 정렬 알고리즘을 알아봅시다. 

 삽입 정렬 알고리즘은 점진적으로 정렬 범위를 넓혀 나가는 방식으로 정렬하는 알고리즘입니다. 이를 위해 새로운 범위에 포함하는 마지막 원소를 앞으로 이동하면서 자신보다 작은 요소를 찾을 때까지 이동하면서 자리를 교환합니다. 

삽입 정렬(base:컬렉션, n:원소 개수, compare:비교 논리)

    반복(i:=1;  i<n  ; i:= i+1)

        반복(j=i; j>0 ; j:=j-1)

            조건(compare (base [j-1], base [j]) < 0)

                temp: = base [j-1]

                base[j-1] = base [j]

                base[j] = temp

            아니면

                루프 탈출

삽입 정렬 알고리즘 바로가기

쉘 정렬 (Shell Sort) 

쉘 정렬은 삽입 정렬 알고리즘을 이용하는 정렬 방식입니다.

쉘 정렬은 같은 간격에 있는 원소들을 삽입 정렬 원리로 정렬하는 것을 반복합니다. 

간격의 초기값은 배열의 크기/2이며 간격이 1일 때까지 1/2로 줄이면서 반복합니다.

퀵 정렬(Quick Sort)

퀵 정렬 알고리즘은 재귀적인 방법으로 문제를 해결하는 알고리즘입니다. 

 퀵 정렬 알고리즘은 피벗 값을 선택하여 피벗 값보다 작은 값들은 왼쪽으로 보내고 큰 값들은 오른쪽으로 보낸 후에 이들 사이에 피벗을 위치시키는 원리를 이용합니다. 이후 피벗보다 작은 값들을 재귀 호출로 정렬하고 피벗보다 큰 값들도 재귀 호출로 정렬하는 방식입니다. 

 그런데 퀵 정렬은 어떠한 요소를 피벗으로 선택하냐에 따라 성능에 차이가 납니다. 만약 전체 요소의 중간 순위의 요소를 선택하면 재귀 호출에서 반씩 나누어 정렬을 하게 되어 좋은 성능을 발휘합니다. 하지만 가장 작은 값이나 가장 큰 값을 피벗으로 선택하면 최악의 성능을 발휘합니다. 

 여기에서는 맨 앞과 맨 뒤, 그리고 중간 위치의 요소를 비교하에 세 값 중에 중간 값을 피벗으로 선택할게요. 

퀵 정렬(base:컬렉션,n: 원소 개수, compare:비교 논리)

    조건(n<=1)

        종료

    피벗을 선택한다.

    피벗과 맨 앞의 요소를 교환한다.

    big:=0

    small:=n

    반복(big<small)

            반복(big:=big+1; big<n; big:=big+1)

                조건(compare(base[0],base[big])<0)

                    루프 탈출

            반복(small:small-1;small>0;small:small-1)

                조건(compare(base[0],base[small])>0)

                    루프 탈출

            조건(big<small)

                교환(base [big], base [small])

    교환(base [0], base [small])

    퀵 정렬(base,small, compare)

    퀵 정렬(base+big, n-big, compare)

 

 퀵 정렬 알고리즘의 탈출 조건은 n이 1보다 작거나 같을 때입니다.

퀵 정렬 알고리즘 바로가기

병합 정렬(Merge Sort, 합병 정렬) 알고리즘

이번에는 병합 정렬 알고리즘을 살펴봅시다. 

 병합 정렬 알고리즘은 배열을 작은 단위의 배열로 분할한 후에 분할한 배열을 정렬하고 이들을 다시 정렬하면서 전체 배열을 정렬하는 알고리즘입니다. 

병합 정렬(base:배열의 시작 주소, n: 원소 개수, compare:비교 논리)

    ah:= n/2

    bh:= n - ah;

    조건(n 1보다 작거나 같으면) 종료

    병합정렬(base,ah,compare)

    병합접열(base+ah,bh,compare)

    tbase에 동적 메모리 할당(원소크기*원소개수)

    메모리 복사(tbase,base)

    ai:=0

    bi:=ah

    i:=0

    반복(ai ah보다 작으면서 bi n보다 작다)

        조건(tbase[ai]가 tbase[bi]보다 작거나 같으면

            base[i] := base[ai]

            ai:= ai+1

        아니면

             base[i]:= base[bi]

             bi:= bi+1

        i:=i+1

    반복(ai가 ah보다 작다)

        base[i]:= tbase[ai]

        i:=i+1

        ai:=ai+1

    반복(bi가 n보다 작다)

        base[i]:= tbase[bi]

        i:=i+1

        bi:=bi+1

병합 정렬 알고리즘 바로가기

힙 정렬(Heap Sort)

힙 정렬은 힙 트리를 이용하는 알고리즘입니다. 최대 힙을 사용하면 크기 순(Ascend)으로 정렬하고 최소 힙을 사용하면 크기 역순(Descend)으로 정렬합니다. 

힙 정렬은 먼저 힙 트리를 구성합니다. 그리고 루트의 값과 맨 마지막 값을 교환한 후에 정렬 범위를 1 줄입니다. 이와 같은 작업을 반복하여 정렬 범위가 1일 때까지 반복합니다. 

최대 힙 트리에서 루트는 최대 값을 갖습니다. 따라서 마지막 값과 교환하면 제일 큰 값이 맨 뒤로 배치할 수 있습니다. 그리고 난 후에 정렬 범위를 줄여나가면 최종적으로 정렬 상태를 만들 수 있는 것입니다. 

힙 정렬(base:배열의 시작 주소, n: 원소 개수, compare:비교 논리)

    초기 힙 구성

    루트와 맨 마지막 자손 교환

    n 1 감소

    반복(n: n->1)

        힙 구성

        루트와 맨 마지막 자손 교환

        n을 1 감소

힙 정렬 알고리즘 바로가기

14.3.1 START 트랜잭션이 COMMIT 및 ROLLBACK 구문

START 거래
    [ transaction_characteristic[, transaction_characteristic] ...]

transaction_characteristic:
    일관성 스냅 샷
  | 읽기 쓰기
  | 읽기 전용

[WORK]를 BEGIN
COMMIT [WORK]와 [NO] CHAIN] [[NO] RELEASE]
ROLLBACK [WORK]와 [NO] CHAIN] [[NO] RELEASE] 없습니다
SET의 자동 커밋 = {0 | 1}

이 문은 사용을 제어 제공하는  거래를 :

  • START TRANSACTION 또는  BEGIN 새 트랜잭션을 시작합니다.

  • COMMIT 그 변경 사항을 영구적하고, 현재의 트랜잭션 (transaction)을 범했습니다.

  • ROLLBACK 롤의 변경을 취소, 현재의 트랜잭션 (transaction)를 백업합니다.

  • SET autocommit 비활성화 또는 현재 세션에 대한 기본 자동 커밋 모드를 가능하게한다.

기본적으로 MySQL은 실행됩니다  자동 커밋  활성화 모드. 이것은 바로 디스크에 대한 업데이트 (수정) 테이블, MySQL의 저장 업데이트가 영구적으로하는 문을 실행으로 있다는 것을 의미한다. 변화는 롤백 할 수 없습니다.

문 하나의 시리즈에 대한 암시 적으로 자동 커밋 모드를 해제하려면 사용  START TRANSACTION 문 :

트랜잭션을 시작;
SELECT @A가 : 표 FROM SUM (급여) = WHERE 유형 = 1;
UPDATE 표 2 SET 요약 = @ WHERE 유형 = 1;
범하다;

으로  START TRANSACTION당신과 거래를 종료 할 때까지, 자동 커밋이 비활성화 된 상태로 남아 COMMIT 나  ROLLBACK. 자동 커밋 모드는 이전 상태로 되돌아갑니다.

START TRANSACTION 트랜잭션 특성을 제어하는 ​​여러 가지 개질제를 허용한다. 여러 수식을 지정하려면 쉼표로 구분합니다.

  • WITH CONSISTENT SNAPSHOT 수정은 시작  일관된 읽기  그것을 할 수있는 스토리지 엔진을. 이 경우에만 적용됩니다  InnoDB. 효과는 발행과 동일  START TRANSACTION a로 다음을 SELECT 어떤에서  InnoDB 테이블. 참조  절 15.5.2.3를, "일관된 Nonlocking 읽고" . WITH CONSISTENT SNAPSHOT 수정은 현재의 트랜잭션 (transaction) 변경되지 않는  격리 수준을 , 그래서 현재 격리 수준이 일관된 읽기를 허용 한 경우에만이 일관된 스냅 샷을 제공합니다. 일관된 읽기를 허용하는 유일한 격리 수준이다  REPEATABLE READ. 다른 분리 레벨의 경우,  WITH CONSISTENT SNAPSHOT절은 무시됩니다. 때 MySQL은 5.7.2로, 경고가 생성됩니다  WITH CONSISTENT SNAPSHOT 절이 무시됩니다.

  • READ WRITE 및  READ ONLY 수정 트랜잭션 액세스 모드를 설정합니다. 그들은 허용하거나 트랜잭션에 사용되는 테이블에 대한 변경을 금지합니다. READ ONLY제한은 수정하거나 다른 트랜잭션에 볼 수 있습니다 모두 트랜잭션 및 비 트랜잭션 테이블 잠금에서 거래를 방지; 거래는 여전히 수정하거나 임시 테이블을 잠글 수 있습니다.

    MySQL은에 쿼리에 대한 별도의 최적화를 가능하게  InnoDB 트랜잭션이 읽기 전용으로 알려져있다 테이블. 지정은  READ ONLY 이러한 최적화가 읽기 전용 상태가 자동으로 판별 할 수없는 경우에 적용됩니다 보장합니다. 참조  섹션 9.5.3을, "최적화 InnoDB의 읽기 전용 거래" 더 많은 정보를 얻을 수 있습니다.

    접근 모드를 지정하지 않으면, 디폴트 모드가 적용된다. 기본이 변경되지 않는 한, 그것은 읽기 / 쓰기. 모두를 지정하는 것은 허용되지 않습니다  READ WRITE 과  READ ONLY 같은 성명에서.

    읽기 전용 모드에서, 그것은으로 만든 테이블을 변경할 수 남아  TEMPORARY DML 문을 사용하여 키워드를. DDL 문으로 변경은 영구 테이블로, 허용되지 않습니다.

    기본 모드를 변경하는 방법을 포함하여 트랜잭션 액세스 모드에 대한 자세한 내용은 다음을 참조 섹션 14.3.6, "SET 트랜잭션 구문"을 .

    경우  read_only 시스템 변수가 사용 가능으로, 명시 적으로 트랜잭션을 시작  START TRANSACTION READ WRITE 요구  SUPER 권한을.

중대한

(예 : JDBC 등) MySQL 클라이언트 응용 프로그램을 작성에 사용되는 대부분의 API는 (때로는한다) 대신에 보내는 사용할 수 있습니다 트랜잭션을 시작하는 자신의 방법을 제공하는  START TRANSACTION 클라이언트에서 문을. 참조  25 장,  커넥터 및 API에 대한 자세한 내용은, 당신의 API에 대한, 또는 문서를.

명시 적으로 자동 커밋 모드를 해제하려면 다음 문을 사용합니다 :

SET의 자동 커밋 = 0;

설정에 따라 자동 커밋 모드를 해제 한 후  autocommit 제로 변수 (예에 대한 것과 같은 트랜잭션 안전 테이블의 변경  InnoDB 또는  NDB) 즉시 영구적으로하지 않습니다. 당신은 사용해야  COMMIT 디스크에 변경 사항을 저장하거나  ROLLBACK 변경 사항을 무시.

autocommit 세션 변수이며, 각 세션에 대해 설정해야합니다. 각각의 새로운 연결을 위해 자동 커밋 모드를 사용하지 않으려면의 설명을 참조하십시오  autocommit에서 시스템 변수를  제 6.1.5, "서버 시스템 변수" .

BEGIN 과  BEGIN WORK 의 별칭으로 지원되는  START TRANSACTION 트랜잭션을 시작합니다. START TRANSACTION 표준 SQL 구문이고, 임시 트랜잭션을 시작하는 좋은 방법이며, 수정 허용 BEGIN 하지 않습니다.

BEGIN 문은 사용과 다른  BEGIN 시작 키워드  BEGIN ... END 화합물 문을. 후자는 트랜잭션을 시작하지 않습니다. 참조 섹션 14.6.1를, "BEGIN ... END 복합 문 구문" .

노트

저장된 모든 프로그램 (저장 프로 시저 및 함수, 트리거, 이벤트), 파서 취급 이내  BEGIN [WORK] (a)의 시작으로  BEGIN ... END 블록. 와이 컨텍스트에서 트랜잭션을 시작  START TRANSACTION 하는 대신.

옵션  WORK 키워드에 대한 지원  COMMIT 및  ROLLBACK한,  CHAIN 그리고  RELEASE 절을. CHAIN 및 RELEASE 거래 완료 추가 제어에 사용될 수있다. 의 값은  completion_type 시스템 변수는 기본 완료 동작을 결정합니다. 참조  섹션 6.1.5, "서버 시스템 변수"를 .

AND CHAIN 절은 현재 종료하자마자 시작 새로운 트랜잭션을 발생, 새로운 트랜잭션 방금 종료 트랜잭션과 같은 분리 레벨을 갖는다. RELEASE 절은 현재 트랜잭션을 종료 한 후 현재 클라이언트 세션을 분리하도록 서버를 발생합니다. 포함  NO 키워드를 억제  CHAIN 또는  RELEASE 경우에 유용 할 수 있습니다 완료,  completion_type 시스템 변수가 체인의 원인 또는 기본적으로 완료를 해제하도록 설정되어 있습니다.

트랜잭션을 시작하면 보류중인 트랜잭션이 커밋됩니다. 참조  섹션 14.3.3, "는 암시 적 커밋 원인 문" 자세한 내용을.

트랜잭션을 시작도 함께 획득 한 테이블 잠금이 발생  LOCK TABLES 하면 실행했던 것처럼, 발매 예정을  UNLOCK TABLES. 트랜잭션을 시작하기로 취득하는 글로벌 읽기 잠금을 해제하지 않습니다  FLUSH TABLES WITH READ LOCK.

최상의 결과를 얻으려면, 거래는 하나의 거래 안전 스토리지 엔진에 의해 관리 만 테이블을 사용하여 수행해야합니다. 그렇지 않으면, 다음과 같은 문제가 발생할 수있다 :

  • 둘 이상의 트랜잭션 안전 저장 엔진 (예에서 테이블을 사용하는 경우  InnoDB) 및 트랜잭션 격리 수준되지 않습니다  SERIALIZABLE, 하나의 트랜잭션이 커밋 할 때 같은 테이블을 사용하는 다른 지속적인 거래 만 일부 변경을 볼 가능성이있다 첫 번째 트랜잭션에 의해 만들어진. 즉, 트랜잭션의 원 자성이 혼합 된 엔진과 발생할 수 있습니다 일관성을 보증 할 수 없습니다. (혼합 엔진 트랜잭션이 빈번하지 않으면 사용할 수  SET TRANSACTION ISOLATION LEVEL 로 분리 레벨을 설정할  SERIALIZABLE 필요에 따라 트랜잭션 단위에서).

  • 당신이 트랜잭션 내에서 트랜잭션 안전하지 않은 테이블을 사용하는 경우, 해당 테이블의 변경에 관계없이 자동 커밋 모드의 상태를 한 번에 저장됩니다.

  • 당신이 실행하는 경우  ROLLBACK 트랜잭션 내에서 비 트랜잭션 테이블을 업데이트 한 후 문을 ER_WARNING_NOT_COMPLETE_ROLLBACK 경고가 발생합니다. 트랜잭션 안전 테이블에 대한 변경 사항은 nontransaction 안전 테이블에 대한 변경 사항을 롤백하지만되지 않습니다.

각 트랜잭션에, 한 덩어리에 바이너리 로그에 저장됩니다  COMMIT. 롤백 트랜잭션은 기록되지 않습니다. ( 예외 :. 비 트랜잭션 테이블에 대한 수정은 롤백 할 수 없습니다 롤백 트랜잭션이 비 트랜잭션 테이블에 대한 수정이 포함 된 경우, 전체 트랜잭션이 함께 기록됩니다  ROLLBACK비 트랜잭션 테이블에 대한 수정이 복제되도록 끝에 문.) 참조  섹션 6.4.4, "바이너리 로그" .

당신은와의 거래에 대한 격리 수준 또는 액세스 모드를 변경할 수 있습니다  SET TRANSACTION 문을.참조  섹션 14.3.6, "SET 트랜잭션 구문"을 .

위로 롤링 (오류가 발생하면, 예를 들어)을 명시 적으로 요청하는 데 사용하지 않고 암시 적으로 발생할 수있는 느린 동작 할 수있다. 이 때문에,  SHOW PROCESSLIST 표시  Rolling back 에서  State 세션의 열뿐만 아니라, 명시 적으로 수행 롤백 용  ROLLBACK 문장뿐만 아니라 내재 롤백 대한.


[MySQL] alter table 명령어

※ 경고
높이뜬새의 모르는 사람이 없는 팁시리즈 입니다.
이 팁시리즈는 지면낭비라는 항의시에 즉각 중단됩니다.


alter table 명령어는 모르시는 분이 없듯이 테이블의 스키마를 변경 할 수 있게 끔 해주는 아주 유용한 명령어입니다.
alter table 명령어가 없으면 아주 끔찍한 일이 벌어질 수도 있습니다. create table...과 drop table의 남발!! 정말 끔찍하지 않습니까? 그런데 테이블의 스키마가 무엇이냐구요? 

이것 역시 모르시는 분이 단 한사람도 없겠지만 쉽게 말해서 테이블의 구조라고 이해하시면 됩니다. 관계형 데이타베이스를 이용할려면 이놈들을 속성(attribute)들로 뭉쳐진 의미있는 정보의 단위로 이끌어 내야 하는데 이러한 것을 엔티티(Entity)라고 합니다. 정확히 말하면 이 엔티티의 구조를 설명한 게 스키마입니다! 이 스키마는 논리적 스키마와 물리적 스키마로 분리해서 부르는데 특정회사의 데이타베이스에 맞게 스키마를 표현하게 되면 물리적 스키마라고 부릅니다.
엔티티가 물리적 스키마에 의해서 실제 데이타베이스에 적용된 것이 테이블입니다. 더 자세한 사항은 시중에 나와 있는 데이타베이스 설계 관련 서적을 참고하세요! 없는 실력으로 더 자세한 설명은 도저히 무리입니다.^^

alter table 명령어는 각 회사의 데이타베이스에 따라서 약간씩 차이가 있습니다. 여기서는 제가 맨날 까먹는 alter table 명령어 중 MySQL 용 몇개를 오로지 안까먹기 위해서 적어둘려고 합니다. 사실 팁은 아닙니다.^^

일단 alter table 명령어를 쓰기 위해서 간단한 테이블을 하나 만들겠습니다. 테이블 이름은 mytable이고 컬럼은 id와 name만 가지고 있는 아주 간단한 예제용 테이블입니다.
 

mysql> create table mytable ( 
        ->id varchar(12) not null, 
        ->name varchar(20) not null );
 

아주 simple 하고 좋은데 id와 name외에 주소를 뜻하는 addr 컬럼을 추가해 봐야 하겠습니다.

[테이블에 새로운 컬럼 추가하기]

형식) alter table [테이블명] add column [추가할 컬럼명] [추가할 컬럼 데이타형]
 

mysql> alter table mytable add column addr varchar(70) not null;


뒤에 not null 을 안 붙이면 null 허용으로 컬럼이 추가됩니다. 위의 명령어에 의해서 addr 이라는 컬럼이 mytable에 추가 되었습니다. 그런데 주소를 뜻하는 addr 컬럼의 자릿수가 웬지 부족해 보입니다. varchar(100)으로 늘려봐야 겠습니다.

[테이블의 컬럼 타입 변경하기]

형식) alter table [테이블명] modify column [변경할 컬럼명] [변경할 컬럼 타입]
 

mysql> alter table mytable modify column addr varchar(100) not null;


desc mytable로 확인해 보면 addr 컬럼이 varchar(100)으로 정확하게 변경되었습니다.주소 컬럼에는 맞지 않겠지만 addr 컬럼을 int 형에 null 허용으로 변경해 보고 싶습니다.
 

mysql> alter table mytable modify column addr int;


desc mytable로 확인 해보니 int 형에 null 허용으로 변경되었습니다. 주소가 int 형이라니 말이 안됩니다. int 형이라면 아무래도 나이가 맞을 것 같습니다. addr의 컬럼명 자체를 age 로 바꿔야 겠습니다. 

[테이블의 컬럼 이름 변경하기] 

형식) alter table [테이블명] change column [기존 컬럼명] [변경할 컬럼명] [변경할 컬럼 타입]
 

mysql> alter table mytable change column addr age int not null;


addr 컬럼명이 age 라는 컬럼명으로 정확히 바뀌었습니다. 그치만 애초에 age 컬럼은 필요가 없었습니다. 테이블에서 age 컬럼을 삭제 해야 하겠습니다.

[테이블 컬럼 삭제하기]


형식) alter table [테이블명] drop column [삭제할 컬럼명]
 

mysql> alter table mytable drop column age;


desc mytable로 확인해보니 age 컬럼이 삭제되어져 있습니다. 뭔가 허전합니다. id 컬럼에 인덱스(Index)를 주면 허전함이 달래질 것 같습니다.

[테이블 컬럼에 인덱스 주기]

형식) alter table [테이블명] add index 인덱스명( 인덱스를 줄 컬럼1, 인덱스를 줄 컬럼2,...)
 

mysql> alter table mytable add index myindex( id );


위에서는 myindex 라는 인덱스명으로 id 컬럼에 index 가 추가되었습니다. 인덱스를 줄 컬럼을 한개 이상 써 넣으면 복수개의 컬럼에 대해서도 인덱스를 생성할 수 있습니다. 이번에는 id에 주어진 index 를 삭제해 보겠습니다.

[테이블 컬럼에 인덱스 삭제하기]


형식 ) alter table [테이블명] drop index 인덱스명
 

mysql> alter table mytable drop index myindex;


위에서는 처음에 줬던 index 인 myindex 를 삭제하고 있습니다. 참고로 인덱스를 확인하기 위해서는 아래의 명령어를 쓰시면 됩니다. mytable은 테이블 명입니다.
 

mysql> show index from mytable;


alter table로 인덱스도 추가하고, 날려봤는데 primary key 는 못 만들까요? 당연히 만들수 있습니다. 그대신 primary key 는 조건이 있습니다. primary key를 만들려는 컬럼에 조건이겠지요!!! 레코드에 값이 추가 안 되어있을 때는 괜찮지만, 값이 들어있다면 column 에 null 값이 들어 있는지, 중복된 값이 존재하는 column 인지를 따져봐야 합니다. null 값이 없고, 중복되는 column 이 없다면 primary key를 만들 수 있습니다.


[테이블에 primay key 만들기]


형식 ) alter table [테이블명] add primary key ( 키를 줄 column명1, 키를 줄 column명2, ... );
 

mysql> alter table mytable add primary key ( id );


위에서는 mytable의 id 컬럼에 primary key 를 만들고 있습니다. primary key 는 composite key 가 가능하므로 여러개의 column을 묶어서 primary key 로 사용할 수도 있습니다.


만들어진 primary key를 지워야 할 때도 있습니다.


[테이블에 primay key 삭제하기]

형식) alter table [테이블명] drop primary key;
 

mysql> alter table mytable drop primary key;


위에서는 mytable에 만들었던 primary key를 삭제하고 있습니다. 참고로 primary를 만들때는 index가 자동으로 primary key 컬럼에 추가됩니다.

primary key도 다시 날려버리고 아주 좋습니다. 마지막으로 테이블명을 변경해 보고 싶습니다. 사실 mytable이라는 테이블명이 아주 마음에 안 들었습니다.

[테이블 명 바꾸기]


형식) alter table [원본 테이블명] rename [새로운 테이블명];

mysql> alter table mytable rename utable;


alter table에 대해서 대충 알아 봤습니다. 그 유명한 다른 primary key를 뽀려온 foreign key 에 대한 부분도 정리 했으면 좋겠는데.. 오늘은 제 시간 관계 상 여기서 정리를 끝내겠습니다.


이번 강좌에서는

  • 포인터에 대한 완벽한 이해
  • *, & 단항 연산자의 의미


  우왕~ 안녕하세요 여러분. 아마 C 언어를 배웠거나 배우고 있는 사람들은 포인터에 대해 익히 들어 보셨을 것 입니다. 이해하기 힘들기로 악명 높은 그 포인터를 말이죠. 하지만, 저와 함께 한다면 큰 무리 없이 배우 실 수 있을 것이라 생각됩니다.


  포인터를 이해하기 앞서
 

  앞서 3 강에서 이야기 하였지만 모든 데이터들은 메모리 상에 특정한 공간에 저장 되어 있습니다. 우리는 앞으로 편의를 위해, 메모리의 특정한 공간을 '방' 이라고 하겠습니다. 즉, 각 방에는 데이터들이 들어가게 되는 것 입니다. 보통 사람들은 한 방의 크기를 1 바이트 라고 생각합니다. 우리가 만약 int 형 변수를 정의한다면 4 바이트 이므로 메모리 상의 4 칸을 차지하게 됩니다. 그런데 말이죠. 프로그램 작동 시 컴퓨터는 각 방에 있는 데이터를 필요로 하게 됩니다. 따라서, 서로 구분하기 위해 각 방에 고유의 '주소(address)' 를 붙여 주었습니다. 우리가 아파트에서 각 집들을 호수로 구분하는 것 처럼 말입니다. 예를 들어 우리가 아래와 같은 int 변수 a 를 정의하였다면 특정한 방에 아래 그림 처럼 변수 a 가 정의됩니다.

int a = 123; // 메모리 4 칸을 차지하게 한다.



메모리에 123 이란 수가 있고 이 수는 메모리에 0x152839 (앞에 0x 는 이 수가 16 진수로 표시되었다는 것을 의미해요) 에 위치



이 때, 0x152839 는 제가 아무렇게나 정한 이 방의 시작 주소 입니다. 참고로, 0x 가 뭐냐고 물어보는 사람들이 있을 텐데, 이전 강좌에서도 이야기 하였지만 16 진수라고 표시한 것 입니다. 즉, 16 진수로 152839 (10 진수로 1386553) 라는 위치에서 부터 4 바이트의 공간을 차지하며 123 이라는 값이 저장되어 있게 하라는 뜻이지요.

그렇다면 아래와 같은 문장은 어떻게 수행 될까요?

a = 10;


사실 컴파일러는 위 문장을 아래와 같이 바꿔주게 됩니다. 

메모리 0x152839 위치에서 부터 4 바이트의 공간에 있는 데이터를 10 으로 바꾸어라! 

결과적으로, 컴퓨터 내부에서는 올바르게 수행되겠지요. 
참고적으로 말하는 이야기 이지만 현재 (아마 이 블로그에 접속하는 사람들 중 99% 이상이) 많은 사람들은 32 비트 운영체제를 사용하고 있습니다. 이 32 비트에서 작동되는 컴퓨터들은 모두 주소값의 크기가 32 비트 (즉, 4 바이트.. 까먹었다면 2-3 강 참조) 로 나타내집니다. 즉 주소값이 0x00000000 ~ 0xFFFFFFFF 까지의 값을 가진다는 것이지요. 어랏! 조금 똑똑하신 분들이라면 32 비트로 사용할 수 있는 주소값의 가지수는 2 의 32 승 바이트, 즉 RAM 은 최대 4 GB 까지 밖에 사용할 수 없다는 사실을 알 수 있습니다. 맞습니다. 이 때문에 32 비트 운영체제에서는 RAM 의 최대 크기가 4 GB 로 제한되지요(즉, 돈을 마이 들여서 RAM 10GB 로 만들어도 컴퓨터는 4 GB 까지 밖에 인식하지 못합니다. 어찌 이렇게 슬플수가..) 1

여기까지는 상당히 직관적이고 단순해서 이해하기 쉬웠을 것 입니다. 그런데 C 를 만든 사람은 아주 유용하면서도 골때리는 것을 하나 새롭게 만들었습니다. 바로 '포인터(pointer)' 입니다. 영어를 잘하는 분들은 이미 아시겠지만 '포인터' 라는 단어의 뜻이 '가리키는 것(가르켜지는 대상체를 말하는 것이 아닙니다)' 이란 의미를 가지고 있습니다. 

사실, 포인터는 우리가 앞에서 보았던 int 나 char 변수들과 다른 것이 전혀 아닙니다. 포인터도 '변수' 입니다. int 형 변수가 정수 데이터, float 형 변수가 실수 데이터를 보관했던 것 처럼, 포인터도 특정한 데이터를 보관하는 '변수' 입니다. 그렇다면 포인터는 무엇을 보관하고 있을 까요? 

바로, 특정한 데이터가 저장된 주소값을 보관하는 변수 입니다. 여기서 강조할 부분은 '주소값' 이라는 것 이지요. 여기서 그냥 머리에 박아 넣어 버립시다. 이전에 다른 책들에서 배운 내용을 싹 다 잊어 버리고 그냥 망치로 때려 넣듯이 박아버려요. 포인터에는 특정한 데이터가 저장된 주소값을 보관하는 변수 라고 말이지요. 크게 외치세요. '주소값!!!!!' 

암튼, 뇌가 완전히 세뇌되었다고 생각하면 다음 단계로 넘어가도록 하겠습니다. 아직도 이상한 잡념이 머리에 남아 있다면 크게 숨을 호흡하시고 주소값이라고 10 번만 외쳐 보세요.. 

자. 되었습니다. 이제 포인터의 세계로 출발해 봅시다. 뿅

  포인터
 

다시 한 번 정리하자면

"포인터" : 메모리 상에 위치한 특정한 데이터의 (시작)주소값을 보관하는 변수!

우리가 변수를 정의할 때 int 나 char 처럼 여러가지 형(type) 들이 있었습니다. 그런데 놀랍게도 포인터에서도 형이 있습니다. 이 말은 포인터가 메모리 상의 int 형 데이타의 주소값을 저장하는 포인터와, char 형 데이터의 주소값을 저장하는 포인터가 서로 다르다는 말입니다. 응?? 여러분의 머리속에는 아래와 같은 생각이 번개 처럼 스쳐 지나갈 것입니다. 

"야 이 Psi 같은 놈아. 아까 포인터는 주소값을 저장하는 거래며. 근데 우리가 쓰는 컴퓨터에선 주소값이 무조건 32 비트, 즉 4 바이트래며!! 그러면 포인터의 크기는 다 똑같은것 아냐? 근데 왜 포인터가 형(type)을 가지는 건데!! 아아아아악" 

휴우우. 진정좀 하시고. 여러분 말이 백번 맞습니다 - 단, 현재 까지 배운 내용을 가지고 생각하자면 말이지요. 포인터를 아주 조금만 배우면 왜 포인터에 형(type) 이 필요한지 알게 될 것입니다. 

C 언어에서 포인터는 다음과 같이 정의할 수 있습니다. 

(포인터에 주소값이 저장되는 데이터의 형) *(포인터의 이름);

혹은 아래와 같이 정의할 수 도 있습니다.

(포인터에 주소값이 저장되는 데이터의 형)* (포인터의 이름);

  예를 들어 p 라는 포인터가 int 데이터를 가리키고 싶다고 하면 ...

int *p; // 라고 하거나
int* p; // 로 하면 된다

라 하면 올바르게 됩니다. 즉 위 포인터 p 는 int 형 데이터의 주소값을 저장하는 변수가 되는 것 입니다. 와우! 

  & 연산자
 

그런데 말입니다. 아직도 2% 부족합니다. 포인터를 정의하였으면 값을 집어 넣어야 하는데, 도대체 우리가 데이터의 주소값을 어떻게 아냐는 말입니까? 하지만, 여러분의 이러한 욕구를 충족시키는 연산자가 C 에 (당연히) 있습니다. 바로 & 연산자 이지요. 

그런데, 아마 복습을 철저하게 잘하신 분들은 당황할 수 도 있습니다. 왜냐하면 & 가 AND 연산자이기 때문입니다. (4 강 참조) 그런데, & 연산자는 두 개의 피연산자를 필요로 했습니다. 즉,

a & b; //o.k
a & // NOT ok

와 같이 언제나 2 개가 필요 하다는 것이지요. 그런데, 여기에서 소개할 & 연산자는 오직 피연산자가 1 개인 연산자 입니다. (이러한 연산자를 단항(unary) 연산자라 합니다) 즉, 위의 AND 연산자와 완전히 다르 다는 것이지요. 이는 데이터의 주소값을 불러 옵니다. 사용은 그냥 아래와 같은 꼴로 사용해 주면 됩니다.

& (주소값을 계산할 데이터)

백설(說)이 불여일행(行). 한 번 프로그램을 짜 봅시다. 

/* & 연산자 */
#include <stdio.h>
int main()
{
    int a;
    a = 2;

    printf("%x \n", &a); 
    return 0;
}

성공적으로 컴파일 했다면


와 같이 나옵니다. 참고로, 여러분의 컴퓨터에 따라 결과가 다르게 나올 수 도 있습니다. 사실, 저와 정말 인연 이상의 무언가가 있지 않는 이상 전혀 다르게 나올 것 입니다. 더 놀라운 것은 실행할 때 마다 결과가 달라질 것입니다. 

2 번째 실행한 것


위와 같이 나오는 이유는 나중에 설명하겠지만 주목할 것은 '값' 이 출력되었다는 것 입니다. 

    printf("%x \n", &a);

위 문장에서 &a 의 값을 16 진수 (%x) 로 출력하라고 명령하였습니다. 근데요. 눈치가 있는 사람이라면 금방 알겠지만 위에서 출력된 결과는 4 바이트(16 진수로 8 자리)가 아닙니다! (여러분의 컴퓨터는 다를 수 있습니다. 아무튼..) 하지만 저는 32 비트 운영체제를 사용하고 있습니다. 그렇다면 뭐가 문제인가요? 사실, 문제는 없습니다. 단순히 앞의 0 이 잘린 것 이지요. 주소값은 언제나 4 바이트 크기, 즉 16 진수로 8 자리 인데 앞에 0 이 잘려서 출력이 안된 것일 뿐입니다. 따라서 변수 a 의 주소는 아마도 0x001EF8D4 가 될 것입니다. 

 아무튼 위 결과를 보면, 적어도 제 컴퓨터 상에선 int 변수 a 는 메모리 상에서 0x001EF8D4 를 시작으로 4 바이트의 공간을 차지하고 있었다는 사실을 알 수 있습니다. 

 자, 이제 & 연산자를 사용하여 특정한 데이터의 메모리 상의 주소값을 알 수 있다는 사실을 알았으니 배고픈 포인터에게 값을 넣어 봅시다. 

/* 포인터의 시작 */
#include <stdio.h>
int main()
{
    int *p;
    int a;

    p = &a;

    printf("포인터 p 에 들어 있는 값 : %x \n", p);
    printf("int 변수 a 가 저장된 주소 : %x \n", &a);

    return 0;
}

실행해 보면 많은 이들이 예상했던 것 처럼.... 


똑같이 나옵니다. 어찌 보면 당연한 일입니다. 

    p = &a;

에서 포인터 p 에 a 의 주소를 대입하였기 때문이죠. 참고로, 한 번 정의된 변수의 주소값은 바뀌지 않습니다. 따라서 아래 printf 에서 포인터 p 에 저장된 값과 변수 a 의 주소값이 동일하게 나오게 됩니다. 어때요. 쉽죠? 

  * 표  
 

  이제, 드디어 포인터의 핵심에 다다랐습니다. 현재 까지 우리가 배운 바로는 "포인터는 특정한 데이터의 주소값을 보관한다. 이 때 포인터는 주소값을 보관하는 데이터의 형에 * 를 붙임으로써 정의되고, & 연산자로 특정한 데이터의 메모리 상의 주소값을 알아올 수 있다" 까지 알고 있습니다. 참고로 아래에서 설명하는 * 는 앞서 포인터를 정의할 때 사용하였던 * 와 다른 의미를 지닌 다는 사실을 알고 있으세요. 

  앞서 & 연산자가 2% 부족한 부분을 채워준다고 했지만 안타깝게도 1% 가 남았습니다. 하지만 * 연산자가 이 1% 를 채워질 것 입니다.... 잠깐만. * 연산자?? 어디서 많이 들어본 것 같네요.. 맞아요. * 연산자도 & 처럼 피연산자를 2 개 가질 때 에는 곱셈 연산자로 사용됩니다. 즉

a * b; // a 와 b 를 곱한다.
a *; // Not OK

하지만 이 연산자는 위 & 처럼 1 개의 피연산자를 가지는 단항 연산자 입니다. * 연산자는 쉽게 풀이하자면

 "나(포인터)를 나에게 저장된 주소값에 위치한 데이터라고 생각해!"

  라는 의미의 연산자 입니다. 한 번 아래 예제를 봅시다. 

/* * 연산자의 이용 */
#include <stdio.h>
int main()
{
    int *p;
    int a;

    p = &a;
    a = 2;

    printf("a 의 값 : %d \n", a);
    printf("*p 의 값 : %d \n", *p);

    return 0;
}

성공적으로 컴파일 한다면 


가 됩니다. 

    int *p;
    int a;

일단 int 데이터를 가리키는 포인터 p 와 int 변수 a 를 각각 정의하였습니다. 평범한 문장 이지요. 

    p = &a;
    a = 2;

그리고 포인터 p 에 a 의 주소를 집어 넣었습니다. 그리고 a 에 2 를 대입하였습니다. 

    printf("a 의 값 : %d \n", a);
    printf("*p 의 값 : %d \n", *p);

일단 위의 문장은 단순 합니다. a 의 값을 출력하란 말이지요. 당연하게도 2 가 출력됩니다. 그런데, 아래에서 *p 의 값을 출력하라고 했습니다. * 의 의미는 앞서, "나를 나에 저장된 주소값에 해당하는 데이터로 생각하시오!" 로 하게 하는 연산자라고 하였습니다. 즉, *p 를 통해 "p 에 저장된 주소(변수 a 의 주소)에 해당하는 데이타, 즉 변수 a" 를 의미할 수 있게 되었습니다. 다시 말해 *p 와 변수 a 는 정확히 동일합니다. 즉, 위 두 문장은 아래 두 문장과 10000% 일치합니다.

   printf("a 의 값 : %d \n", a);
    printf("*p 의 값 : %d \n", a);

마지막으로 * 와 관련된 예제 하나를 더 살펴 봅시다.

/* * 연산자 */
#include <stdio.h>
int main()
{
    int *p;
    int a;

    p = &a;
    *p = 3;

    printf("a 의 값 : %d \n", a);
    printf("*p 의 값 : %d \n", *p);

    return 0;
}

성공적으로 컴파일 하였다면


아마 많은 여러분들이 예상했던 결과 이길 바랍니다^^

    p = &a;
    *p = 3;

위에서도 마찬가지로 p 에 변수 a 의 주소를 집어 넣었습니다. 그리고 *p 를 통해 "나에 저장된 주소(변수 a 의 주소)에 해당하는 데이터(변수 a) 로 생각하시오" 를 의미하여 *p = 3 은 a = 3 과 동일한 의미를 지니게 되었습니다. 어때요. 간단하지요? 이로써 여러분은 포인터의 50% 이상을 이해하신 것 입니다~~! 짝짝짝짝

 자. 그럼 '포인터' 라는 말 자체의 의미를 생각해 봅시다. int 변수 a 와 포인터 p 의 메모리 상의 모습을 그리면 아래와 같습니다.

포인터 p 도, a 도 메모리 상에 각각 존재합니다만, 그 위치에는 a 의 경우 3 이 있지만 p 의 경우 a 의 주소값이 있습니다.

참고로 주소값은 제가 임의로 정한 것 입니다.


  즉, 포인터  p 는 * 를 통해 a 를 의미 할 수 (가리 킬 수) 있게 되었지요. 흔히 많은 책들은 포인터 p 가 변수 a 를 가리키고 있다 라고 말합니다. 사실 저는 이 말이 여러분에게 어렵게 다가올 까봐 여태까지 하고 있지 않았지만 아무튼, 포인터 p 에 어떤 변수 a 의 주소값이 저장되어 있다면 '포인터 p 는 변수 a 를 가리킨다' 라고 말합니다. 참고적으로 말하지만 포인터 또한 엄연한 변수 이기 때문에 특정한 메모리 공간을 차지합니다. 따라서 위 그림과 같이 포인터도 자기 자신만의 주소를 가지고 있지요. 

 이제 여러분들은 포인터에 왜 '형(type)' 이 필요한지 이야기 할 수 있는 단계가 되었습니다. (아마 여러분들 중 반 수 이상은 이미 짐작하고 계실 것 입니다.) 다시 말해 '형' 이 없다는 것은 포인터가 자신이 가리키고 있는 대상에 대해 어떠한 정보도 가지지 않아도 된다는 것 입니다. 여기서 포인터를 선언하기 위해 pointer 라는 저 만의 키워드를 이용했다고 합시다. (실제로 이런게 사용되는 것이 아닙니다;;;) 

int a;
pointer *p;
p = &a;
*p = 4;

자. 위 명령이 올바른 결과를 출력할까요? 포인터 p 에는 명백히 변수 a 의 '시작 주소' 가 들어 있습니다. '시작 주소' 란 말입니다. 즉, *p 라고 했을 때 포인터 p 에는 자신이 가리키는 대상의 시작 주소가 있지만 대상의 크기에 대한 정보가 없습니다. 헉! 컴퓨터는 *p 라고 했을 때 메모리에서 0x12345678 로 부터 (&a 가 0x12345678 이라 가정합시다) 몇 개의 바이트를 더 읽어 들어서
값을 변경해야 할 지 모른다는 말입니다. 결국 포인터는 쓸모 없게 됩니다. 

하지만, 우리가 여태까지 해왔던 것 처럼 

int a;
int *p;
p = &a;
*p = 4;

라고 한다면 어떨 까요? 컴퓨터는 0x12345678 로 부터 포인터 p 가 int * 라는 사실을 보고 "아하. 이 포인터는 int 데이터를 가리키는 구나!" 라고 알게 되어 정확히 4 바이트를 읽어 들어 값을 바꾸게 됩니다. 따라서 정확한 값이 출력될 수 있겠지요. 

여러분이 현재 까지 배운 내용에 대해 완벽하게 이해를 하고 있다면 아래와 같은 궁금증이 생길 것 입니다. 

"야. 그런데 말야. 주소값은 무조건 4 바이트 잖아? 그러면 int *p; 처럼 귀찮게 * 을 붙이지 말고 int p; 라고 한다음에 p = &a; 라고 해도 상관 없는거 아니야? 그지? 그지? 그지? " 

  훌륭한 생각입니다. 한 번 해봅시다. 

/* 될까? */
#include <stdio.h>
int main()
{
    int p;
    int a;

    p = &a;
    a = 3;

    printf("*p 의 값? : %d \n", *p);

    return 0;
}

안타깝게도 printf 부분에서 아래와 같은 오류가 출력됩니다.

error C2100: 간접 참조가 잘못되었습니다.

사실, 우리가 현재 까지 배운 내용을 바탕으로 이해해 보자면 위에서 왜 오류가 발생하는지 이해하기 힘듦니다. 그냥 단순히 * 연산자는 포인터들에게만 적용된다는 사실만을 알아 두시면 감사하겠습니다.

/* 포인터도 변수이다 */
#include <stdio.h>
int main()
{
    int a;
    int b;
    int *p;

    p = &a;
    *p = 2;
    p = &b;
    *p = 4;

    printf("a : %d \n", a);
    printf("b : %d \n", b);
    return 0;
}

성공적으로 컴파일 하였다면



    p = &a;
    *p = 2;
    p = &b;
    *p = 4;

  사실, 이런 예제까지 굳이 보여주어야 하나 하는 생각이 들었지만 그래도 혹시나 하는 마음에 했습니다. 앞에서도 말했듯이 포인터는 '변수' 입니다. 즉, 포인터에 들어간 주소값이 바뀔 수 있다는 것이지요. 위와 같이 처음에 a 를 가리켰다가, (즉 p 에 변수 a 의 주소값이 들어갔다가) 나중에 b 를 가리킬 수 (즉 p 에 변수 b 의 주소값이 들어감) 있다는 것 이지요. 뭐 특별히 중요한 예제는 아니였습니다만. 나중에 상수 포인터, 포인터 상수에 대해 이야기 하면서 다시 다루어 보도록 하겠습니다.

마지막으로, 강의를 마치며 여러분에게 포인터에 대해 완벽히 뇌리에 꽂힐 만한 동화를 들려드리겠습니다.

옛날 옛날에 .. 대략 2 년 전에 (뭐.. 전 여러분과 옛날의 정의가 다릅니다ㅋ) 변철수, 변수철, 포영희라는 세 명의 사람이 OO 아파트에 살고 있었습니다. 

int chul, sue; 
int *young;

그런데 말이죠. 포영희는 변철수를 너무나 좋아한 나머지 자기 집 대문 앞에 큰 글씨로 "우리집에 오는 것들은 모두 철수네 주세요" 라고 써 놓고 철수네 주소를 적어 놓았습니다

young = &chul;

어느날 택배 아저씨가 영희네 집에 물건을 배달하러 왔다가 영희의 메세지를 보고 철수네에 가져다 주게 됩니다.

*young = 3; // 사실 chul = 3 과 동일하다!

영희에 짝사랑이 계속 되다가 어느날 영희는 철수 보다 더 미남인 수철이를 보게 됩니다. 결국 영희는 마음이 변심하고 수철이를 좋아하기로 했죠. 영희는 자기 대문 앞에 있던 메세지를 떼 버리고 "우리집에 오는 것은 모두 수철이네 주세요." 라 쓰고 수철이네 주소를 적었습니다.

young = &sue;

며칠이 지나 택배 아저씨는 물건을 배달하러 영희네에 왔다가 메세지를 보고 이번엔 수철이네에 가져다 줍니다.

*young = 4; // 사실 sue = 4 와 동일하다

이렇게 순수한 사랑이 OO 아파트에서 모락 모락 피어났습니다..... 끝

return 0; // 종료를 나타내는 것인데, 아직 몰라도 되요. (정확히 말하면 리턴...) 

생각해 볼 문제 

* 와 & 연산자의 역할이 무엇인지 말해보세요 (난이도 : 下)

int **a; 와 같은 이중 포인터(double-pointer) 에 대해 생각해 보세요 (난이도 : 中上)


함수에서 외부의 값(실인수)을 전달받기 위해 사용되는 변수를  형식인수라한다. 이 형식인수에 외부값(실인수)이 전달되는 방식에 따라 값에 의한 호출과 참조에 의한 호출로 나뉘어 지는데, 값호출과 참조호출로 나뉜다. 일반적으로 실인수를 형식인수에 전달시 대입연산을 하는 경우와 마찬가지 동작이 일어나, 실인수의 값을 형식인수에 대입을 한다. 메모리상에선 실인수의 값을 복사한 후 형식인수에게 할당을 한다. 그 결과 같은 값을 지닌 변수가 서로 다른 메모리 공간에 2개 존재하게 된다. 이 경우 형식인수값을 조작하더라도 실인수의 값은 어떤 영향도 받지 않다. 이런 호출방식을 값호출이라고한다.


그럼 참조에 의한 호출은 어떤 것인까?

실인수의 값을 전달하는 것이 아닌 주소를 전달하는 것이다. 사실 내부적으로 보면 값호출이나 참조호출이나 같은 동작이 일어난다. 주소도 갓이므로 주소값이 형식인수에 복사가 된다. 그런데 주소값을 전달하기 위해서는 형식인수의 타입은 실인수타입의 포인터여야 한다. 즉 참조호출은 형식인수가 포인터기 때문에, 내부적으로는 실인수의 값을 알기위해서는 형식인수(포인터)에 의한 참조 방식으로 알아야 한다. 결국 이런 전달방식은 실인수에 직접 접근이 가능하다.


그럼 값호출과 참조호출의 차이점을 간단히 예를 들어보자


값에 의한 호출

위 코드의 동작후 결과 값은 3을 출력한다. 의도한바는 a의 값을 전달받아 3을 더 더해서 6이 되길 바라는 코드였다. 그런데 위에서 설명했듯이, 값에의한 호출은 값만을 형식인수에 복사하기 때문에, 실인수 a와는 아무런 상관없이 함수내부의 형식인수가 6의 값을 갖는다. 함수내부의 a라는 값은 지역변수기 때문에 함수가 리턴될 때 바로 소멸된다.


함수 내부에서 외부의 값을 다루기 위해선 포인터에의한 참조를 통해 외부값을 조작해야한다.


참조에 의한 호출

위 코드는 참조호출로 바꾼 코드다. 결과 값은 6을 출력한다.

단순히 주소값을 전달하여 내부적으로는 실인수에 접근하여 값을 조작했다. 함수내부에서 선언된 지역변수인 형식인수 a가 소멸되지만, 소멸과 관계없이 실인수 a의 값은 조작된 후다. 



그러면 이번엔 좀 더 깊게 생각해보자.


위 코드는 동적 메모리의 함수를 우해 래퍼함수인 n_call 함수를 만든 것이다. 포인터를 전달한후 동적할다을 하여 TEST라는 값을 넣었다. 그리고 실인수의 값을 출력하는 코드이다. 그런데 이상하게 제대로 동작하지 않는다.

분명 name도 포인터 변수고, name 가리키는 공간의 주소값을 넘겨주어서 그 공간에대한 메모리의 동적 할당을 하는 것인데 왜 안될까.. 이렇게 생각 될 수도 있겠다. 그러면 다음의 그림을 보자.


값에 의한 호출이 된 경우

좀 헷갈릴 수 있겠으나, 기본적인 동작은 같다. 실인수의 동적할당을 제대로 받기 위해선 실인수의 포인터를 전달해야한다. 위 그림은 형식인수가 동적할당은 받은 후 TEST를 입력받았다. 이 경우도 함수가 리턴된후 a의 값은소멸된다. 그리고 Name은 아무런 변화가 없다. 즉 값에의한 호출이다.

 단순히 Name의 값만 바꾸려 했다면 제대로 동작 했을 것이다. 이런일이 일어나는 이유는 함수내부에 있는 malloc이 포인터의 값을 변화시키는 함수기 때문이다. 때문에 포인터를 기준으로 생각해야한다.

실제로 원하는 동작을 만들기 위해선 형식인수로 &Name을 전달해야하며, 형식인수의 타입은 char **a 가되어야 한다. 그리고 함수 내부에서 형식인수는 '*'연산으로 실인수의 실제 값을 참조해야한다. 다음 그림을 참조하길바란다.


제대로 참조에 의한 호출이 된경우



2014/02/11 - [프로그래밍/C언어] - 이중포인터에 대한 이해 C언어


이경우 C언어의 문자열을 다루는 형식의 특성상 2중포인터를 사용하긴 했으나, 실제로, 다른 참조호출의 동작과 같은 것이다. 원리 또한 동일하다. 단지 위 경우는 기준점이 포인터 Name 이었기 때문이다.


+ Recent posts