C언어 포인터 기본에 대해서 배워보자

 

C언어를 처음 접하신 분들은 포인터에 대해서 어려워 하시는 분들이 있는데 어려워 할 필요 없습니다.

 

C언어 포인터 에 대해서 조금만 이해하신다면 예제보고 테스트 소스를 코딩해보고 그러다 보면 언젠가 이게 뭐가 어렵지? 라는 생각을 하게 될 것입니다.

 

포인터에 대해서 조금 더 깊게 파고 들어가면 포인터연산, 함수포인터, 이중포인터 등등 여러가지가 나오는데 이 부분은 일단 포인터에 대해서 공부를 한다음 진도를 나가 봅시다.

 

C언어 를 테스트 할려면 컴파일러가 필요한데 아래 Dev-C++ 무료 컴파일러 입니다. 사용하시는 컴파일러가 있다면 굳이 다운로드 안하셔도 됩니다.

 

C언어 무료 컴파일러인 Dev-c++ 다운로드 방법 입니다.

http://seonissi.tistory.com/admin/entry/post/?id=25

 

 

Dev-c++ 간략한 사용법

http://seonissi.tistory.com/admin/entry/post/?id=26

 

 

C언어 포인터는 단순히 주소라고 생각하면 됩니다.

 

서울시 구로구 구로동 XXXX아파트 101동 1101호

우리가 사는 집엔 모두 주소가 붙듯이 변수를 선언하면 주소가 붙게 됩니다.


 

 

 

 

char 형태의 p라는 변수를 선언 했습니다.

 

 


선언된 변수를 printf문으로 출력해볼 경우 주소가 나타납니다..

 

 

 

 

 

 

 

이렇게 사용하면 변수의 주소를 공유하게 되 포인터로도 변수의 값을 사용 및 수정이 가능합니다. 예를 들어서 우리가 사는 주소에 A라는 사람이 들어가면 그 주소안에는 A라는 사람이 있는거고 B라는 사람이 들어가게 되면 그 주소안에는 B라는 사람이 있는것과 동일한 것입니다..

 

 

변수와 포인터의 주소값을 출력해볼 경우 동일하다라는걸 확인이 가능합니다.

 

 

  

 

포인터 p에 변수를 할당 하지 않을 경우 경우 각각 주소가 다릅니다.

 

위의 결과처럼 주소값이 1만 증가할수 있지만 경우에 따라서 주소값이 다른곳을 가르킬수 있습니다. 포인터가 가르키는 주소값은 메모리 어느 부분을 가르키고 있는데 주소를 할당하지 않은채 사용하다가는 언젠가는 프로

그램이 죽는 현상이 발생하니 주의해야 합니다.

 

 

C언어 포인터 예제 1

 

int main(int argc, char *argv[])

{
    char ch;
    char *p;

 

ch = 'a';
 
printf("ch = %c\n", ch);


    p = &ch;
 
    printf("*p = %c\n", *p);
    printf("ch = %c\n", ch);

}

 

위 예제를 보게 되면 char 형태의 ch를 선언하고 포인터 p를 선언했습니다.

char 형태의 변수값에 'a'라는 값을 넣고 포인터 p에 변수 ch 주소를 할당 했습니다.

그리고 포인터p 가 어떻게 변했느가를 살펴보는 예제 입니다.

 

 

 

 

 결과값을 보면 ch와 포인터p와 값이 동일하다는게 확인이 됩니다.

 

 

 


C언어 포인터 예제 2

int main(int argc, char *argv[])
{
 char ch1;
 char ch2;
 char *p;

 

 ch1 = 'a';
 ch2 = 'b';
 
 printf("ch1 = %c\n", ch1);
 printf("ch2 = %c\n", ch2);
 
 p = &ch1;
 printf("*p1 = %c\n", *p);
 
 p = &ch2;
 printf("*p2 = %c\n", *p);
}

 

 

 

결과값을 보면 할당된 포인터 값에 따라 포인터p의 값이 변환되는 것을 알수 있습니다.

이처럼 포인터는 주소값을 가지고 움직인다는것을 확인했습니다.

 

 

 

C언어 포인터 예제 3

void test1 (char *p)
{
 printf("test1 *p = %c\n", *p);
 *p = '1';
}

 

void test2 (char *p)

{

 printf("test2 *p = %c\n", *p);
 *p = 'a';
}

 

int main(int argc, char *argv[])
{
 char ch1 = 'z';

char *p;

 

 p = &ch1;

 

printf("*p = %c\n", *p);

 test1(p);
 printf("ch1 = %c\n", ch1);

 test2(p);
 printf("ch1 = %c\n", ch1);
}

 

위 예제를 보면 함수에 포인터를 대입해서 함수를 호출 했을때 인자값으로 전송 받았던 포인터 값에 대해 값이 변함이 없는지를 확인할 수 있는 예제 입니다.


거기에 인자값으로 받은 포인터에 다른 값을 대입했을 시 변수로 선언한 ch값이 변화가 있는지 없는지도 확인이 가능합니다.


함수안에서 값을 변경해도 main에서 선언된 ch1이라는 변수에도 영향을 미칩니다.


위의 예제와 같이 함수를 호출할때 함수에 포인터 변수나 주소를 전달하는 것을 call by reference라고 합니다..


ch값을 받고 할당 받은 포인터를 함수로 전달받고 그 값을 printf값으로 출력 하는 예제입니다.

 

 

 


결과값에서 봤듯이 인자값으로 받은 포인터가 ch값과 동일하다는것을 확인할 수 있습니다.


함수안에서 포인터 값을 변경해도 변수로 선언된 ch값 까지 변환되는 부분까지 확인이 가능합니다.


함수의 인자값으로 포인터를 사용해서 그 값을 공유할수 있다는 것입니다.
위와 같이 많이 사용하게 될텐데 포인터를 잘 사용해야 프로그램을 개발할때 모듈화를 하기가 쉽습니다.


이 부분은 더 공부를 해야 될 것입니다.

 


C언어 포인터 예제 4

int main(int argc, char *argv[])

{
 char ch1[10];
 char ch2[20];
 char *p;

 

 ch1[0] = '1';
 ch1[1] = '2';

 ch2[0] = 'a';
 ch2[1] = 'b';
 
 p = ch1;
 printf("ch1 p[0] = %c\n", p[0]);
 printf("ch1 p[1] = %c\n", p[1]);

 

 p = ch2;
 printf("ch2 p[0] = %c"\n, p[0]);
 printf("ch2 p[1] = %c\n", p[1]);
}

 

위 예제는 포인터와 배열에 대한 부분인데 ch1은 10개의 박스를 다 소유하고 있다고 생각하면 됩니다.

 

 

10개의 박스의 안에는 박스 사이즈에 맞게 물건을 넣을수 있는데 사이즈는 "char"형 이라고 보면 되고 물건은 "char"로 선언된 변수라고 보시면 됩니다.

 

포인터로 선언된 p로 ch1을 가르키게 되면 ch1의 데이타를 쓰기 및 읽어올수가 있습니다.


배열을 선언하고 그 배열을 포인터로 할당한 다음 간단하게 포인터로 제어를 하는 식으로 많이 사용합니다.


위의 예제를 보면 포인터 p에 ch1을 할당해서 ch1에 설정된 a, b값을 포인터 p로도 확인을 할수가 있습니다.


다시 포인터 p에 ch2를 할당하면 ch2와 동일하게 사용이 가능합니다.

 

 



결과값을 보시면 ch1과 ch2의 값을 포인터 p로 할당한 다음 printf문으로 출력했을 경우 동일하게 출력 된다는 것을 확인할 수 있습니다.

 

 

C언어 포인터 예제5

 

int main(int argc, char *argv[])

{
 char ch1 = '1';
 char *p1;
 char *p2;

 p1 = &ch1;
 p2 = &ch1;
 
 printf("*p1 = %c\n", *p1);
 printf("*p2 = %c\n", *p2);
 
*p1 = 'a';
 
 printf("*p2 = %c\n", *p2);
}

 

위의 예제를 보면 ch1의 값을 포인터p1과 포인터p2를 동시에 할당을 했습니다. 이럴경우 p1과 p2가 동일하다고 생각하면 되는데 같은 ch1의 변수를 할당받았고 p1을 수정했을 경우 p2의 값도 변한다는 것을 확인할 수 있습니다.


이렇게 포인터를 여러개 선언해서 같이 수정도 가능하다. ch1이 아니라 "p1 = p2"이렇게 선언해도 상관없습니다.
p1도 포인터고 p2도 포인터기 때문에 언제든 주소를 다시 할당할수 있습니다.

 

 

 

 

 

결과값을 보면 p1과 p2는 동일하게 변수 ch1의 값을 동일하게 printf문으로 출력을 하고 있음이 확인 가능합니다.

 

위의 예제처럼 포인터의 경우 다양한 사용이 가능합니다. 구조체를 선언해서 포인터로 할당해서 사용이 가능하고 여러가지 사용이 가능합니다. 위에는 간단하게 C언어 포인터 기본에 대해서만 예제를 설명했습니다.

 

C언어에서 포인터를 사용하지 않아도 프로그램 코딩은 가능하나 포인터를 사용하지 않고 프로그램 코딩을 하는 개발자는 없을 것입니다.

 

실제로 프로그램을 개발하게 되면 이렇게 간단하게 포인터를 사용하지 않고 포인터 연산도 들어가고 포인터 구조체도 쓰고 배열도 들어가고 복잡하게 진행이 됩니다.

 

포인터를 잘못사용할 시 메모리가 뒤죽박죽이 되버릴 수 있습니다. 그리고 포인터를 잘못 정의해도 컴파일러가 체크를 못하는 경우가 종종 발생하기 때문에 개발 완료 후 문제가 발생할 가능성이 있습니다. 그래서 좀더 세세한 코딩이 필요합니다.

도요타 급발진 문제처럼 버그가 발생할 수 있기 때문에 주의해서 개발을 해야 합니다.

MySQL에서 사용자의 로그인 보안 수준을 높이기 위해서,
MySQL 4.0.x 이하 버전과 MySQL 4.1.x 이상 버전의 PASSWORD() 함수의 구현 알고리즘이 달라졌다.
사실 아주 오래전 이야기이지만, 아직도 MySQL 4.0.x를 사용하는 사이트가 많고,
최근 들어서 MySQL 5.1이나 5.5 버전으로 업그레이드를 준비하면서 이런 내용이
업그레이드에 걸림돌이 되는 경우가 많은 것으로 보인다.



MySQL 4.0.x 이하 버전
  - PASSWORD()
  - OLD_PASSWORD() 함수는 없음


  mysql> SELECT PASSWORD('mypass');
  +--------------------+
  | PASSWORD('mypass') |
  +--------------------+
  | 6f8c114b58f2ce9e   |
  +--------------------+


MySQL 4.1.x 이상 버전
  - PASSWORD()         ==> 버전업된 암호화 함수
  - OLD_PASSWORD()     ==> 기존 암호화 (MySQL 4.0.x의 PASSWORD()와 동일)


  mysql>select PASSWORD('mypass');
  +-------------------------------------------+
  | password('mypass')                        |
  +-------------------------------------------+
  | *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
  +-------------------------------------------+
  
  mysql>select OLD_PASSWORD('mypass');
  +------------------------+
  | old_password('mypass') |
  +------------------------+
  | 6f8c114b58f2ce9e       |
  +------------------------+


위 결과를 보면, 새로운 암호화 알고리즘에 의해서 암호화된 내용은 암호 제일 앞에 "*" 마크가 붙게 되므로, 새로운 암호인지 예전 암호인지 쉽게 구분할 수 있다.


그런데, old_passwords=1로 설정하게 되면, MySQL 4.1.x 이상 버전에서도 아래와 같이 MySQL 4.0.x와 같은 결과를 보여주게 된다.
MySQL 4.1.x 이상 버전에서도, 이전 버전과 동일하게 짧은 암호화 문장으로 변환됨
  root@localhost:(none)>select PASSWORD('mypass');
  +--------------------+
  | PASSWORD('mypass') |
  +--------------------+
  | 6f8c114b58f2ce9e   |
  +--------------------+
  
  root@localhost:(none)>select OLD_PASSWORD('mypass');
  +------------------------+
  | OLD_PASSWORD('mypass') |
  +------------------------+
  | 6f8c114b58f2ce9e       |
  +------------------------+


MySQL 4.1.x 이상 버전에서 old_passwords=0 (기본 설정값)인 경우에는, mysql.user 테이블의 비밀번호가 
예전 버전에서 생성된 짧은 비밀번호라면 로그인할 수 없게 된다.


하지만, MySQL 4.1.x 이상 버전에서도 mysql.user 테이블에 사용자의 비밀번호가 예전 버전인 경우 로그인할 수 있도록 
해주기 위해서 old_passwords 라는 옵션을 1로 설정할 수 있도록 해둔 것이다.


근본적인 원인은 MySQL이 업그레이드되면서 기존 비밀번호의 암호화 수준을 보완하여 더 보안 수준을 높이면서 
이런 문제가 야기되었다는 것이며, 가능하다면 업그레이드된 긴 암호를 사용하는 것이 좋아 보인다.


더 중요한 것은
MySQL의 PASSWORD() 함수는 MySQL의 사용자 자체의 계정 및 비밀번호를 관리하기 위한 함수이지
일반 서비스용 계정및 암호를 관리하는 용도로는 적합하지 않다는 것이다.


예를 들어서 MySQL 4.0.x 버전의 회원정보가 예전 방식의 PASSWORD() 함수로 암호화되어서 저장되어져 있다면,
MySQL 4.1.x 이상의 PASSWORD() 함수로는 로그인이 되지 않게 될 것이다. (이 경우에는 OLD_PASSWORD()를 사용해야만 로그인을 할수 있게 된다.)
하지만, 이 경우 예전 버전의 PASSWORD()함수의 암호화 내용을 새로운 버전의 PASSWORD() 함수로 대체할 수 없다.
(한번 암호화된 문장은 다시 풀어낼 수 없는 형태 - 비대칭형 암호화 - 이기 때문)
서비스용 계정이 MySQL의 사용자 계정 암호화 방식에 의존하게 되면 이런 문제가 야기될 수 있으므로 
필요시에는 MD5와 같은 알고리즘을 사용할 것을 추천한다.


또한, 이렇게 두개 버전의 암호화 방식이 존재하는 상태에서
서비스용 MySQL의 버전 업그레이드와 연관되어서 명확히 해결하지 않고 업그레이드를 진행하게 되면,
일부 사용자의 암호는 예전 버전이고, 또 일부 사용자는 새로운 암호화 버전을 사용하도록 되어버리면
걷잡을 수 없는 혼란에 빠져들고, 이를 처리하기 위해서 또 한번 쿼리와 IF ~ ELSE가 필요해질 수도 있다는 것이다.

'프로그램 > MySql' 카테고리의 다른 글

[MySQL]사용자 계정 생성 및 삭제  (0) 2016.11.29
MYSQL 사용자 생성  (0) 2016.11.29
MySQL 계열의 FLUSH PRIVILEGES 명령어  (0) 2016.11.28
MySQL 내장 함수 (MySQL전용 함수)  (0) 2016.11.28
MySQL 기본 명령어 정리  (0) 2016.11.28

+ Recent posts