길이가 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;
}
#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;
}
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을 타임 슬라이스마다 번갈아가면서 할당하기 때문에 무한루프로 돌아도 다른 명령을 잘 받는다.