프로그램을 만들다보면 숫자를 랜덤으로 만들어내야 할 때가 있습니다. 이번 포스팅은 C언어와 C++에서 랜덤으로 숫자를 추출하는 방법과 여러 예제를 살펴보겠습니다.


1. rand 함수의 기본 사용 방법


rand 함수를 사용하면 랜덤으로 숫자를 출력할 수 있습니다.


함수 원형은 아래와 같습니다. 

int rand(void)


#include<stdlib.h>로 stdlib.h 헤더파일에 해당 선언이 적혀있습니다. 


해당 함수는 0 부터 32767까지의 숫자 중 하나를 랜덤으로 뽑아냅니다.


여기에 % 연산자를 사용하여 원하는 숫자의 범위를 정합니다.


예를들어 rand() % 10 을 하면 0부터 9까지의 숫자중 하나가 나오게 됩니다.


만약 5부터 10까지의 숫자 중 를 랜덤으로 추출하고 싶다면 


rand() % 6 + 5 를 하면 됩니다. rand() % 6 을 하면 0부터 5까지의 숫자 중 하나이고 거기에 +5를 하면 5부터 10까지의 숫자중 하나가 됩니다.


이렇게 하면 자신이 원하는 숫자를 랜덤으로 추출할 수 있습니다.


아래 사용예제 입니다. 0부터 99까지의 숫자중 랜덤으로 변수 num에 저장하여 출력합니다.

총 3번을 반복합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
 
void main() 
{        
    int num;
    num = rand() % 100;
    printf("rand : %d\n", num);
    num = rand() % 100;
    printf("rand : %d\n", num);
    num = rand() % 100;
    printf("rand : %d\n", num);
}
cs


- 출력결과 -

rand : 41

rand : 67

rand : 34




2. srand 함수를 사용하여 프로그램을 실행할 때마다 다른 난수 출력하기


 하지만 이상한 것은 프로그램을 실행 할 때마다 매번 같은 숫자가 나오게 됩니다.

이것은 프로그램을 실행할 때마다 항상 난수표에 같은 지점에서부터 숫자를 추출하기 때문입니다.

난수표라는 것은 글 가장 마지막에 설명을 써놓았습니다.


 그래서 프로그램을 실행할 때마다 숫자가 다르게 나오게 하고 싶다면 srand 함수를 이용해야 합니다.

아래 함수의 원형 입니다.


void srand( unsigned int seed );


 seed값을 받아 난수표에 시작 지점을 바꾸는 것 입니다. 보통은 seed로 현재의 시간을 unsigned int 로 변환하여 사용하게 됩니다. 그러면 프로그램을 시작하는 시간이 계속 달라지기 때문에 프로그램을 실행할 때마다 다른 수가 추출되게 됩니다.


srand 함수를 사용하기 위해서는 #include<time.h>를 추가하여야 합니다.


아래 사용예제 입니다. 위의 예제와 같지만 프로그램이 시작할때 scanf() 를 사용합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
 
void main() 
{        
    int num;
    srand(time(0));
    num = rand() % 100;
    printf("rand : %d\n", num);
    num = rand() % 100;
    printf("rand : %d\n", num);
    num = rand() % 100;
    printf("rand : %d\n", num);
}
cs


출력결과가 매번 다릅니다.




3. 알파벳 랜덤으로 출력


아래 예제는 응용한 것으로 알파벳 소문자 a부터 z중 하나를 출력하는 예제입니다.

알파벳 개수가 26개기 때문에 rand() % 26 을하고 +'a' 를 합니다. 아스키2 코드를 이용 하는 것입니다.

그러면 a부터 z중 하나의 숫자가 출력됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
 
void main() 
{        
    int i;
    char ch;
    srand(time(0));
    for (i = 0; i < 10; i++) {
        ch = rand() % 26 + 'a';
        printf("rand : %c\n", ch);
    }
}
cs


- 출력결과 -

rand : e 

rand : s 

rand : b 

rand : f 

rand : i 

rand : p 

rand : n 

rand : b 

rand : j 

rand : d 






4. 로또 번호 추첨


1부터 45까지의 숫자를 중복없이 6개 출력하는 로또 번호추첨 예제이다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
 
#define MAX 45 //1부터 45 숫자
#define NUMBER //6개 숫자출력
 
void main() 
{        
    int i, tmp;
    int save[NUMBER];
    int count = 0;
    int isSame = 0;
    srand(time(0));
    
    //숫자 추출
    while (count < NUMBER) {
        isSame = 0;
        tmp = rand() % MAX + 1;//1부터 45 출력
        for (int i = 0; i < count; i++) { //중복검사
            if (tmp == save[i]) { //중복이 있을때
                isSame = 1;
                break;
            }
        }
        if (isSame == 0) { //중복없음
            save[count] = tmp;
            count++;
        }
    }
 
    //결과 출력
    printf("추첨 숫자 : ");
    for(i =0; i < NUMBER; i++)    
        printf("%d ", save[i]);
    printf("\n");    
}
cs


- 출력결과 -

추첨 숫자 : 35 15 29 19 27 38




5. 프로그래밍 언어에서 난수(랜덤)란?


 추가로 사실 대부분에 프로그램에서 사용하는 랜덤 함수들은 정밀하게는 완벽한 랜덤이 아닙니다. 난수표 라고해서 특성 숫자들이 적힌 테이블이 있는데 이 테이블들을 순서대로 출력하는 것 입니다. 

 예를들어 1 5 4 2 0 3 7 6 란 숫자가 있으면 1을 선택하고 그 다음에는 5를 선택하고 이런식으로 차례대로 숫자를 선택합니다. 이와 같은 방식으로 C와 C++에서는 0부터 32767 까지의 숫자들이 난수표에 적혀 있습니다. 그래서 엄밀하게는 완벽한 랜덤이라고 할 수 는 없습니다.

 따라서 보안이 중요한 곳이나 이런데서는 랜덤함수를 사용할때 주의가 필요합니다. 랜덤함수를 사용하여 추출한 결과를 보면 다음 수를 예측하거나 하는 방법으로 해킹등을 할 수도 있기 때문입니다.

Posted by 꿈만은공돌
,

메모리를 동적으로 할당 받는 이유는 일반 변수는 컴파일 단계에서 변수의 크기가 정해지기 때문에 프로그램이 실행 되면서 얼마만큼의 배열크기를 사용하게 될지 정확히 알 수 없기 때문에 넉넉하게 할당 받는다. 그래서 프로그램이 실행도중에 사용자가 원하는 만큼 메모리를 할당 받을 수 있다면 메모리 낭비가 줄어들고 프로그램도 안정적으로 동작이 된다. 그래서 나온 개념이 메모리 동적 할당이다.

C언어에는 Heap 영역에 사용자가 원하는 크기만큼 메모리를 할당받아 사용할 수 있다.


그것을 지원하는 3가지 함수가 있는데 malloc, calloc, realloc 이다. 이 포스팅에서는 이 3가지 함수에 차이점에 대해서 배워보도록 하자. 기본적으로 malloc 을 많이 사용하지만 다른 함수들도 알아 두면 유용할때가 많다.


그리고 참고로 메모리를 해제하는 함수는 free이다. 메모리를 할당하고 해제하지 않으면 나중에 사용할 heap 영역에 크기가 부족해 프로그램이 정상동작하지 않을 수 있다.


우선 malloc과 free에 기본적인 사용방법은 아래 링크를 참고하자. 정확히 이해하지 않으면 이번 포스팅이 어려울 수 있다.


C/C++ malloc, free를 사용한 메모리 할당 및 해제 : http://hijuworld.tistory.com/59


그리고 메모리 할당과 해제를 하는 원리는 아래 링크를 참고하자.


C, C++ 에서 동적 메모리의 할당과 해제 원리 : http://hijuworld.tistory.com/28





malloc과 calloc의 차이에 대해서 알아보자.


1. 할당받을 메모리 사이즈를 받는 파라미터의 갯수가 다르다.

2. calloc은 메모리 할당을 받고 0으로 초기화 한다.



우선 함수의 선언을 보면 아래와 같다.


void *malloc(size_t size);

void *calloc(size_t num, size_t size);


malloc은 메모리를 할당받을 사이즈 하나만을 파라미터로 받는 반면

calloc은 메모리를 할당받을 사이즈에 그 메모리를 몇개를 할당 받을지 갯수도 파라미터로 받는다.

둘다 void 포인터를 반환하는데 그 이유는 할당한 메모리를 어떤 크기로 잘라서 사용할지는 그때그때 다르기 때문에 사용자가 강제형변환을 통해 사용하면 되기 때문이다. 


아래 간단한 예제를 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
 
int main() {        
    int *pi1; 
    int *pi2;
 
    pi1 = (int *)malloc(sizeof(int* 3); //할당
    pi1[0= 1;
    pi1[1= 5;
    pi1[2= 8;
    
    pi2 = (int *)calloc(3sizeof(int)); //할당
    pi2[0= 1;
    pi2[1= 5;
    pi2[2= 8;    
 
    free(pi1); //메모리 해제
    free(pi2); //메모리 해제
}
cs


pi1 은 malloc으로 할당을 받고 pi2 는 calloc으로 할당을 받았다. 

파라미터의 갯수 말고도 더 큰 차이가 있다.

바로 calloc은 메모리를 할당과 동시에 0으로 초기화 하는 것이다.


아래 예제를 보자. 모두 할당만 하고 값을 출력시켜 보았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
 
int main() {        
    int *pi1; 
    int *pi2;
 
    pi1 = (int *)malloc(sizeof(int* 3); //할당
    printf("malloc : %d %d %d\n", pi1[0], pi1[1], pi1[2]);
    
    pi2 = (int *)calloc(3sizeof(int)); //할당
    printf("calloc : %d %d %d\n", pi2[0], pi2[1], pi2[2]);
 
    free(pi1); //메모리 해제
    free(pi2); //메모리 해제
}
cs



출력결과

malloc : -842150451 -842150451 -842150451

calloc : 0 0 0                                          


할당하면서 0으로 초기화가 되기때문에 상황에 따라 편리할 수 있다.


realloc에대해서 알아보자.

realloc은 malloc이나 calloc으로 할당받은 메모리의 사이즈를 변경할 때 사용한다.


함수선언은 아래와 같다.

void *realloc(void *block, size_t size);


파라미터로 기존에 할당받았던 메모리를 전달하고 다음 파라미터로 새로 할당 받을 size를 전달한다.


아래 예제를 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
 
int main() 
{        
    int i;
    int *pi = (int *)malloc(sizeof(int* 3); //4x3 메모리 할당
    for (i = 0; i < 3; i++)
        pi[i] = i * 4;
    for (i = 0; i < 3; i++)
        printf("pi[%d] : memory %d %d\n", i, &pi[i], pi[i]);
    printf("-----------------------------\n");
 
    pi = (int *)realloc(pi,sizeof(int* 5); //4x5 메모리 재할당
    for (i = 3; i < 5; i++)
        pi[i] = i * 4;
    for (i = 0; i < 5; i++)
        printf("pi[%d] : memory %d %d\n", i, &pi[i], pi[i]);
    printf("-----------------------------\n");
 
    free(pi); //메모리 해제
}
cs


출력결과

pi[0] : memory 9457232 0  

pi[1] : memory 9457236 4  

pi[2] : memory 9457240 8  

-----------------------------  

pi[0] : memory 9457232 0  

pi[1] : memory 9457236 4  

pi[2] : memory 9457240 8  

pi[3] : memory 9457244 12 

pi[4] : memory 9457248 16 

-----------------------------  


처음 4X3 메모리를 할당받았고 그다음에는 4X5 메모리를 재할당 받았다.

재할당 받은 뒤에 기존에 할당받았던 값 이외에 값인 4번째, 5번째에만 값을 넣어준다.

재할당 받은 뒤에도 기존에 값은 정상적으로 존재하는 것을 확인할 수 있다.

단순히 메모리 사이즈만 변경하는 것이 아닌 내용물도 복사를 한다.

 

그리고 메모리의 주소값이 같은것을 볼 수 있는데 이것은 보장하지는 않는다.

완전 새로운 메모리에 값을 새로 할당받아 값을 복사할 수도 있다.


아래 예제를 보자. 메모리를 기존보다 훨씬 크게 할당한 것을 볼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
 
int main() 
{        
    int i;
    int *pi = (int *)malloc(sizeof(int* 30000); // 메모리 할당
    for (i = 0; i < 3; i++)
        pi[i] = i * 4;
    for (i = 0; i < 3; i++)
        printf("pi[%d] : memory %d %d\n",i,&pi[i],pi[i]);
    printf("-----------------------------\n");
 
    pi = (int *)realloc(pi,sizeof(int* 5000000); // 메모리 재할당
    for (i = 3; i < 5; i++)
        pi[i] = i * 4;
    for (i = 0; i < 5; i++)
        printf("pi[%d] : memory %d %d\n", i, &pi[i], pi[i]);
    printf("-----------------------------\n");
 
    free(pi); //메모리 해제
}
cs

출력결과

pi[0] : memory 17674192 0 

pi[1] : memory 17674196 4 

pi[2] : memory 17674200 8 

-----------------------------  

pi[0] : memory 21880896 0 

pi[1] : memory 21880900 4 

pi[2] : memory 21880904 8 

pi[3] : memory 21880908 12

pi[4] : memory 21880912 16

----------------------------- 


메모리의 주소값이 달라진 것을 볼 수 있다. 하지만 0, 4, 8은 잘 복사된 것을 볼 수 있다.




Posted by 꿈만은공돌
,

동적 메모리 할당은 사용자가 프로그램이 실행도중 원하는 메모리의 크기를 할당하여 사용하는 것이다.

프로그래머가 코드를 작성할 당시에 정확히 얼마에 메모리가 필요한지 알 수 없기때문에 프로그램 실행도중 필요한 만큼만 할당받기 때문에 메모리 낭비를 줄일수 있다.

포인터가 사용되기 때문에 포인터에 대한 이해가 낮으면 조금 어려울 수 있는 내용이다. 그래도 계속 보다보면 적응이 될 것이다.


C언어에서는 힙영역에 사용자가 메모리를 동적 할당하는 3가지 함수인 malloc, calloc, realloc 이 있고

메모리를 해제하는 함수인 free가 있다.


메모리 할당 및 해제에 관한 원리는 아래 링크를 참고하자.

C, C++ 에서 동적 메모리의 할당(malloc, new)과 해제(free,delete) 원리 : http://hijuworld.tistory.com/28


동적 할당하는 3가지 함수인 malloc, calloc, realloc 의 차이에 대해서는 아래 링크를 참고하자.

C/C++ 동적 메모리할당 malloc, calloc, realloc 함수 비교 및 예제 : http://hijuworld.tistory.com/60





가장 기본인 동적 할당 함수인 malloc에 대해서 알아보자.


#include <stdlib.h> 

void* malloc(size_t size);


선언은 위와 같은 형식이다.

파라미터로 할당할 메모리의 크기를 넘기고 반환형이 void 형 포인터이다.

void 포인터인 이유는 할당받은 메모리를 몇바이트씩 잘라서 사용할지는 그때그때 다르기 때문에 사용자가 형변환을 통해서 사용하면 되기 때문이다. 


간단하게 예제를 보자. 1바이트 메모리를 할당해서 char형 포인터에서 할당한 주소값을 저장한다.

1바이트할당 받은 것을 (char*) 를 사용해서 강제형변환을 한 것을 볼 수 있다. 


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
 
int main() {        
    char *= (char*)malloc(1); //1바이트 메모리 할당
    *= 'h';
    printf("%d, %c\n",c, *c);//c의 주소값, c의 값 'h'
    free(c); //메모리 해제
}
cs

출력 결과

17674200, h


 할당받은 것을 그림으로 그려보면 아래와 같다. 우선 malloc을 이용해서 힙영역에 0x1000번지에 1바이트에 메모리를 할당 한다. 그리고 스택영역에 선언한 지역변수인 포인터 C가 malloc으로 할당한 주소를 가지고 있는다. 

그리고 해당 포인터를 가지고 *c = 'h'로 malloc으로 할당한 메모리에 문자열을 저장한다.



 파라미터로 사이즈를 넘기기 때문에 위의 예제처럼 직접 숫자를 입력해도 가능하진 하지만 아래 예제와 같이 sizeof 를 써서 할당할 변수의 크기와 그 변수의 갯수를 입력하는것이 휴먼에러를 줄이는 방법이다. char 가 1바이트고 int가 4바이트고 float이 4바이트 라는 보장은 할 수 없다. 컴파일러나 운영체제에 따라서 달라질 수 있기 때문이다. 그래서 가장 안전한 방법인 sizeof() 함수를 이용해서 해당 변수에 크기를 그때그때 검사해주는 방법이 좋다. 


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
 
int main() {        
    char *= (char*)malloc(sizeof(char)); //char 변수 하나의 메모리 할당
    *= 'h';
    printf("%d, %c\n",c, *c);//c의 주소값, c의 값 'h'
    free(c); //메모리 해제
}
cs

비슷한 예제로 int형 변수 4개를 할당받아보자. int에 크기에 4개를 곱한 사이즈를 할당 받았다.

그리고 (int *)로 형변환을 시켜서 i[1] 등을 했을때 4바이트씩 메모리를 증가시키게 된다. 그래서 아래와 같이 배열처럼 사용을 하면 된다. i[1] 은 *(i+1)과 완벽하게 같은 말이다. 


1
2
3
4
5
6
7
8
9
int main() {        
    int *= (int*)malloc(sizeof(int* 4); //int 사이즈 4개 메모리 할당
    i[0= 5;
    i[1= 1;
    i[2= 10;
    i[3= 20;
    printf("%d %d %d %d\n",i[0],i[1],i[2],i[3]);
    free(i); //메모리 해제
}
cs


출력 결과

5 1 10 20


sizeof(int) * 4를 그림으로 그려보면 아래와 같다.  힙영역에 0x1000번지 부터 총 16바이트에 메모리를 할당 받았다. 그리고 지역변수로 선언산 int형 포인터 변수인 i가 그 시작 주소값을 저장한다. i는 int* 이기 때문에 i는 0x1000번지를 가리키고 i+1을 하면 4바이트가 증가하여 1004번지를 가리키게 된다. 그래서 배열과 같이 사용할 수 있게 된다.


2차원 배열을 메모리로 할당 받아보자.

더블포인터를 이용해서 한번 할당받고 다시 각각의 2차원배열 요소들을 할당 받아야 한다.

아래예제를 보자. 처음보면 상당히 복잡하고 난해할 수 있다. 특히 이중포인터를 사용했기 때문에 쉽게 이해가 가지 않을 수 있다. 이해가 가지 않는다면 우선 해당 코드를 복사해서 사용하고 차차 이해해도 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main() {        
    int **ppi = (int**)malloc(sizeof(int** 3); //int* 사이즈 3개 메모리 할당
    int i,j;
    for(i = 0; i < 3; i++)
        ppi[i] = (int*)malloc(sizeof(int* 2); //int 사이즈 2개 메모리 할당
    
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 2; j++) {
            ppi[i][j] = (i + 1) * (j + 1);
        }
    }
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 2; j++) {
            printf("%d ", ppi[i][j]);
        }
    }
    printf("\n");
    
    for (i = 0; i < 3; i++)
        free(ppi[i]); //메모리 해제
    free(ppi); //메모리 해제
}
cs


출력결과

1 2 2 4 3 6


그리고 메모리 해제역시 2차원배열이기 때문에 각 요소들을 해제하고 2차원배열을 해제해줘야 한다.



Posted by 꿈만은공돌
,

 본포스팅은 C언어와 C++에서 콘솔화면 초기화 하는 예제를 소개하고 설명합니다. 콘솔 화면에 써진 것들을 모두 지우는 기능을 합니다. 


 콘솔화면을 지워야 하는 경우  windows.h 에 있는 system 함수를 사용하면 됩니다. system("cls")로 호출하면 콘솔화면이 지워지게 됩니다.


 int system(const char *cmd); 해당 함수에 원형입니다. cmd에 적은 명령을 수행하도록 하는 함수 입니다. 윈도우 키를 입력하고 cmd를 입력하여 콘솔창을 실행해서 cls를 입력하면 화면이 지워지는 것을 볼 수 있습니다. 따라서 해동함 수는 윈도우 콘솔에서 제공하는 명령들을 실행합니다.


 C언어와 C++ 모두에서 사용 가능하다. 콘솔에 코드를 작성하다보면 요긴하게 사용 할일이 많이 있다. 예를들어 해당 기능으로 간단한 게임등을 구현 가능합니다. 화면을 계속 지우고 그리면서 애니메이션 처럼 캐릭터가 움직이거나 특정 모양의 객체가 움직이는 것등을 포현할 수 있습니다. 물론 콘솔이라는 환경적 제약 때문에 조잡해 보일순 있지만 공부한 프로그래밍 언어를 가지고 간단한 게임을 구현하면서 더욱 흥미를 높일 수 있습니다. 예를들어 테트리스나 벽돌깨기등의 게임을 제작해보는 것도 방법입니다.


 간단한 사용예제입니다. 처음에 화면에 지우기 1초전 이라는 구문을 출력하고 1초간 딜레이를 줍니다. 그리고 System("cls")로 콘솔화면을 초기화 합니다. 그러면 이전에 출력했던 지우기 1초전 이라는 문자가 지워지는 것을 볼 수 있습니다. 그리고 다시 1초간 프로그램이 정지 했다가 화면에 지우기 완료 라는 문자가 출력되는 것을 볼 수 있습니다. 해당 예제에 사용된 sleep함수는 ms 단위로 프로그램을 일시 정지 시키는 기능을 합니다. 예를들어 1000을 적으면 1초가 되고 60,000은 1분을 의미합니다. 알아두면 유용하게 사용 가능합니다.


1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <windows.h>
 
int main() 
{    
    printf("지우기 1초전\n");
    Sleep(1000); //1초 딜레이
    system("cls"); //콘솔화면 지우기
    Sleep(1000);
    printf("지우기 완료\n");
}
cs




두번째 예제 입니다. 

 while을 사용하여 무한반복하면서 사용자로부터 값을 입력받습니다. scanf로 값을 입력받아서 입력받은 데이터가 1이면 초기화를 시키고 2이면 프로그램을 종료시킵니다. if문으로 입력받은 값이 1인지 2인지를 검사합니다. 1이면 system("cls") 함수 호출로 화면을 초기화 시킵니다. 2이면 break 문을 사용하여 while문을 나가게 합니다. 그래서 해당 프로그램이 종료하게 됩니다. 간단하지만 응용하면 다양한 프로그램을 작성할 수 있습니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <windows.h>
 
void main()
{
    int a;
    while(1) {
        printf("숫자입력(1:초기화, 2: 종료) : ");
        scanf("%d"&a);
        if(a==1) {
            system("cls");
        } else if(a==2) {
            break;
        }
    }    
}
 
cs

 콘솔을 지우는 것 이외에도 다양한 system 함수에 기능이 존재합니다. 예를들어 System함수안에 "calc"를 입력하면 계산기가 실행되는 것을 볼 수 있다. notepad 를 입력하면 메모장이 실행됩니다. 해당 명령어들은 하나의 프로그램이며 해당 프로그램들은 windows 폴더안에 있습니다. 알아두면 유용한 것 들이 많으니 나중에 정리해서 포스팅하도록 하겠습니다.


Posted by 꿈만은공돌
,

c언어/ C++ 에서 딜레이 함수 사용하기


프로그램을 진행하다보면 프로그램을 멈추어야할 떄가 존재합니다.


api를 사용하지 않고 아래와 같이 for문을 중첩사용하여 시간을 끄는 방식이 존재합니다.


int a=0;

for(int i=0; i < 10000; i++){ //1중첩 for문

for(int j=0; j < 10000; j++){ //2중첩 for문

a++; //무의미한 연산

}

}


하지만 위와 같은 방식은 원하는 시간만큼 정확하게 딜레이 시키기가 불가능 합니다.


그래서 C와 C++에서는 Sleep 함수를 사용합니다.


windows에 api 를이용하는 방식입니다.


#include <windows.h> 로 헤더파일을 추가해야합니다.


그리고 Sleep( ) 함수를 사용 합니다.


파라미터로 int 데이터를 사용하는데 ms단위입니다.


예를들어 Sleep(1000); 는 1000ms로 1초를 의미합니다.


프로그램을 사용하다보면 유용하게 쓸일이 많이 있습니다.


아래 예제를 참고바랍니다.



- 사용예제1 -


1
2
3
4
5
6
7
8
#include <stdio.h>
#include <windows.h>
 
int main() {    
    printf("시작\n");
    Sleep(1000); //1초정지
    printf("끝\n");
}
cs



- 사용예제2 -


1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <windows.h>
void main()
{
     int i=0;
     for(i=0; i<100; i++){
        printf("%d\n",i);
        Sleep(200);   //200ms 일시정지
     }
}
 
cs


- 사용예제3 -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <windows.h>
 
void main() {    
    printf("1초 정지\n");
    Sleep(1000);
    printf("2초 정지\n");
    Sleep(2000);
    printf("3초 정지\n");
    Sleep(3000);
    printf("4초 정지\n");
    Sleep(4000);
    printf("5초 정지\n");
    Sleep(5000);
    printf("끝\n");
}
cs


Posted by 꿈만은공돌
,


C, C++에서 콘솔화면에 커서를 원하는 위치로 이동하여 해당위치에 문자를 출력하는 방법이 존재한다.


아래 gotoxy함수를 참고하자.

windows.h 헤더에 포함되어 있는 api 를 이용하면 된다.

그러면 원하는 위치로 커서를 이동시킬 수 있다.

아래 gotoxy 함수를 소스에 추가해서 사용하면 편리하다.


1
2
3
4
5
6
7
8
9
#include <windows.h>
 
void gotoxy(int x, int y)
{
      COORD Cur;
      Cur.X = x;
      Cur.Y = y;
      SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),Cur);
}

cs




아래는 해당 함수를 가지고 키보드의 방향키를 입력 받아 해당 방향키대로 A 문자를 콘솔 화면에서 이동시키는 코드이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <conio.h>
#include <windows.h>
 
#define UP 0x48
#define LEFT 0x4B
#define RIGHT 0x4D
#define DOWN 0x50
#define SPACE 0x20
 
class CCharacter {
    int m_x, m_y;
    char m_ch;
    int m_key;
public:
    CCharacter(char ch) : m_x(0), m_y(0//생성자(x,y좌표값을 모두 0으로 초기화 시킨다.)
    {
        m_ch = ch;
    }
    void gotoxy() {//커서이동함수
        COORD Cur;
        Cur.X = m_x;
        Cur.Y = m_y;
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Cur);
    }
    bool KeyProcess() {
        if (kbhit()) // 키입력을 체크
        {
            m_key = getch();
            switch (m_key) {
            case UP: m_y--break;
            case DOWN: m_y++break;
            case LEFT: m_x--break;
            case RIGHT: m_x++break;
            case SPACE: break;
            }
            return true;
        }
        return false;
    }
    void Draw() {
        system("cls"); // 화면을 지운다.
        gotoxy();//커서를 이동시킨다.
        printf("%c", m_ch); //문자출력
        
    }
};
void main() {
    CCharacter ch('A');
    while (1) {
        boolean b = ch.KeyProcess();
        if(b) 
            ch.Draw();
    }
}
cs


아래 화면처럼 A 문자를 키보드 방향키로 마음대로 움직이게 할 수 있다.



방향키를 입력받을때마다 화면을 지우고 커서가 있는 좌표에 문자를 출력하는 방식이다.

화면을 초기화하는 것은 아래 링크를 참고하자.


C언어/C++ 콘솔 화면 초기화 방법, 예시 : http://hijuworld.tistory.com/53



Posted by 꿈만은공돌
,

C언어와 C++에서는 new 연산자와 malloc 함수를 이용해서 힙(heap)영역에 동적 메모리 할당을 받고 delete 연산자와 free 함수를 이용해서 동적 메모리를 해제하게 된다.


heap 영역에 메모리를 할당하게 되면 자유메모리 블록 리스트(Free memory block list)로 관리가 된다.


새로운 메모리를 할당 할 때에는 자유메모리 블록 리스트를 참고하여 필요한 만큼의 메모리를 할당 할 수 있는 공간을 찾는다.


찾으면 자유 메모리 블록 리스트에 추가하고 할당한 해당 메모리 공간을 초기화 시킨다.


아래 그림을 보면 쉽게 이해가 될 것이다.



처음 malloc(7000); 으로 7000바이트에 메모리를 할당받는다.

가장 첫번째 A영이있는 앞부분부터 탐색을 시작하여 사용하지 않는 메모리를 찾습는다.

B구역이 사용하지 않는 메모리이지만 크기가 4k밖에 안되게 때문에 계속탐색을 하여 가장 뒷부분인 E영역에 할당을 하게 된다


그리고 free(C) 로 C블록을 메모리를 해제시킵니다. 그러면 C영역에 5000바이트의 메모리가 해제가 된다..

이때 malloc(5000) 으로 메모리를 다시 할당하게 된다.

A부터 해서 다시 빈메모리영역을 찾습니다. G영역이 9k가 비어있기 때문에 앞쪽부터 5000바이트에 메모리를 할당 한다.


이런방식으로 계속 메모리 할당과 해제를 계속 하다보면 메모리 단편화가 일어날 수 있다.

메모리를 할당과 해제를 계속하다보면 중간중간 사용하는 메모리와 사용하지 않는 메모리가 존재하게 되된다. 

아래그림처럼 사용하는 메모리와 사용하지 않는 메모리가 있는데 여기서 300바이트가 넘는 메모리를 할당요청하게 되면 할당이 실패하게 된다. 전체 사용하지 않는 메모리는 850바이트나 되지만 단편화가 일어나서 메모리 할당에 실패하게 된다.



메모리 단편화를 피하려면 메모리를 너무 자주 할당 해제하는 것을 피하는 것이 좋다.


그리고 이와 같은 메모리 할당 방식은 앞에서 부터 빈 메모리영역을 탐색하기 때문에  Heap 메모리 영역을 탐색하는데 많은 시간이 소요될 수 있다. 특히 메모리 크기가 크고 빈 메모리영역이 뒷쪽에 있다면 매번 메모리 할당에 오버헤드가 발생한다.


그리고 할당만 하고 해제를 하지 않으면 메모리가 낭비될 수 있다.


사용자가 직접 해제를 시켜줘야하기 때문에 많은 주의가 필요하다.


이런 사용자가 직접 메모리를 할당하고 해제하는 방식과 반대로 가비지 컬렉터가 사용하지 않는 메모리를 자동으로 해제하는 방식에 가비지 컬렉션이라는 방식이 존재한다.

C++ 이후에 등장한 대부분에 언어에서는 이런 가비지 컬렉션을 사용한다. 예를들면 JAVA, C#, GO 언어 들이 가비지 컬렉션을 사용한다.


C#/.NET 가비지 컬렉션의 메모리 할당 및 해제 기본 원리 : http://hijuworld.tistory.com/32


Posted by 꿈만은공돌
,

포인터 배열(Array of Pointer)배열 포인터(Pointer to Array)는 비슷해 보이지만 많은 차이점이 있다.


기본적으로 포인터와 배열에 개념을 정확하게 이해해야 아래 내용을 이해하기 쉽다.


간단히 설명하자면 둘다 비슷한 속성을 가지고 있지만 배열은 메모리공간을 사용자 마음대로 접근하며 데이터를 넣고 수정할 수 있다.


포인터 같은 경우에는 특정 메모리에 주소값을 가지고 있어서 간접참조를 하는 방식이다.


쉽게 이해가 안가면 기본서를 참고해서 공부하는 것이 좋다.


배열 포인터와 포인터 배열 말장난 같지만 가장 쉽게 구분하는 방식은 앞에 붙은 단어는 수식어이고 뒤에 단어가 명사이다.


바나나우유, 딸기우유가 있으면 우유인데 딸기나 바나나가 첨가된 우유이다.


포인터 배열은 주소값들을 저장하는 배열 이다.


배열 포인터는 배열의 시작주소값을 저장할 수 있는 포인터 이다.



- 포인터 배열- 


char *data[] = {"가나다", "ABC", "포인터"};


의 경우 연산자 우선순위에 따라 뒤에 붙은 []이 우선이고 그다음 char *가 그 다음이다.


따라서 data 변수는 배열이다. 그리고 그 배열에 char *를 담는다.


"가나다" 문자열에 시작주소 값을 data[0] 에 넣고


"ABC" 문자열에 시작주소 값을 data[1] 에 넣는다.



아래는 사용 예제 이다. 위에 그림을 코드화 한 것이다. data는 포인터 배열로 char *를 저장하는 배열이다. 그래서 for문을 사용하여 해당 배열들을 탐색하며 값을 출력하는 것을 알 수 있다. data 배열의 사이즈는 명시하지 않았지만 오른쪽에 데이터 갯수를 보고 컴파일러가 알아서 3을 넣게된다.

char *data[] = {"가나다", "ABC", "포인터"};

int i;

for (i=0;i<3;i++) {
    printf("%s\n",data[i]);
}

- 출력 결과 -

가나다

ABC

포인터





 위와같이 포인터 배열은 문자열등과 같이 큰 길이에 데이터들을 포인터로 가르키게하고 해당 포인터들을 다시 배열로 묶어서 관리하기 쉽게 할때 사용하면 좋다. 위와같이 문자열 여러개를 각각 포인터가 가르키게하고 그 포인터들을 하나의 배열로 묶어서 관리해서 사용하기도 쉽고 가독성도 좋다. 만약에 따로따로 하나의 char* 로 할당을 했다면 for문을 사용하기도 힘들었을 것이다.


- 배열 포인터 -


char arr[3][7] = {"가나다", "ABC", "포인터"};

char (*data)[7];

data = arr;


의 경우 (*data) 가 먼저이고 char [7]가 그 다음이다.


따라서 data는 포인터 이다. 포인터는 주소값을 저장하는 곳이다. 따라서 char [7] 자료형에 주소값을 담는다.



data는 char [7]의 주소값을 가지기 때문에 data + 1을 하면 주소값이 sizeof(char) * 7 증가한 메모리의 주소 값을 가진다.  아래 그림과 같다.



아래는 사용 예제 이다. 위에 그림을 코드화 한것이다. arr은 2차원 배열로 문자열들을 할당하고 data는 배열 포인터이다. char [7]을 의 포인터를 가르키는 역활을 한다. 

char arr[3][7] = {"가나다", "ABC", "포인터"};
char (*data)[7];

data = arr;

int i;

for (i=0 ; i<3 ; i++) {
    printf("%s\n", *(data+i));
}


- 출력 결과 -

가나다

ABC

포인터


 배열 포인터는 보통 함수선언을 해서 2차원 배열의 데이터를 파라미터로 받을때 많이 사용한다. 위의 예제에서 처럼 2차원으로 선언한 배열이 있다면 이것을 배열 포인터로 가리키게 하여 접근할 수 있다.

 한가지 주의해야 할 점은  위의 예제에서 처럼 data 입장에서는 arr이 몇개의 데이터를 가지고 있는지 알 수 없다. 그래서 함수에 넘겨줄때에는 3개의 char [7]의 데이터가 존재한다는 사실을 알려주기 위해 추가에 파라미터 변수가 필요하다. 그래야 안정적인 코드작성이 가능하다.

 포인터라는 개념을 공부할 때에는 이해가 가는것 같지만 나중에 실전에서 내가 선언한 배열이나 포인터등을 다른함수에 넘겨주고 다시 반환하는 등에 과정에서 많이 어려워하는 경우가 많다.  


- 참고 사이트 : http://www.soen.kr/

Posted by 꿈만은공돌
,



int arr[10][30]; 와 같이 다차원 배열에 값을 -1이나 0으로 초기화 할때 for문을 이용할 수도 있지만 번거롭고 불편할 수 있다.

그럴땐 메모리를 특정 값으로 셋팅하는 memset() 함수를 이용하면 된다.


1
2
int arr[10][10];
memset(arr, -1sizeof(arr));




위와 같이 입력하면 -1로 해당 배열에 값을 초기화 할 수 있다.

배열에 인자들을 출력해보면 -1로 출력되는것을 볼 수 있다.

0으로 초기화 하고 싶으면 -1대신 0을 입력하면 된다.

Posted by 꿈만은공돌
,



Vector a 에서 vector b와 겹치는 인자를 제거한 결과를 반환하는 함수 이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
 
#include<iostream>
#include<vector>
using std::cout;
using std::endl;
using std::vector;
 
vector<int> duplication(vector<int>& a, vector <int>& b) {
    vector<int>::iterator iter;
    vector<int>::iterator iter_b;
    vector<int> c = a; //a의 값 복사
    
    for (iter_b = b.begin(); iter_b != b.end(); iter_b++) {
        for (iter = c.begin(); iter != c.end();) {
            if (*iter == *iter_b)
                iter = c.erase(iter); //중복 제거
            else
                iter++;            
        }        
    }
    return c; //결과 반환
}
 
int main() {
    vector<int> a;
    vector<int> b;
 
    a.push_back(1);
    a.push_back(3);
    a.push_back(-1);
    a.push_back(11);
    a.push_back(-5);
    
    b.push_back(-1);
    b.push_back(-5);
    b.push_back(4);
 
    vector<int> c = duplication(a,b); //a에서 b와 겹치는 인자 제거한 결과 반환
    
    //원본 a출력
    for (vector<int>::iterator iter = a.begin(); iter != a.end(); ++iter) 
        cout << *iter <<" ";
    cout << endl;
 
    //b와의 중복 제거된 c출력
    for (vector<int>::iterator iter = c.begin(); iter != c.end(); ++iter) 
        cout << *iter << " ";    
    cout << endl;
 
    return 0;
}
 
cs


- 출력 결과 -



1 3 -1 11 -5

1 3 11

Posted by 꿈만은공돌
,