반응형

문제 1

길이가 5인 int형 배열 arr을 선언하고 이를 1, 2, 3, 4, 5로 초기화한 다음, 이 배열의 첫 번째 요소를 가리키는 포인터 변수 ptr을 선언한다. 그 다음 포인터 변수 ptr에 저장된 값을 증가시키는 형태의 연산을 기반으로 배열요소에 접근하면서 모든 배열요소의 값을 2씩 증가시키고, 정상적으로 증가가 이뤄졌는지 확인하는 예제

#include <stdio.h>

int main()
{
	int arr[5] = { 1,2,3,4,5 };//길이가 5인 int형 배열 arr을 선언하고 이를 1, 2, 3, 4, 5로 초기화
	int* ptr = arr;//배열의 첫 번째 요소를 가리키는 포인터 변수 ptr을 선언
	for (int i = 0; i < 5; i++)
		*(ptr++) += 2;
    //포인터 변수 ptr에 저장된 값을 증가시키는 형태의 연산을 기반으로 배열요소에 접근하면서 모든 배열요소의 값을 2씩 증가
	for (int i = 0; i < 5; i++)
		printf("arr[%d]: %d   ", i, arr[i]);
	
	return 0;
}

문제2

문제 1에서는 포인터 변수 ptr에 저장된 값을 변경시켜가면서 배열요소에 접근하라고 하였다. 그런데 이번에는 포인터 변수 ptr에 저장된 값을 변경시키지 않고, ptr을 대상으로 덧셈연산을 하여, 그 결과로 반환되는 주소 값을 통해서 모든 배열요소에 접근하여 값을 2씩 증가시키는 예제

#include <stdio.h>

int main()
{
	int arr[5] = { 1,2,3,4,5 };//길이가 5인 int형 배열 arr을 선언하고 이를 1, 2, 3, 4, 5로 초기화
	int* ptr = arr;//배열의 첫 번째 요소를 가리키는 포인터 변수 ptr을 선언
	for (int i = 0; i < 5; i++)
		*(ptr + i) += 2;
    //포인터 변수 ptr에 저장된 값을 증가시키는 형태의 연산을 기반으로 배열요소에 접근하면서 모든 배열요소의 값을 2씩 증가
	for (int i = 0; i < 5; i++)
		printf("arr[%d]: %d   ", i, arr[i]);
	
	return 0;
}

문제3

길이가 5인 int형 배열 arr을 선언하고 이를 1~5로 초기화하고 이 배열의 마지막 요소를 가리키는 포인터 변수 ptr을 선언. 그 다음 ptr에 저장된 값을 감소하는 형태의 연산을 기반으로 모든 배열요소에 접근해서 전부 더하고 출력

#include <stdio.h>

int main()
{
	int arr[5] = { 1,2,3,4,5 };
		int* ptr = &arr[4];//배열의 마지막 요소를 가리키는 포인터 변수 ptr을 선언.
		int sum=0;

		for (int i = 0; i < 5; i++)
			sum += *ptr - i;
            //그 다음 ptr에 저장된 값을 감소하는 형태의 연산을 기반으로 모든 배열요소에 접근해서 전부 더하고 출력
		printf("%d", sum);
	return 0;
}

배열 안의 값 arr[4]=*(ptr+4)  ptr이 arr[0]일때

배열의 주소 &arr[4]=ptr+4 

문제4

길이가 6인 int형 배열 arr을 선언 1~6으로 초기화, 배열의 저장된 값은 순서가 6~1이 되도록 변경하는 예제 작성.

단, 배열의 앞과 뒤를 가리키는 포인터 변수 두개를 선언해서 이를 활용하여 저장된 값의 순서 바꾸야 한다.

#include <stdio.h>

int main()
{
	int arr[6] = { 6,5,4,3,2,1 };
	int* ptr1 = arr;
	int* ptr2 = &arr[5];
	int temp = 0;

	for (int i = 0; i < 3; i++)
	{
		temp = *(ptr1 + i);//ptr+0의 값을 저장
		*(ptr1 + i) = *(ptr2-i);//ptr+0을 ptr2-0의 값으로 변경
		*(ptr2 - i) = temp;//ptr2-0의 값을 temp의 값으로 변경
	}
	for (int i = 0; i < 6; i++)		
	{
		printf("%d ", arr[i]);
	}	
	return 0;
}
반응형
반응형

13-1 포인터와 배열의 관계

배열의 이름도 포인터이다. 

int arr[3];라는 배열을 선언 했을때 arr 그 자체는 arr[0]의 주소값을 가리킨다.

이게 무슨말인지 확인해보자

#include <stdio.h>

int main()
{
	int arr[3] = {11,22,33};
	printf("arr : %p \n", arr);
	printf("arr[0] : %p \n", &arr[0]);	
    printf("arr[1] : %p \n", &arr[1]);
	printf("arr[2] : %p ", &arr[2]);
    
	return 0;
}

 

위 소스코드 출력결고

arr과 arr[0]의 주소 출력이 같은걸 확인할 수 있다.  배열의 이름은 배열의 시작 주소의 값을 의미한다는 걸 확인했다.

또 이 소스로 확인 할 수 있는 부분은 int형 배열의 크기는 4바이트기 때문에 배열에서 다음 주소 값의 차가 4바이트임을 보여준다.

 

배열이름이 포인터라면 포인터와 배열의 차이점은 무엇일까?

배열은 안에 주소 값을 변경 할 수 없지만 arr= 0070F6D2; 로 변경불가

포인터는 주소 값을 변경 할 수 있다. ptr = 0070F6D2; 변경가능

배열은 그 값을 바꿀 수 없는 '상수 형태의 포인터'이다.

 

 

 

13-2 포인터 연산

int num[]={10, 20, 30};
int* ptr=num;

printf("num : %d ", *ptr+1);
int num[]={10, 20, 30};
int* ptr=num;

printf("num : %d ", *(ptr+1));

두 가지 소스의 출력결과가 예상되는가? 

첫 번째 소스는 *ptr이 num배열을 가리키고 있기 때문에 num[0] 값에 +1한 11이 출력 될 것이다.

두 번째 소스는 포인터 변수 ptr에 +1을 하고 그 값을 출력하기 때문에 20이 출력 될 것이다.

포인터 변수에 1을 더한다면 그 포인터 변수가 가지고있는 자료형의 크기만큼 증가 시킨다.

double형 포인터에 n을 더했다면 ptr+n = n * sizeof(double)의 크기만큼 증가 할 것이다. 

 

 중요한 결론은 arr[n] == *(arr+n) 

int arr[]={10, 20};

printf("%d %d\n", *(arr+0), arr[0]); //10 10
printf("%d %d", *(arr+1), arr[1]); //20 20 

똑같은 값이 출력되는 걸 확인할 수 있다. 이는 이후에 다차원 배열에서도 성립하는 유용한 식이니, 반드시 이해하고 기억해야 한다.

 

13-3 상수 형태의 문자열을 가리키는 포인터

char str[]="My String";
char* str2= "Your String";

문자열 배열에 str[0~10] "My String\n"를 저장한다.

"Your String\n"은 자동 메모리 공간에 할당하고 str2에 'Y'의 주소값을 저장한다.

#include <stdio.h>

int main()
{
	char str[] = "My String";
	char* str2 = "Your String";
	printf("%s %s \n", str, str2);

	str2 = "Our String";// 가리키는 대상 변경
	printf("%s %s \n", str, str2);

	str[0] = 'X'; //문자열 변경 성공!
	//str2[0] = 'X'; //문자열 변경 실패!
	printf("%s %s \n", str, str2);
	
	return 0;
}

str2 포인터 문자열에 다른 문자열을 넣으면 str2가 가리키는 문자열 주소를 변경한다. 소스에서는 'Y'주소를 가리키다가 'O'소스를 가리키는 포인터로 변경되었다. 그래서 2번째 줄에는 Our String이 출력된다. 이런 문자열을 '상수 형태의 문자열'이라 한다.

 

str[]은 3번째 출력에서 확인해보면 배열을 대상으로 값이 변경가능하다. str[0]='X'로 바뀠기 때문에 Xy String이 출력된다. 이런 문자열을 '변수 형태의 문자열'이라 한다.

 

13-4 포인터 변수로 이뤄진 배열: 포인터 배열

포인터 변수도 변수라 배열로 선언할 수 있다. 문자열 저장하는 포인터 배열을 만들 수 있다. 

char * strArr[3]; 길이가 3인 char형 포인터 배열을 만들면 어떤 길이의 문자열이든 3가지를 저장 할 수 있다.

#include <stdio.h>

int main()
{
	char *str[3] = { "asdasdasdasdas", "asdasda", "asdweds" };
	printf("%s \n", str[0]);
	printf("%s \n", str[1]);
	printf("%s \n", str[2]);
	
	return 0;
}

결과

char *str[3]에는 asdad....은 메모리에 저장되고 1~3번째 'a'의 주소가 저장된다.

실제 저장된 값은 ex) char * str[3]={0x1004, 0x1048, 0x2012}; //반환된 주소 값은 임의로 결정하였다.

첫 번째 주소를 불러서 \0까지 char를 읽는다.

 

-윤성우 열혈C프로그래밍을 참고해여 작성한 글입니다.

반응형
반응형

Point라는 변수는 주소값을 저장하는 변수이다.

여기서 주소값은 정수로 저장되기 때문에 int형 변수에 주소값을 저장 할 수도 있다.

그렇다면 왜 C에서는 Point라는 변수에 따로 주소값을 저장하는걸까?

메모리에 저장된 주소값

위 사진과 같이 변수 num의 주소값은 정수 0x12ff76이다.

정수이기 때문에 int변수에도 저장과 출력이 가능하지만 메모리 주소에 접근하지는 못한다.

우리가 주소값을 얻는 이유가 뭘까?

현실에서 생각해보면 부산광역시 남구 감만동이라는 주소를 알고 거기에 찾아가기 위해 우리는 주소를 집마다 부여한다.

컴퓨터에서도 메모리의 주소를 알고 주소에 접근(찾아가기)위해 Point 변수를 사용한다.

 

point변수의 크기는 64bit 시스템에서는 8byte이고 32bit 시스템에서는 4byte이다.

포인터변수 앞에서 자료형을 정해주는 이유는 8byte포인터가

int num=7;
int* pnum;
pnum = &num;

정수형 변수 num을 가리킨다면 포인터 변수 pnum은 int형 포인터 변수로 4바이트 메모리 공간에 부호 있는 정수의 형태로 데이터를 읽고 쓴다.

4byte까지 읽는다

그렇다면 만약 double 변수형에 int*(정수 포인터)로 연산하면 어떻게 될까?

	double num = 7.3;
	int* pnum = &num;
	*pnum = 4;
	printf("%d", *pnum);

형이 불이치 하지만 컴파일은 된다.

pnum이 가리키는 것은 double형 변수인데, pnum이 int형 변수이므로 int형 데이터처럼 해석된다.

 

다시 말하면

int형 포인터 변수는 4바이트 메모리 공간까지 읽고

double형 포인터변수는 8바이트 메모리 공간까지 읽는다.

포인터 변수마다 각각의 고유 자료형 이름을 가지는 이유는 변수가 시작되는 주소 값에서 얼마만큼의 크기를 읽어드릴지 위한 단위의 지정​하기 위함이다.

 

그래서 포인터의 형과 변수의 자료형을 맞춰줘야한다.

 

포인터 사용 방법

#include <stdio.h>

int main()
{
	int num = 100, num2 =100;
	int* ptr; //포인터 변수 선언
	ptr = &num; //ptr을 num주소값으로 초기화한다.
	*ptr += 30; //포인터가 가리키는 주소값에 변수의 값을 바꾼다.
	ptr = &num2;//ptr은 num2를 가리킨다.
	*ptr -= 30;//num2 -= 30과 동일

	printf("%d / %d", num, num2);
	
	return 0;
}

type없이 *ptr만 하면 메모리의 주소에 직접방문 할 수 있다.

직접방문해서 *ptr이 가리키는 값에 연산을 할 수도 있고, *ptr이 가리키는 값을 직접 출력할 수도 있다.

 

&는 변수의 주소 값을 반환한다.

 

-윤성우 열혈C프로그래밍을 참고해여 작성한 글입니다.

반응형
반응형

11-1 

문제1

정수 5개 최댓값, 최솟값, 총합 출력하는 예제

#include <stdio.h> 


int main(void) { 

	int A[5];
	int sum=0;
	for (int i = 0; i < 5; i++)
		scanf_s("%d", &A[i]);

	int max = A[0];
	int	min = A[0];

	for (int i = 0; i < 5; i++)
	{
		if (A[i] > max)
			max = A[i];
		if (A[i] < min)
			min = A[i];

		sum += A[i];
	}
	
	printf("최대 : %d 최소 : %d 합 : %d", max, min, sum);

	return 0;

}


문제2

%s 사용하지 않고 Good time문자 출력

#include <stdio.h> 


int main(void) {

	char A[] = { 'G', 'o', 'o', 'd', ' ', 'T', 'i','m','e' };

	for (int i = 0; i < sizeof(A); i++)
	{
		printf("%c", A[i]);
	}
	return 0;

}

11-2

문제 1 

영단어 하나를 입력받아 길이와 영단어를 출력

#include <stdio.h> 


int main(void) {

	char A[6];
	int i = 0;
	scanf_s("%s", A, 6);

	while (A[i] != NULL)
		i++;

	printf("%d\n", i);
	printf("%s", A);

	return 0;

}


문제 2

영단어 하나를 입력받아 거꾸로 뒤집어서 출력

#include <stdio.h> 


int main(void) {

	char A[256];
	char temp;
	int cnt=0;
	char B[256];

	scanf_s("%s", A, 256);

	while (A[cnt] != NULL)
		cnt++;
	int tempcnt = cnt;
	for (int i = 0; i < cnt; i++)
	{
		
		temp = A[tempcnt-1];
		B[i] = temp;
		tempcnt--;
		
	}
	B[cnt] = A[cnt];

	printf("%s", B);

	return 0;

}


 

문제3

입력된 영단어 중 아스키코드로 가장 큰 값을 찾아 출력

#include <stdio.h> 


int main(void) {

	char A[256];
	int num=0;
	char max;
	scanf_s("%s", A, 256);
	while (A[num]!=NULL)
	{
		if (A[num] < A[num + 1])
			max = A[num + 1];
		num++;
	}
	printf("%c", max);


	return 0;

}


반응형
반응형

subEdit (서브 클래스)

SubEdit MainWindow

소스 코드

#define ID_EDIT1 100
#define ID_EDIT2 101
HWND hEdit1, hEdit2;
WNDPROC OldEditProc;
LRESULT CALLBACK EditSubProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	switch (iMessage) {
	case WM_KEYDOWN:
		if (wParam == VK_RETURN) {
			MessageBox(hWnd, TEXT("Enter is Pressed"), TEXT("Edit"), MB_OK);
		}
		break;
	}
	return CallWindowProc(OldEditProc,hWnd,iMessage,wParam,lParam);//OldEditProc로 리턴
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR *Mes=TEXT("에디트의 Enter키 입력을 검출합니다");
	switch (iMessage) {
	case WM_CREATE:
		hEdit1=CreateWindow(TEXT("edit"),NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
			ES_AUTOHSCROLL,
			10,10,200,25,hWnd,(HMENU)ID_EDIT1,g_hInst,NULL);
		hEdit2=CreateWindow(TEXT("edit"),NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
			ES_AUTOHSCROLL,
			10,50,200,25,hWnd,(HMENU)ID_EDIT2,g_hInst,NULL);
		SetFocus(hEdit1);
		OldEditProc=(WNDPROC)SetWindowLongPtr(hEdit1,GWLP_WNDPROC,(LONG_PTR)EditSubProc);//hEdit1에 EditSubRroc로 set한다. 
		//기존 Proc는 밀어내고 OldEditProc가 된다.
		return 0;
	case WM_KEYDOWN:
		if (wParam == VK_RETURN) {
			MessageBox(hWnd,TEXT("엔터키를 눌렀습니다."),TEXT("알림"),MB_OK);
		}
		return 0;
	case WM_PAINT:
		hdc=BeginPaint(hWnd, &ps);
		TextOut(hdc,10,100,Mes,lstrlen(Mes));
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		SetWindowLongPtr(hEdit1,GWLP_WNDPROC,(LONG_PTR)OldEditProc);
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

메인 윈도우 소스

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR *Mes=TEXT("에디트의 Enter키 입력을 검출합니다");
	switch (iMessage) {
1	case WM_CREATE:
		hEdit1=CreateWindow(TEXT("edit"),NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
			ES_AUTOHSCROLL,
			10,10,200,25,hWnd,(HMENU)ID_EDIT1,g_hInst,NULL);
		hEdit2=CreateWindow(TEXT("edit"),NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
			ES_AUTOHSCROLL,
			10,50,200,25,hWnd,(HMENU)ID_EDIT2,g_hInst,NULL);
		SetFocus(hEdit1);
		OldEditProc=(WNDPROC)SetWindowLongPtr(hEdit1,GWLP_WNDPROC,(LONG_PTR)EditSubProc);//hEdit1에 EditSubRroc로 set한다. 
		//기존 Proc는 밀어내고 OldEditProc가 된다.
		return 0;
	case WM_KEYDOWN:
		if (wParam == VK_RETURN) {
			MessageBox(hWnd,TEXT("엔터키를 눌렀습니다."),TEXT("알림"),MB_OK);
		}
		return 0;
	case WM_PAINT:
		hdc=BeginPaint(hWnd, &ps);
		TextOut(hdc,10,100,Mes,lstrlen(Mes));
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		SetWindowLongPtr(hEdit1,GWLP_WNDPROC,(LONG_PTR)OldEditProc);
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

1. 윈도우 생성시 에디터 2개를 만든다. 

첫 포커싱은 에티터 1이다

OldEditProc=(WNDPROC)SetWindowLongPtr(hEdit1,GWLP_WNDPROC,(LONG_PTR)EditSubProc)

OldEditProc에는 기존의 메인윈도우가 리턴된다.

(WNDPROC)SetWindowLongPtr(hEdit1,GWLP_WNDPROC,(LONG_PTR)EditSubProc)는 hEdit1을 선택했을때 운영체제가 메인윈도우로 명령을 보내지 않고 EditSubProc가 명령을 가로챈다.

 

명령을 가로채면 EditSubProc의 명령이 수행된다.

LRESULT CALLBACK EditSubProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	switch (iMessage) {
	case WM_KEYDOWN:
		if (wParam == VK_RETURN) {
			MessageBox(hWnd, TEXT("Enter is Pressed"), TEXT("Edit"), MB_OK);
		}
		break;
	}
	return CallWindowProc(OldEditProc,hWnd,iMessage,wParam,lParam);//OldEditProc로 리턴
}

Edit1을 선택하면 Enter is Pressed 메시지 박스가 뜬다.

그러고 다시 윈도우 프로시져 메인윈도우(OldEditProc)로 명령어를 보낸다.

그렇기 때문에 2번째 부터 엔터를 누르면 

	case WM_KEYDOWN:
		if (wParam == VK_RETURN) {
			MessageBox(hWnd,TEXT("엔터키를 눌렀습니다."),TEXT("알림"),MB_OK);
		}
		return 0;

메인윈도우의 엔터 메시지를 보내기 때문에 "엔터키를 눌렀습니다" 메시지 박스가 뜬다.

 

서브클래스 활용

1. 네이버 로그인화면에서 아무것도 입력하지 않고 엔터를 누르면 아무일도 일어나지 않는다(메인 윈도우에서 엔터)

2. 아이디를 입력하고 엔터를 누르면 비밀번호로 에디터로 포커스가 넘어가고 비밀번호를 입력해달라는 텍스트가 뜬다.

이 이벤트는 ID창 에디터로 엔터 명령이 전달되었을때만 수행되는것이다.

RandGrp

랜덤 한 위치에 점을 찍으면서 마우스 오른쪽 클릭시 원을 그리는 프로그램

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	int i;

	switch (iMessage) {
	case WM_CREATE:
		SetTimer(hWnd,1,50,NULL);
		return 0;
	case WM_TIMER://1000개 찍고 겟메시지로 돌아옴
//0.05초마다 점을 1000개 찍고 return 0 가면 겟 메시지를 다시 읽는다
		hdc=GetDC(hWnd);
		for (i=0;i<1000;i++) {
			SetPixel(hdc,rand()%500, rand()%400,
				RGB(rand()%256,rand()%256,rand()%256));
		}
		ReleaseDC(hWnd, hdc);
		return 0;
	case WM_LBUTTONDOWN:
		hdc=GetDC(hWnd);
		Ellipse(hdc,LOWORD(lParam)-10,HIWORD(lParam)-10,
			LOWORD(lParam)+10,HIWORD(lParam)+10);
		ReleaseDC(hWnd, hdc);
		return 0;
/* 이렇게 무한 루프를 돌면 안된다.	윈도우가 처음 시작되면  WM_PAINT에 있는 for문을 계속 돌면서 찍고있다.
	case WM_PAINT:
		hdc=BeginPaint(hWnd, &ps);
		for (;;) {
			SetPixel(hdc,rand()%500, rand()%400,
				RGB(rand()%256,rand()%256,rand()%256));
		}
		EndPaint(hWnd, &ps);
		return 0;
//*/
	case WM_DESTROY:
		KillTimer(hWnd, 1);
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

우선 돌아가는 프로그램은 타이머로 점을 찍어주면 마우스 왼쪽을 눌렀을때도 반응한다.

하지만 WM_PAINT에서 무한루프를 통해 계속 점을 찍으면 마우스 왼쪽버튼을 눌렀을때 반응하지 않는다.

왜 그런일이 일어나는 걸까?

무한루프로 점을 찍으면 다른 명령어가 들어와도 확인 할 수 없다. CPU는 점을 찍는일을 하고있기 때문에 다른곳에 눈을 돌릴 틈(return 0)이 없기 때문이다.

WM_TIMER에서 점을 찍는다면 1000개를 찍고나서 eektl WinMain에 while문에서 메시지가 있는지 없는지 확인 할 것이다. 우리가 만약 왼쪽버튼을 누르는 메시지를 발생시켰다면 메시지가 있으므로 DispatchMessage로 왼쪽버튼을 클릭하면 일어나는 이벤트를 수행한다. 

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
		  ,LPSTR lpszCmdParam,int nCmdShow)
{

	while (GetMessage(&Message,NULL,0,0)) {
		TranslateMessage(&Message);
		DispatchMessage(&Message);
	}
	return (int)Message.wParam;
}

이걸 눈으로 확인할 수 있는 방법은 for문의 1000을 100000으로 늘리면 점을 더 많이 찍고 리턴하기 때문에 원을 그리는 반응 속도가 더 느릴것이다.

RandGrip처럼 동시에 두 가지 작업을 수행할려면 저런 방식으로 밖에 프로그램을 만들지 못하는 걸까?

Thread를 사용하면 무한루프를 돌아도 다른 명령을 받을 수 있는 프로그램을 만들 수 있다.

Thread를 사용한 타임 쉐어링 프로그램은 쓰레드마다 시간을 할당하고 시간이 끝나면 다른 쓰레드로 작업한다.

동시에 작업하는 것처럼 보이지만 CPU가 빠르게 쓰레드를 Context Switching 해주기 때문에 우리는 동시에 작업하는 것처럼 보이는 것이다.

 

한글2018에서 한글 'ㅁㄴㅇ'을 입력하고 엔터를 누르면 'asd'라고 바뀌는걸 볼 수 있다.

이건 백그라운드에서 'ㅁㄴㅇ'이라는 단어가 있는지 사전을 뒤지고 없어서 영어로 변환하고, 영어로 변환한 뒤 영어사전에도 asd라는 단어가 없기 때문에 밑에 빨간줄이 쳐져있는 것 이다.

Thread 없이 위같은 기능을 똑같이 구현 할려면, 한글 사전을 찾는 시간, 한글을 영어로 변환하는 시간, 영어사전을 찾는시간 동안 우리는 대기하고 있어야 할 것 이다.

또 통신시스템에서 서버는 한명의 클라이언트랑만 소통할 수 있을것이다.

 

Thread를 사용한 RandGrp

DWORD WINAPI ThreadFunc(LPVOID temp)
{
	HDC hdc;
	hdc=GetDC(MainWnd);
	for (;;) {//무한 루프 돌아도 다른 명령을 잘 받음
		for (int i=0;i<100;i++) 
			SetPixel(hdc,rand()%500, rand()%400,
			RGB(rand()%256,rand()%256,rand()%256));
	}
	ReleaseDC(MainWnd, hdc);
	return 0;
}

// 프로그램 실행과 동시에 스레드를 하나 만든다.
long FAR PASCAL WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	DWORD ThreadID;
	HANDLE hThread;
	HDC hdc;

	switch(iMessage) {
	case WM_RBUTTONDOWN:
		hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &ThreadID);
		//CreateThread 쓰레드 생성
		CloseHandle(hThread);
		return 0;
	case WM_LBUTTONDOWN:
		hdc=GetDC(hWnd);
		Ellipse(hdc,LOWORD(lParam)-10,HIWORD(lParam)-10,
			LOWORD(lParam)+10,HIWORD(lParam)+10);
		ReleaseDC(hWnd, hdc);
		return 0;	
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}

오른쪽버튼 클릭시 쓰레드를 만든다.

쓰레드는 무한으로 돌고 있지만 CPU는 메인 쓰레드 WndProc와 ThreadFunc을 타임 슬라이스마다 번갈아가면서 할당하기 때문에 무한루프로 돌아도 다른 명령을 잘 받는다.

 

Thread로 문자열 특정 문자 입력시 카운트하는 프로그램을 만들어보자

TCHAR str[256];

DWORD WINAPI ThreadFunc(LPVOID temp)
{
	HDC hdc;
	TCHAR num[128];
	hdc = GetDC(hWndMain);
	int iCount = 0;
	for (int i = 0; i < lstrlen(str); i++){
		if (str[i] == 'a')
			iCount++;
		wsprintf(num, TEXT("%d"), iCount);
		TextOut(hdc, 10, 20, num, lstrlen(num));
	}
	ReleaseDC(hWndMain, hdc);
	return 0;
}


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	
	DWORD ThreadID;
	HANDLE hThread;
	int len;

	switch (iMessage) {
	case WM_CHAR:
//*
		len=lstrlen(str);
		str[len]=(TCHAR)wParam;
		str[len+1]=0;
		InvalidateRect(hWnd,NULL,FALSE);
		hWndMain = hWnd;
		hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);//문자 누를때마다 쓰레드 생성후 삭제
		//CloseHandle(hThread);
		return TRUE;
//*/
/*
		if ((TCHAR)wParam == ' ') {
			str[0]=0;
		} else {
			len=lstrlen(str);
			str[len]=(TCHAR)wParam;
			str[len+1]=0;
		}
		InvalidateRect(hWnd,NULL,TRUE);
//*/
		return 0;
	case WM_PAINT:
		hdc=BeginPaint(hWnd,&ps);
		TextOut(hdc,100,100,str,lstrlen(str));
		EndPaint(hWnd,&ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

문자를 누를때마다 str[]문자열에 누른 문자를 전달하고 ThreadFunc 쓰레드를 실행한다.

메인윈도우를 가리키는 윈도우 핸들 hWndMain

쓰레드는

str문자열에 담긴 문자 중에 'a'를 찾고, 찾으면 iCount에 1을 늘리고 iCount의 숫자를 메인 윈도우에 출력한다. 

 

문자열에 검사하고 싶은 문자가 여러 개라면?

HWND MainWnd;
TCHAR str[256];

struct ThreadParam {
	int x, y;
	TCHAR ch;
} Param[4] = {
	{ 10, 10, TEXT('a') },
	{ 10, 30, TEXT('b') },
	{ 10, 50, TEXT('c') },
	{ 10, 70, TEXT('d') }
};

DWORD WINAPI ThreadFunc(LPVOID temp)//함수포인트
{
	HDC hdc;
	int len;
	int aCount = 0;

	//temp가 void이므로 ThreadParam 형태로 만듬
	ThreadParam *P = (ThreadParam *)temp;

	TCHAR tmp[256];

	hdc = GetDC(MainWnd);
	len = lstrlen(str);
	for (int i = 0; i < len; i++)
	{
		if (str[i] == P->ch)
			aCount++;
	}
	wsprintf(tmp, TEXT("%c의 개수 : %d"), P->ch, aCount);
	TextOut(hdc, P->x, P->y, tmp, lstrlen(tmp));
	ReleaseDC(MainWnd, hdc);
	return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	DWORD ThreadID;
	//HANDLE hThread[4];
	HANDLE hThread;
	PAINTSTRUCT ps;
	int len;

	switch (iMessage) {
	case WM_CHAR:
		len = lstrlen(str);
		str[len] = (TCHAR)wParam;
		str[len + 1] = 0;
		InvalidateRect(hWnd, NULL, FALSE);//무효영역 발생(다시 그려야 할 영역)
		//for (int i = 0; i < 4; i++){
		//	hThread[i] = CreateThread(NULL, 0, ThreadFunc, &Param[i], 0, &ThreadID);
		//	CloseHandle(hThread[i]);
		//}
		CreateThread(NULL, 0, ThreadFunc, &Param[0], 0, &ThreadID);
		//CloseHandle(hThread);
		CreateThread(NULL, 0, ThreadFunc, &Param[1], 0, &ThreadID);
		//CloseHandle(hThread);
		hThread = CreateThread(NULL, 0, ThreadFunc, &Param[2], 0, &ThreadID);
		//CloseHandle(hThread);
		hThread = CreateThread(NULL, 0, ThreadFunc, &Param[3], 0, &ThreadID);
		//CloseHandle(hThread);
		return 0;
	case WM_LBUTTONDOWN:
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc, 100, 100, str, lstrlen(str));gkrh 
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

구조체로 위치와 검사할 문자를 설정해두고 하나의 ThreadFunc에 여러 인자를 전달해서 사용한다.

 

반응형
반응형

비트맵

#include "resource.h"
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc,MemDC;
	PAINTSTRUCT ps;
	HBITMAP MyBitmap, OldBitmap;
	switch (iMessage) {
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		1 MemDC=CreateCompatibleDC(hdc);//주요 소스 /원도우그리기 도구통과 상응하는 도구통을 메모리에 만듬 
		2 MyBitmap=LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1));//IDB_BITMAP1에 있는 그림을 로드
		3 OldBitmap=(HBITMAP)SelectObject(MemDC, MyBitmap);//메모리에 그림을 넣는다
		4 BitBlt(hdc, 0,0,1000,160,MemDC,0,0,SRCCOPY);//메모리에서 윈도우로 그림을 넣는다
		//StretchBlt(hdc,0,0,246,320,MemDC,0,0,123,160,SRCCOPY);
		5 SelectObject(MemDC,OldBitmap);
		6 DeleteObject(MyBitmap);
		7 DeleteDC(MemDC);
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

1. 메모리에 그리기 도구통을 줍니다

2. MyBitmap에 리소스에 있는 IDB_BIMAP1을 로드합니다

3. MyBitmap를 MemDC메모리에 있는 그리기 도구통에 넣는다. OldBitmap에는 기존 MyBitmap의 값이 들어있다

4. BitBlt로 메모리에서 윈도우로 그림을 보낸다

5. MemDC의 값을 기존 값으로 바꾸고

6. MyBitmap 반납

7. 그리기 도구통 반납

 

text 파일 그림 출력

data.text

메모장에 보이는 숫자들이 멀 의미하고 있는거 같은가?

저 숫자를 읽어서 그림으로 출력하는 프로그램을 만들어 보겠다.

소스

FILE * fpin;

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     HDC         hdc, MemDC;
	 HBITMAP MyBitmap, OldBitmap;
     PAINTSTRUCT ps ;
	 int i,j, temp;

	 static int image[256][256];
	      
     switch (message)
     {
     case WM_CREATE:
		 if((fpin=fopen("data.txt", "r"))==NULL)
		 {
			MessageBox(hwnd, "Read File open failed", "Fiel Open Error", MB_OK);
		 }

		 for(i=0; i<256; i++)
			 for(j=0; j<256; j++)
			 {
				 fscanf(fpin, "%d", &image[i][j]);
			 }

		 fclose(fpin);
		 return 0 ;
	 
     
	 case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
		  MemDC=CreateCompatibleDC(hdc);
		  MyBitmap=CreateCompatibleBitmap(hdc,256,256);//도구통 크기 
		  OldBitmap=(HBITMAP)SelectObject(MemDC, MyBitmap);

		  for(i=0; i<256; i++)
			  for(j=0; j<256; j++)
			  {
				    temp=image[i][j];
					SetPixel(MemDC,i,j,RGB(temp,temp,temp));//도트를 하나씩 그린다. 메모리에
			  }
		  
		  BitBlt(hdc, 0,0,256,256,MemDC,0,0,SRCCOPY);//출력한다
		  SelectObject(MemDC,OldBitmap);
		  DeleteObject(MyBitmap);
		  DeleteDC(MemDC);
		  EndPaint (hwnd, &ps) ;
          return 0 ;
          
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}
 switch (message)
     {
     case WM_CREATE:
		1 if((fpin=fopen("data.txt", "r"))==NULL)
		 {
			MessageBox(hwnd, "Read File open failed", "Fiel Open Error", MB_OK);
		 }

		2 for(i=0; i<256; i++)
			 for(j=0; j<256; j++)
			 {
				 fscanf(fpin, "%d", &image[i][j]);
			 }

		fclose(fpin);
		 return 0 ;
	 

메인윈도우 실행시 한번

1. data.text를 연다. data.text가 없으면 오류메시지 출력

2. image 배열에 data.text의 정수를 읽어서 넣는다.

 case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
		  MemDC=CreateCompatibleDC(hdc);
		  MyBitmap=CreateCompatibleBitmap(hdc,256,256);//도구통 크기 
		  OldBitmap=(HBITMAP)SelectObject(MemDC, MyBitmap);

		  for(i=0; i<256; i++)
			  for(j=0; j<256; j++)
			  {
				    temp=image[i][j];
					SetPixel(MemDC,i,j,RGB(temp,temp,temp));//도트를 하나씩 그린다. 메모리에
			  }
		  
		  BitBlt(hdc, 0,0,256,256,MemDC,0,0,SRCCOPY);//출력한다
		  SelectObject(MemDC,OldBitmap);
		  DeleteObject(MyBitmap);
		  DeleteDC(MemDC);
		  EndPaint (hwnd, &ps) ;
          return 0 ;
          

대부분 Bitmap에서 본 코드와 비슷하며

다른 부분은 for 문을 돌면서 MemDC 메모리 픽셀 하나하나 마다 image배열에 있는 RGB 값을 넣어준다.

 

다시 메모장을 보면 메모장에 숫자가 뭘 의미하는지 알 수 있다.

메모장에 있는 숫자는 RGB값을 뜻하는 것이다.

출력된 모습

rgb값이 모두 똑같기 때문에 흑백사진으로 출력된다. 

 

 

반응형

+ Recent posts