mysql을 한글 지원으로 컴파일했다면 한글 이름을 줘도 되지만 이것은 사용하지 않는 편이 좋습니다.
그리고 이름엔 "." 문자를 사용할 수 없고 첫 글자는 영문으로 사용해야 합니다.
또, 최대 64자까지 지원되며, 별명은 최대 256자까지 지원됩니다.
이름의 구성은 영문,숫자, "_" , "$"로 이루어져야 합니다. Mysql을 유닉스나 리눅스
계열에서 사용한다면 그 운영체제의 환경을 따르기 때문에 대소문자를 구별해서 데이터베이스 이름, table
이름이 저장됩니다.
Win32계열의 경우는 대소문자를 구분하지 않지만 하나의 문에서 대소문자를 같이 사용할 수는 없습니다.
하지만 별명은 대소문자를 구분해 줍니다.
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개 이상을 얻을 수 있습니다.
---------------------------------------------------------------------------------------------------------------------
./mysql -h 호스트명 -u 유저 -p ("mysql>" 프롬프트가 나타나고 쿼리를 실행하면 됩니다.)
디폴트 인스톨을 했다면 "/usr/local/mysql/bin" 디렉토리에서 접속 명령을 실행합니다.
물론 -p 옵션은 유저에 해당하는 비밀 번호입니다.
위 명령을 실행하고 엔터를 치면 passw 입력 란이 뜨고 거기에 passw를 입력하면 됩니다.
그럼 이제 직접 database를 만들고 여러 가지 사용하는 방법을 알아보겠습니다. 먼저 만들기 전에 기본적으로 만들어진 database를 확인해 봅니다.
데이터베이스 보기
mysql>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 ;
"show" 명령으로 확인하면 work가 만들어진 것을 볼 수 있습니다.
work 데이터베이스의 사용을 위해 use 명령을 실행하고 다음 테이블을 확인합니다.
물론 새로 만든 데이터베이스이기 때문에 테이블은 비었다고 나옵니다.
(자! 그럼 table를 만들어 보겠습니다. 다른 방법도 있지만 여기선 직접하는 방법을 택했습니다.
다른 방법은 txt 파일을 만들어 파일을 실행하는 방법입니다. table명은 "works"로 하겠습니다.)
이제 테이블에 데이터를 입력할 차례입니다. 이것 또한 직접 하나씩 입력하는 방법과 파일로 한꺼번에 입력하는 방법이 있습니다.
테이블에 데이터 입력하기(직접)
mysql>insert into 테이블명 values(‘자료명’ , ‘ ..’ , ‘..’) ;
이것은 해당 테이블을 새롭게 생성했을 경우 처음 입력할 때 주로 사용 합니다.
자료의 입력 순서는 각 열에 해당하는 인자 순으로 나열하면 되고, 만약 해당 열에 자료를 넣지 않으려면
"null" 을 사용합니다.
테이블에 데이터 입력하기(파일)
mysql>load data local infile "파일명" into table 테이블명 ;
모든 자료 입력은 테이블 열의 이름순으로 하면 됩니다. 그리고 중요한 것은 열과 열 사이는 tab 키로 해야
하는 것입니다. 어떠한 에디터를 사용하건 확장자나 이름은 중요하지 않습니다. 또, 빈 공간이 없이 입력을
해야 합니다. 열의 항목을 비워 두려면 " /n "을 사용하면 됩니다.
위 파일을 보면 공백이 없는 것이 확인 될 것입니다.
이 파일은 "works_table.sql"로 만들었고 2) 번의 방법으로 해당 테이블에 입력했습니다.
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일 경우 (둘 다 참이어야 합니다.)
위 그림은 여자 중에 일을 4번 한 사람을 찾는 것입니다.
mysql>select * from works where (own = "청소" and hab = "잠자기");
where ( own = "청소" and hab = "잠자기" ) 이것은 맡은 일이 청소이면서 취미가 잠자기인
사람을 찾는 것입니다. 여기까지는 검색명을 "*"로 사용했기 때문에 열 전부를 보여 주었습니다.
특정한 열만 보려면 그 열의 이름을 적어주면 됩니다.
where or 사용 하기
mysql>select * from works where (own = "청소" or hab = "농구");
or일 경우는 (둘 중 적어도 하나는 참이어야 합니다)
그리고 위 방법 말고도 and와 or을 같이 사용하는 방법과 한 개 이상을 사용하는 방법 여러 가지가 있습니다.
where and, or 사용하기
mysql>select * from works where (sex = "m" and work >= "2000-01-20") or
(sex= "m" and hab= "잠자기");
그림을 보면 "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개 이상 반복합니다.
^ - 문자열의 처음을 나타냅니다.
$ - 문자열의 끝을 나타냅니다.
[,] - 괄호 안의 문자열 일치를 확인합니다.
{,} - 반복을 나타냅니다.
위 그림은 works 테이블에서 필드가 name인 항목 중 알파벳 b로 시작하는 사람을 찾아 줍니다.
order by 사용하기
mysql>select 열명 from 테이블명 order by 열명;
order by는 인자 순으로 열명에 해당하는 자료를 정렬해 주는 명령입니다. 자료를 순서대로 보기위한 좋은
방법입니다. 게시판을 만들 경우 이 명령을 꼭 한번은 사용해야 할 것입니다.
위 그림을 보면, work 순으로 차례로 정렬한 모습을 볼 수 있습니다.
제일 처음 일을 그만 둔 사람을 위 명령으로 쉽게 찾을 수 있습니다.
order by DESC 사용하기
mysql>select 열명 from 테이블명 order by 열명 desc ;
desc 옵션을 사용하면 해당 열명을 역순으로 정렬해 줍니다. 뒤 경매 게시판에서도 볼 수 있지만 이 옵션은
유용하게 쓰입니다. 게시판의 경우 사람들은 순서대로 보다 역순으로 봐야지만 제일 최근 자료를 볼수 있는
것입니다. 이 명령은 제일 최근까지 일했던 사람을 역순으로 제일 위에 오게 출력해 줍니다.
mysql의 경우 이 명령이 순차적인 것보다 조금 느리게 출력될 경우도 있습니다. 물론 자료가 많을 경우입니다.
하지만 한번 실행하고 난 뒤는 거의 속도 차이가 없다고 보면 됩니다.
여기서 주의 할 부분은 desc 인자 앞의 열명만 적용이 된다는 것입니다.
그 앞에 다른 열명을 적어 줘도 역순으로 정렬되지 않습니다.
위에서 볼 수 있듯이 desc 인자의 역할은 되지 않고 있습니다.
레코드 수 보기
mysql>select count(*) from work ;
총 레코드 수를 계산해 줍니다.
위 명령과 group by 명령을 같이 사용할 경우 더 강력한 기능을 합니다.
group by 사용하기
mysql>select 열명,count(*) from 테이블명 group by 열명 ;
해당 그룹의 레코드에 관해 숫자를 파악해서 출력해주는 역할을 합니다. (count(*)와 같이 사용할 경우)
출력의 결과를 보면 일의 종류에 따라 배치된 사람의 수를 파악할 수 있습니다.
위 그림을 보면 jun이 물주기 역할을 두개나 받았다는 걸 알 수 있습니다. 또, 총 맡은 일을 볼 수도 있습니다.
이렇게 여러 가지를 그룹으로 출력해 주는 명령은 유용하게 쓰일 수 있습니다.
만들어진 테이블을 지우거나 다시 수정하는 방법은 몇 가지가 있습니다. 게시판의 사용 시에 이 부분은 꼭 들어갑니다. 데이터의 삭제와 추가 등에 사용됩니다.
테이블 지우기
mysql>delete from 테이블명 ;
해당 테이블을 삭제합니다.
테이블에 필드(열) 추가하기
mysql>alter table 테이블명 add 열명 자료형태 ;
테이블에 다른 열을 추가시킵니다.
만약 게시판의 admin 툴을 만든다면 이 방법을 적용할 수 있습니다. 지금까지 말한 모든 설명들은 데이터베이스
admin 툴을 만들 때 사용하는 중요한 기본 질의들입니다.
위 그림에서 tel 필드가 새롭게 추가된 것을 볼 수 있습니다.
테이블의 특정 필드(열) 삭제하기
mysql>alter table 테이블명 drop 열명 ;
해당 테이블의 특정 필드(열)를 삭제합니다.
위 그림에서 tel 항목이 삭제된 것을 볼 수 있습니다.
테이블 특정 레코드 삭제하기
mysql>delete from 테이블명 where 열명 = ‘레코드명’(데이터명);
해당 테이블에서 특정한 레코드만 삭제합니다.
위 그림에서 name이 glee라는 레코드만 지워진 것을 볼 수 있습니다.
테이블 특정 레코드 수정하기
mysql>update 테이블명 set 열명= ‘레코드명’(데이터명) where 열명= ‘레코드명’;
이 방법은 해당 테이블의 특정 레코드의 내용만 수정할 때 사용합니다.
위 그림에서 name가 bian인 레코드 중 hab가 "?" 에서 컴고치기로 고쳐진 것을 볼 수 있습니다.
이제 mysql에 대한 접근하는 방법은 익숙하리라 생각합니다. 그러면 이제 각 데이터베이스 별 접근과 권한에 관한 설정들을 알아 보겠습니다. 뒤의 게시판 등의 소스에는 그냥 root 권한으로 설정했지만 여러분들이 이 부분을 수정하고 여러 가지 다른 방법으로 데이터베이스 접근을 하도록 만들 수 있습니다.
mysql 권한을 설정하기 전에
먼저 mysql 클라이언트에 접속을 합니다. 그리고 디폴트로 설치된 mysql의 데이터베이스 항목을 살펴 보겠습니다.
위 그림은 아직 데이터베이스를 추가로 만들지 않은 여러분들과는 조금 다릅니다.
디폴트는 mysql과 test 데이터베이스만 있습니다.
그럼 mysql의 전반적인 설정들이 들어 있는 mysql 데이터베이스를 선택하겠습니다.
위 그림에서 나열된 테이블 중 db와 user을 살펴 보겠습니다.
db 테이블 - 각 데이터베이스의 이름과 호스트, 사용자 등의 권한을 설정합니다.
user 테이블 - mysql을 사용할 수 있는 유저와 해당 유저의 권한을 설정합니다.
위 그림은 db 테이블의 속성들입니다. 이 테이블에 해당 유저들이 사용할 데이터베이스를 설정하고 권한을 줍니다.
데이터베이스 사용 user 등록하기
여기서는 test 데이터베이스의 사용자(cry)를 등록해 보겠습니다.
위 그림에서 볼 수 있듯이 db 테이블은 데이터베이스의 소유자를 등록하는 곳입니다.
(Test 데이터베이스의 소유자는 지금 아무도 설정되지 않았습니다.)
그리고 user 테이블은 데이터베이스를 사용하는 user을 등록하는 곳입니다. 즉, db 테이블에 소유자가 등록되어
있더라도 user 테이블에 그 소유자(사용자)가 존재하지 않으면 안됩니다.
처음 mysql의 설정에서 root를 설정했습니다. 이 root 계정은 뭐든지 다 할 수 있는 계정이므로 데이터베이스별로
관리를 할 수 있고 모든 권한이 없는 해당 데이터베이스만 관리 하는 계정이 필요로 한 것입니다.
db 테이블의 속성을 보면 여러 가지 데이터베이스에서 질의하는 속성 들을 볼 수 있을 것입니다.
그 중 사용하게 할 질의 들은 ‘y’ 그렇지 못하게 할 경우는 ‘n’ 등을 줌으로 해서 데이터베이스 관리자의 권한을
정할 수 있습니다. 해당 질의 권한을 설정하는 부분은 총 10개 항목이 있습니다.
앞에서 insert 문과 update 문을 보았기 때문에 테이블을 다루는 방법은 이제 습득했을 줄 압니다.
여기서는 기존에 존재하는 test 테이블을 수정하는 것이기 때문에 update문을 사용 했습니다.
만약 새로운 데이터베이스를 db 테이블에 추가할 경우는 당연히 insert 문을 이용하면 되겠습니다.
insert into 문을 이용한 새로운 데이터베이스 등록 역시 다음과 같은 식으로 해주면 됩니다.
insert into db values("해당 열" , "해당 열" …….);
Update 방법은 기존에 있던 행을 말 그대로 업데이트 한 것 입니다.
필자의 경우 update가 조금 헛갈리는 경우가 있어 여러분들도 혹 그런 분이 있을 거 같아 insert 대신
update문을 사용해 봤습니다.
그럼, 해당 항목들이 업데이트 되었는지를 select 문을 이용해 확인해 보겠습니다.
select Host,Db, User from db ;
Mysql 사용자 등록하기
user 테이블의(mysql 데이터베이스에서) test 데이터베이스를 사용할 cry 사용자를 등록해 보겠습니다.
우선 user 테이블의 속성들을 확인합니다.
user 테이블 역시 해당 질의에 관한 권한이 있습니다. 총 14개 항목이 존재합니다.
여기서는 insert into 문을 사용해서 추가를 하겠습니다.
위 그림에서 유저를 등록하기 전에 상기 할 부분은 각각의 질의에 관한 권한 설정을 명확히 정한 다음
등록하는 것입니다. cry라는 사용자가 test 데이터베이스 만을 관리하는 것이라면 질의 모두 "n"으로
해야 될 것입니다. 하지만 cry가 다른 데이터베이스도 관리하길 원한다면 질의 중 필요한 항목만 "y"로
선택하면 됩니다.
그럼 다음을 입력해서 user 테이블의 등록 상태를 확인해 보도록 하겠습니다.
select Host, User, Password from user ;
위 그림에서 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"이기
때문에 입력하지 않아도 됩니다.
위 그림에서 192.168.0.1의 Host와 cry 유저 그리고 암호가 등록된걸 확인할 수 있습니다.
이 설정들은 mysql을 재시동하고 다시 접속할 때부터 적용됩니다.
mysql 사용자 테스트
cry 계정으로 192.168.0.1 호스트로 접속해 보겠습니다.
물론 앞에서 설정한 "cry98" 암호로 접속해야 됩니다.
위 그림에서 cry 계정의 처음 권한 설정에 의해 test 데이터베이스만 사용 가능함을 볼 수 있습니다.
여기까지 대략적인 mysql의 사용자 권한 설정과 추가에 대해 살펴 보았습니다.
이러한 방법들을 이용해서 나중에 게시판에 적용하면 많은 도움이 될 것입니다.
참고로, 뒷 장의 게시판 부분과 나머지 소스들의 mysql 연동은 root 권한으로 설정되어 있습니다.
하지만 여러분들이 원하는 계정을 추가한 다음 입 맛에 맞게 얼마든지 수정할 수 있습니다.
데이터의 백업은 정말로 중요한 일 중에 하나입니다. 필자는 컴퓨터를 통째로 날린 적이 있기 때문에 더욱 백업에 열을 올립니다. 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를 이용하는 방법입니다.
위의 test 데이터베이스를 test_bak.sql 파일로 저장했습니다. 물론 저장된 파일은 /bin 디렉토리에 생성됩니다.
저장된 파일 복구하기
#mysql -h 호스트명 -u 유저명 -p 데이터베이스명 < 백업된 파일명
해당 백업 파일을 다시 원상 복구 시킵니다.
참고로 mysql의 데이터베이스를 삭제하는 방법은 다음과 같습니다.
drop database 데이터베이스 명;
간혹 테이블 지우는 명령은 아는데 데이터베이스 지우는 명령을 모르는 분들이 많더군요.
다음 장에서는 Mysql 함수를 설명하겠습니다.
퀵 정렬 알고리즘은 피벗 값을 선택하여 피벗 값보다 작은 값들은 왼쪽으로 보내고 큰 값들은 오른쪽으로 보낸 후에 이들 사이에 피벗을 위치시키는 원리를 이용합니다.이후 피벗보다 작은 값들을 재귀 호출로 정렬하고 피벗보다 큰 값들도 재귀 호출로 정렬하는 방식입니다.
그런데 퀵 정렬은 어떠한 요소를 피벗으로 선택하냐에 따라 성능에 차이가 납니다.만약 전체 요소의 중간 순위의 요소를 선택하면 재귀 호출에서 반씩 나누어 정렬을 하게 되어 좋은 성능을 발휘합니다.하지만 가장 작은 값이나 가장 큰 값을 피벗으로 선택하면 최악의 성능을 발휘합니다.
여기에서는 맨 앞과 맨 뒤,그리고 중간 위치의 요소를 비교하에 세 값 중에 중간 값을 피벗으로 선택할게요.
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 문으로 변경은 영구 테이블로, 허용되지 않습니다.
경우 read_only 시스템 변수가 사용 가능으로, 명시 적으로 트랜잭션을 시작 START TRANSACTION READ WRITE 요구 SUPER 권한을.
중대한
(예 : JDBC 등) MySQL 클라이언트 응용 프로그램을 작성에 사용되는 대부분의 API는 (때로는한다) 대신에 보내는 사용할 수 있습니다 트랜잭션을 시작하는 자신의 방법을 제공하는 START TRANSACTION 클라이언트에서 문을. 참조 25 장, 커넥터 및 API에 대한 자세한 내용은, 당신의 API에 대한, 또는 문서를.
명시 적으로 자동 커밋 모드를 해제하려면 다음 문을 사용합니다 :
SET의 자동 커밋 = 0;
설정에 따라 자동 커밋 모드를 해제 한 후 autocommit 제로 변수 (예에 대한 것과 같은 트랜잭션 안전 테이블의 변경 InnoDB 또는 NDB) 즉시 영구적으로하지 않습니다. 당신은 사용해야 COMMIT 디스크에 변경 사항을 저장하거나 ROLLBACK 변경 사항을 무시.
저장된 모든 프로그램 (저장 프로 시저 및 함수, 트리거, 이벤트), 파서 취급 이내 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 시스템 변수가 체인의 원인 또는 기본적으로 완료를 해제하도록 설정되어 있습니다.
최상의 결과를 얻으려면, 거래는 하나의 거래 안전 스토리지 엔진에 의해 관리 만 테이블을 사용하여 수행해야합니다. 그렇지 않으면, 다음과 같은 문제가 발생할 수있다 :
둘 이상의 트랜잭션 안전 저장 엔진 (예에서 테이블을 사용하는 경우 InnoDB) 및 트랜잭션 격리 수준되지 않습니다 SERIALIZABLE, 하나의 트랜잭션이 커밋 할 때 같은 테이블을 사용하는 다른 지속적인 거래 만 일부 변경을 볼 가능성이있다 첫 번째 트랜잭션에 의해 만들어진. 즉, 트랜잭션의 원 자성이 혼합 된 엔진과 발생할 수 있습니다 일관성을 보증 할 수 없습니다. (혼합 엔진 트랜잭션이 빈번하지 않으면 사용할 수 SET TRANSACTION ISOLATION LEVEL 로 분리 레벨을 설정할 SERIALIZABLE 필요에 따라 트랜잭션 단위에서).
당신이 트랜잭션 내에서 트랜잭션 안전하지 않은 테이블을 사용하는 경우, 해당 테이블의 변경에 관계없이 자동 커밋 모드의 상태를 한 번에 저장됩니다.
당신이 실행하는 경우 ROLLBACK 트랜잭션 내에서 비 트랜잭션 테이블을 업데이트 한 후 문을 ER_WARNING_NOT_COMPLETE_ROLLBACK 경고가 발생합니다. 트랜잭션 안전 테이블에 대한 변경 사항은 nontransaction 안전 테이블에 대한 변경 사항을 롤백하지만되지 않습니다.
각 트랜잭션에, 한 덩어리에 바이너리 로그에 저장됩니다 COMMIT. 롤백 트랜잭션은 기록되지 않습니다. ( 예외 :. 비 트랜잭션 테이블에 대한 수정은 롤백 할 수 없습니다 롤백 트랜잭션이 비 트랜잭션 테이블에 대한 수정이 포함 된 경우, 전체 트랜잭션이 함께 기록됩니다 ROLLBACK비 트랜잭션 테이블에 대한 수정이 복제되도록 끝에 문.) 참조 섹션 6.4.4, "바이너리 로그" .
위로 롤링 (오류가 발생하면, 예를 들어)을 명시 적으로 요청하는 데 사용하지 않고 암시 적으로 발생할 수있는 느린 동작 할 수있다. 이 때문에, SHOW PROCESSLIST 표시 Rolling back 에서 State 세션의 열뿐만 아니라, 명시 적으로 수행 롤백 용 ROLLBACK 문장뿐만 아니라 내재 롤백 대한.
※ 경고 높이뜬새의 모르는 사람이 없는 팁시리즈 입니다. 이 팁시리즈는 지면낭비라는 항의시에 즉각 중단됩니다.
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 컬럼을 추가해 봐야 하겠습니다.
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 칸을 차지하게 한다.
이 때, 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 를 의미 할 수 (가리 킬 수) 있게 되었지요. 흔히 많은 책들은 포인터 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;
사실, 이런 예제까지 굳이 보여주어야 하나 하는 생각이 들었지만 그래도 혹시나 하는 마음에 했습니다. 앞에서도 말했듯이 포인터는 '변수' 입니다. 즉, 포인터에 들어간 주소값이 바뀔 수 있다는 것이지요. 위와 같이 처음에 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 가되어야 한다. 그리고 함수 내부에서 형식인수는 '*'연산으로 실인수의 실제 값을 참조해야한다. 다음 그림을 참조하길바란다.