// Main 함수
 
#include <stdio.h>
#include <stdlib.h>
 
#include "linkedArrayList.h"
 
int main(void)
{
 // 메뉴 입력 번호 변수
 int inputNumber;
 CString str;
 // List 생성 및 초기화
 arrayList* list = new arrayList();
 list->ListInit();
 
  while(1)
  {
   printf("\n메뉴: 0-종료 1-출력 2-추가 3-검색 4-삭제\n");
   printf("입력 : " );
   scanf("%d", &inputNumber);
 
   switch (inputNumber)
   {
   case 0:
    {
     printf("종료합니다");
    delete list;
     return 0;
    }
   case 1:
    {
     list->LPrint();
     break;
    }
   case 2:
    {
    char inputString[LIST_LEN] = "";    
 
    printf("저장하려는 문자를 입력하세요\n");
    printf("입력: ");
    scanf("%s", inputString);
 
    str = inputString;
    
     list->LInsert(str);
     break;
    }
  case 3:
   {
    char inputString[LIST_LEN] = ""; 
 
    printf("검색할 문자열 입력: ");
    scanf("%s", inputString);  
    str = inputString;
 
    list->LSearch(str);    
    break;
   }
  case 4:
   {
    char inputString[LIST_LEN] = ""; 
 
    printf("삭제할 문자열 입력: ");
    scanf("%s", inputString); 
    str = inputString;
 
    list->LRemove(str);   
    break;
   }
   default:
    continue;
   }
  }
 
 return 0;
}
 
// linkedList.h 파일
#ifndef __ARRAY_LIST_H__
#define __ARRAY_LIST_H__
 
#include "atlstr.h"
 
#define TRUE 1
#define FALSE 0
#define LIST_LEN 100
 
typedef struct _STRING
{
 CString str;
 struct _STRING* next;
} STRING;
 
typedef STRING List;
 
class arrayList
{
public:
 arrayList();
 ~arrayList();
 
 void ListInit();  
 char* LPrint();
 List* LNext(List* list);
  void LInsert(CString str);
 char* LSearch(CString& searchString);
  int findSubString(CString& str, CString& findStr);
 void LRemove(CString& searchString);
 //List* nextFind(List* nextNode);
 
private:
 List * head;
 List * cur;
 List * before;
 int numOfData;
 
};
 
#endif
 
// linkedList.cpp 파일
 
 
#include <stdio.h>
#include "linkedArrayList.h"
 
arrayList::arrayList()
{
 head = NULL;
 cur = NULL;
 before = NULL;
 numOfData = 0;
}
 
arrayList::~arrayList()
{
 
}
 
void arrayList::ListInit()
{
  head = new List();
   head->next = NULL;
 head->str = "";
   cur = NULL;
   before = head; 
   before->next = NULL;
}
 
char* arrayList::LPrint()
{
 List* nextNode = head->next;
 
 while(1)
 {
  if(nextNode != NULL )
  {
   if( nextNode->next == NULL )
   {
    wprintf(_T("%s "), nextNode->str);
    return 0;
   }
   else
   {
    wprintf(_T("%s "), nextNode->str);
    nextNode = LNext(nextNode);      
   }   
  }
  else
   break;
 }
 
 return 0;
}
 
List* arrayList::LNext(List* list)
{
 List* nextNode = list->next;
 
 return nextNode;
}
 
void arrayList::LInsert(CString str)
{
 List* newNode = new List;                       // 다음번 노드를 동적할당 하고
 
 if( numOfData >= LIST_LEN)
 {
  puts("저장할 용량을 초과했습니다");
  delete newNode;
  return;
 } 
 
 newNode->next = head->next;                                  // 새로 생긴 공간의 다음에 널을 만든다
 newNode->str = str;
 head->next = newNode; 
 cur = newNode;  
 
 numOfData++;
}
 
char* arrayList::LSearch(CString& str)
{
 List* nextNode = head->next;
 int pFind;
 
 while(1)
 {
  if(nextNode != NULL)
  {
   if( nextNode->next == NULL )
   {   
    pFind = findSubString(nextNode->str, str);
 
    if( !pFind )
    {
     wprintf(_T("%s"), nextNode->str);
    }
    return 0;
   }
   else
   {
    pFind = findSubString(nextNode->str, str);    
 
    if( !pFind )
    {
     wprintf(_T("%s"), nextNode->str);
    }   
    nextNode = LNext(nextNode);
   }
  }
  else
   break;
 }
 return 0;
}
 
int arrayList::findSubString(CString& str, CString& searchStr)
{
 int ret;
 ret = str.Compare(searchStr);
 
 return ret; 
}
 
void arrayList::LRemove(CString& searchString)
{
 List* nextNode = head->next;
 int pFind;
 
 while(1)
 {
  if(nextNode != NULL)
  {
   pFind = findSubString(nextNode->str, searchString);
 
   if( !pFind )
   {
    cur = nextNode->next;
    delete nextNode;
    nextNode = NULL;
    before->next = cur;
    (numOfData)--;
    nextNode = LNext(before);
   }
   else
   {
    before = nextNode;
    nextNode = LNext(nextNode);
 
    if( nextNode == NULL)
     return;   
   }
  }
  else
   break;
  
 }
 
 return;
}
 
// List* arrayList::nextFind(List* nextNode)
// {
//  List* next = nextNode->next;
//
//  if( next != NULL)
//   return next;
//
//  return 0;
// }

'Computer Language > C|C++' 카테고리의 다른 글

리스트 구현 문제  (0) 2022.10.02
간단한 재귀 문제 풀이  (0) 2022.10.02
Struct STRING
{
        Char string[100];
        STRING* pstring;
}
 
위와 같은 구조체 사용하세요
 

포인터 배열을 사용해서, 문자열에 대해 다음과 같은 메뉴를 처리합니다.
 
     메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
 
   출력 : 지금까지 입력한 모든 문자열을 출력합니다.
   추가 : 새로운 문자열을 포인터 배열에 추가합니다.
   검색 : 검색할 문자열을 포함하고 있는 모든 문자열을 출력합니다.
   삭제 : 삭제할 문자열을 포함하고 있는 모든 문자열을 삭제합니다.
 
   이 문제는 입력할 문자열의 개수와 문자열의 길이가 정해져 있지않다는 점에서 어렵습니다.
   또한 검색과 삭제에서 완전히 똑같은 문자열이 아니라 부분 문자열을 처리하는 것도 어렵습니다.
 
   [입출력]
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 2
   단어 : bonus
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 2
   단어 : big_money
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 2
   단어 : rainbow
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 2
   단어 : handshaking
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 2
   단어 : switch_on
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 1
   bonus big_money rainbow handshaking switch_on
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 3
   단어 : on
   bonus big_money switch_on
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 4
   단어 : on
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 1
   handshaking rainbow
   메뉴 : 0. 종료  1. 출력  2. 추가  3. 검색  4. 삭제
   선택 : 0

 

 

 

 

 

Main.c 파일
 
#include <stdio.h>
#include <stdlib.h>
 
#include "linkedArrayList.h"
#include "stringData.h"
 
int main(void)
{
 // 메뉴 입력 번호 변수
 int inputNumber;
 // List 생성 및 초기화
 lList list;
 List * pString;
 char* nullString = 0;
 ListInit(&list);
 
 while(1)
 {
  printf("\n메뉴: 0-종료 1-출력 2-추가 3-검색 4-삭제\n");
  printf("입력 : " );
  scanf("%d", &inputNumber);
 
  switch (inputNumber)
  {
  case 0:
   {
    printf("종료합니다");
    return 0;
   }
  case 1:
   {
    LPrint(list.head);
    break;
   }
  case 2:
   {
    pString = makeString();
    LInsert(&list, pString);
    break;
   }
  case 3:
   {
    printf("검색할 문자열 입력: ");
    scanf("%s", &nullString);   
 
    LSearch(list.head, &nullString);
    nullString = 0;
    break;
   }
  case 4:
   {
    printf("삭제할 문자열 입력: ");
    scanf("%s", &nullString); 
 
    LRemove(&list, &nullString);
    nullString = 0;
    break;
   }
  default:
   continue;
  }
 }
 
 return 0;
}
 
LinkedArrayList.h 파일
#ifndef __ARRAY_LIST_H__
#define __ARRAY_LIST_H__
 
#include "stringData.h"
 
#define TRUE 1
#define FALSE 0
 
typedef struct _linkedList
{
 List * head;
 List * cur;
 List * before;
 int numOfData;
} linkedList;
 
typedef linkedList lList;
 
// 해당 문제의 ADT
void ListInit(lList * plist);    
// 초기화할 리스트의 주소값을 인자로 전달
// 리스트 생성 후 제일 먼저 호출되어야 하는 함수이다
char* LPrint(List *plist);
// 저장된 문자열 모두를 출력한다
void LInsert(lList * plist); //, LData data);
// 리스트에 데이터를 저장한다. 매개변수 data에 전달된 값을 저장한다
char* LSearch(List * plist, char * searchString);
char* findSubString(char* str, char* findStr); //, char* searchStr)
// 리스트에서 특정 문자열을 찾아서 찾은 문자열을 반환한다
/*int LFirst(List * plist, LData * pdata);*/
// 첫번째 데이터가 pdata가 가리키는 메모리에 저장된다
// 데이터의 참조를 위한 초기화가 진행된다
// 참조 성공시 True 실패시 False를 반환
/*int LNext(List * plist, LData * pdata);*/
// 참조된 데이터의 다음 데이터가 pdata가 가리키는 메모리에 저장된다
// 순차적인 참조를 위해서 반복 호출이 가능하다
// 참조를 새로 시작하려면 먼저 LFirst 함수를 호출한다
// 참조 성공 시 True 실패시 False를 반환
void LRemove(lList * plist, char * searchString);
List* nextFind(List* nextNode);
// LFirst 또는 LNext 함수의 마지막 반환 데이터를 삭제 한다
// 삭제된 데이터는 반환된다
// 마지막 반환 데이터를 삭제하므로 연이은 반복 호출을 허용하지 않는다
/*int LCount(List * plist);*/
// 리스트에 저장되어 있는 데이터의 수를 반환한다
 
#endif
 
stringData.h 파일
 
#ifndef __NAME_CARD_H__
#define __NAME_CARD_H__
 
#define LIST_LEN 100
 
typedef struct _STRING
{
 char stringData[LIST_LEN];
 struct _STRING* next;
} STRING;
 
typedef STRING List;
 
List* makeString();
 
#endif

 

// LinkedArrayList.c 파일
 
#include <stdio.h>
#include "linkedArrayList.h"
 
void ListInit(lList *pList)
{
 pList->head = (List *)malloc(sizeof(List));
 pList->head->next = NULL;
 memset(pList->head->stringData, 0, sizeof(pList->head->stringData));
 //pList->head->stringData[LIST_LEN] = {0};
 pList->cur = NULL;
 pList->before = pList->head; 
 pList->before->next = NULL;
 pList->numOfData = 0;
}
 
char* LPrint(List *plist)
{
 List* nextNode = plist->next;
 if(nextNode)
 {
  if( nextNode->next == NULL )
  {
   printf("%s ", nextNode->stringData);
   return 0;
  }
  else
  {
   LPrint(nextNode); 
   printf("%s ", nextNode->stringData);
  }
 }
 
 return 0;
}
 
void LInsert(lList * plist, List* pStringData)
{
 List* newNode = (List *)malloc(sizeof(List));                       // 다음번 노드를 동적할당 하고
 strcpy(newNode->stringData, pStringData->stringData);
 
 if( plist->numOfData >= LIST_LEN)
 {
  puts("저장할 용량을 초과했습니다");
  free(newNode);
  return;
 } 
 
 newNode->next = plist->head->next;                                  // 새로 생긴 공간의 다음에 널을 만든다
 plist->head->next = newNode; 
 plist->cur = pStringData;  
 
 plist->numOfData++;
}
 
char* LSearch(List * plist, char * searchString)
{
 List* nextNode = plist->next;
 char *pFind;
 
 if(nextNode)
 {
  if( nextNode->next == NULL )
  {   
   pFind = findSubString(nextNode->stringData, searchString);
 
   if( !pFind )
   {
    printf("%s", nextNode->stringData);
   }
 
   return 0;
  }
  else
  {
   LSearch(nextNode, searchString); 
 
   pFind = findSubString(nextNode->stringData, searchString);
 
   if( !pFind )
   {
    printf("%s", nextNode->stringData);
   }   
  }
 }
 
 return 0;
}
 
char* findSubString(char* str, char* searchStr)
{
 if( strstr(str, searchStr) != NULL )
  return 0;
 else
  return -1;
}
void LRemove(lList * plist, char * searchString)
{
 List* nextNode = plist->head;
 char *pFind;
 
 while(1)
 {
  //if( nextNode->stringData == NULL)
  // return;
 
  pFind = findSubString(nextNode->stringData, searchString);
 
  if( !pFind )
  {
   plist->cur = nextNode->next;
   free(nextNode);
   nextNode->stringData[0] = 0;
   plist->before->next = plist->cur;
   (plist->numOfData)--;
   nextNode = nextFind(plist->before);
  }
  else
  {
   plist->before = nextNode;
   nextNode = nextFind(nextNode);
 
   if( nextNode == NULL)
    return;   
  }
 }
 
 return;
}
 
List* nextFind(List* nextNode)
{
 List* next = nextNode->next;
 
 if( next != NULL)
  return next;
 
 return 0;
}
 
// stringData.c 파일
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include "stringData.h"
 
List * makeString()
{
 char inputString[LIST_LEN] = "";
 List * newString = (List *)malloc(sizeof(List));
 
 printf("저장하려는 문자를 입력하세요\n");
 printf("입력: ");
 scanf("%s", inputString);
 
 strcpy(newString->stringData, inputString);
 return newString;
}

 

'Computer Language > C|C++' 카테고리의 다른 글

C++ 기반  (0) 2022.10.02
간단한 재귀 문제 풀이  (0) 2022.10.02

재귀함수 문제

1. 횟수를 입력받은 다음, 입력받은 횟수만큼 "hello" 문자열을 출력하는 재귀함수를 만드세요.

[입력]

횟수 : 4

[출력]

결과 : hello hello hello hello

 

2. 대문자 A에서 Z까지 출력하는 재귀함수를 만드세요.

그리고 Z에서 A까지 거꾸로 출력하는 재귀함수도 만드세요.

[입력]

없음

[출력]

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Z Y X W V U T S R Q P O N M L K J I H G F E D C B A

 

3. 문자열을 출력하는 재귀함수를 만드세요.

이때 문자열은 scanf 함수로 입력받도록 합니다.

[입력]

문자열 : This_is_a_recursive_call.

[출력]

결과 : This_is_a_recursive_call.

 

4. 범위를 표현하는 구조체가 있습니다.

struct RANGE

{

int from, to;

};

RANGE 구조체에 입력한 범위 안에 포함된 모든 정수를 더하는 재귀함수를 만듭니다.

from과 to 멤버 또한 범위에 포함되는 것으로 처리합니다.

[입력]

범위 : 5 11

[출력]

합계 : 56

 

5. 크기가 15인 int 배열을 선언하고, 0부터 11 사이의 난수로 채웁니다.

그리고 배열 내의 위치를 가리키는 정수를 입력받아서,

자신을 포함한 인접한 한 자리 정수들을 모두 -1로 바꾸는 재귀함수를 만듭니다.

[입력]

위치 : 7

[출력]

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

=============================================

3 5 8 10 5 2 0 0 8 3 11 9 1 8 0

3 5 8 10 -1 -1 -1 -1 -1 -1 11 9 1 8 0

 

 

재귀함수 문제

1. 횟수를 입력받은 다음, 입력받은 횟수만큼 "hello" 문자열을 출력하는 재귀함수를 만드세요.

[입력]

횟수 : 4

[출력]

결과 : hello hello hello hello

 

답: Qt

 

void printHello(int n)

{

if( n == 0)

return ;

else

{

qDebug("hello" );

printHello(n-1);

}

}

 

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

{

QApplication a(argc, argv);

printHello(4);

return a.exec();

}

 

void main()

{

int count;

 

printf( "횟수 : " );

scanf( "%d", &count );

 

printf( "결과 : " ); PrintHello( count );

printf( "\n" );

}

 

void PrintHello( int count )

{

if( count > 0 )// 리턴 조건을 다름

{

printf( "hello " );

PrintHello( count-1 );

}

}

 

2. 대문자 A에서 Z까지 출력하는 재귀함수를 만드세요.

그리고 Z에서 A까지 거꾸로 출력하는 재귀함수도 만드세요.

[입력]

없음

[출력]

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Z Y X W V U T S R Q P O N M L K J I H G F E D C B A

 

void printAlfabet(int n)

{

if( n < 65 || n > 90)

return ;

else

{

qDebug("%c", n);

printHello(n+1);

}

}

 

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

QApplication a(argc, argv);

printAlfabet(65);

return a.exec();

}

 

역으로 출력하는 것은 초기값을 -1 시키면 된다.

 

void PrintUpper( char ch );

void PrintUpperRev( char ch );

 

 

void main()

{

PrintUpper( 'A' ); printf( "\n" );

PrintUpperRev( 'A' ); printf( "\n" );

}

 

void PrintUpper( char ch )

{

if( ch > 'Z' )

return;

 

printf( "%c ", ch );

PrintUpper( (char) (ch+1) );

}

 

void PrintUpperRev( char ch )

{

if( ch > 'Z' )

return;

 

PrintUpperRev( (char) (ch+1) );

printf( "%c ", ch );

}

 

3. 문자열을 출력하는 재귀함수를 만드세요.

이때 문자열은 scanf 함수로 입력받도록 합니다.

[입력]

문자열 : This_is_a_recursive_call.

[출력]

결과 : This_is_a_recursive_call.

 

void printStr(char* str, int nStart, int nEnd)

{

if(nEnd == nStart)

return

else

{

printf("%c",str[nStart]);

printStr(str, nStart+1, nEnd);

}

}

 

 

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

{

QCoreApplication a(argc, argv);

char str[20];

printf("문자열을 입력하세요. \n");

scanf("%s", str);

int len = strlen(str);

printStr(str, 0, len);

return a.exec();

}

 

#include <stdio.h>

 

void PrintString( char* str );

 

void main()

{

char str[256];

 

printf( "문자열 : " );

scanf( "%s", str );

 

printf( "결과 : " ); PrintString( str );

printf( "\n" );

}

 

void PrintString( char* str )

{

if( *str == '\0' )

return;

 

putchar( *str );

PrintString( str+1 );

}

 

4. 범위를 표현하는 구조체가 있습니다.

struct RANGE

{

int from, to;

};

RANGE 구조체에 입력한 범위 안에 포함된 모든 정수를 더하는 재귀함수를 만듭니다.

from과 to 멤버 또한 범위에 포함되는 것으로 처리합니다.

[입력]

범위 : 5 11

[출력]

합계 : 56

 

typedef struct RANGE

{

int from, to

}R

 

int printSum(int nStart, int nPlus)

{

if(nPlus == -1)

return 0;

else

{

return nStart + printSum(nStart + 1, nPlus-1);

}

}

 

 

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

{

QCoreApplication a(argc, argv);

R r

r.from = 5;

r.to = 11;

int nPlus = r.to - r.from

printf("%d", printSum(r.from, nPlus ));

return a.exec();

}

 

#include <stdio.h>

 

struct RANGE

{

int from, to;

};

 

int Summation( int from, int to );

 

void main()

{

struct RANGE range;

 

printf( "범위 : " );

scanf( "%d %d", &range.from, &range.to );

 

printf( "합계 : %d\n", Summation(range.from, range.to) );

}

 

int Summation( int from, int to )

{

int sum;

 

if( from > to )

return 0;

 

sum = from;

sum += Summation( from+1, to );

 

return sum;

}

 

 

5. 크기가 15인 int 배열을 선언하고, 0부터 11 사이의 난수로 채웁니다.

그리고 배열 내의 위치를 가리키는 정수를 입력받아서,

자신을 포함한 인접한 한 자리 정수들을 모두 -1로 바꾸는 재귀함수를 만듭니다.

[입력]

위치 : 7

[출력]

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

=============================================

3 5 8 10 5 2 0 0 8 3 11 9 1 8 0

3 5 8 10 -1 -1 -1 -1 -1 -1 11 9 1 8 0

 

void replaceFunc(int* nStart, int nSearch)

{

int* number = nStart + nSearch

if ( nSearch != 1)

{

if(*number > 9)

{

return;

}

else

{

if( *number < 10 && nSearch == -1 )

{

*number = -1;

replaceFunc(number, -1 );

}

else if( nSearch != -1 )

replaceFunc(number, -1 );

}

replaceFunc(number, 1 );

}

 

if( nSearch == 1)

{

if(*number > 9)

{

return;

}

else

{

if( *number < 10 && nSearch == 1 )

{

*number = -1;

replaceFunc(number, 1 );

}

else if( nSearch != 1)

replaceFunc(number, 1 );

}

}

}

 

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

{

QCoreApplication a(argc, argv);

int nNumber[15];

int start = 1;

for(int i = 0 ; i < 15; i++)

{

nNumber[i] = rand()%11;

printf("%d = %d \n", i,nNumber[i]);

}

replaceFunc(nNumber, 7);// 위치입력

for(int i = 0 ; i < 15; i++)

{

printf("\n%d = %d \n", i,nNumber[i]);

}

return a.exec();

}

 

 

#include <stdio.h>

#include <stdlib.h>

 

void Init( int array[], int size, int max );

void PrintRuler( int size );

void Print( int array[], int size );

void TurnUp( int array[], int size, int pos );

 

void main()

{

int array[15];

int seed, pos;

 

printf( "씨앗 : " );

scanf( "%d", &seed ); // 15가 적당

 

srand( seed );

 

printf( "위치 : " );

scanf( "%d", &pos );

 

PrintRuler( 15 );

 

Init( array, 15, 12 );

Print( array, 15 );

 

TurnUp( array, 15, pos );

Print( array, 15 );

}

 

void Init( int array[], int size, int max )

{

int i;

for( i = 0; i < size; i++ )

array[i] = rand() % max;

}

 

 

void PrintRuler( int size )

{

int i;

for( i = 0; i < size; i++ )

printf( "%2d ", i );

printf( "\n" );

 

for( i = 0; i < size; i++ )

printf( "===" );

printf( "\n" );

}

 

 

void Print( int array[], int size )

{

int i;

for( i = 0; i < size; i++ )

printf( "%2d ", array[i] );

printf( "\n" );

}

 

 

void TurnUp( int array[], int size, int pos )

{

if( pos < 0 || pos >= size )

return;

 

if( array[pos] < 0 || array[pos] >= 10 )

return;

 

array[pos] = -1;

 

TurnUp( array, size, pos-1 );

TurnUp( array, size, pos+1 );

}

 

 

void replaceFunc(int* nStart, int nSearch)

{

if( nStart[nSearch] < 0 || nStart[nSearch] >= 10)

return

nStart[nSearch] = -1;

replaceFunc(nStart, nSearch-1 );

replaceFunc(nStart, nSearch+1 );

}

 

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

{

QCoreApplication a(argc, argv);

int nNumber[15];

int start = 1;

for(int i = 0 ; i < 15; i++)

{

nNumber[i] = rand()%11;

printf("%d = %d \n", i,nNumber[i]);

}

replaceFunc(nNumber, 5);// 위치입력

for(int i = 0 ; i < 15; i++)

{

printf("%d = %d \n", i,nNumber[i]);

}

return a.exec();

}

 

수정한 코드

 

void replaceFunc(int* nStart, int nSearch)

{

//int* number = nStart + nSearch;

if(nStart[nSearch] > 9 || nStart[nSearch] < 0)

{

return

}

else

{

if( nStart[nSearch] < 10 )

{

nStart[nSearch] = -1;

replaceFunc(nStart, nSearch-1 );

replaceFunc(nStart, nSearch+1 );

}

}

// if( nSearch == 1)

// {

// if(*number > 9)

// {

// return;

// }

// else

// {

// if( *number < 10 && nSearch == 1 )

// {

// *number = -1;

// replaceFunc(number, 1 );

// }

// else if( nSearch != 1)

// replaceFunc(number, 1 );

// }

// }

}

 

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

{

QCoreApplication a(argc, argv);

int nNumber[15];

int start = 1;

for(int i = 0 ; i < 15; i++)

{

nNumber[i] = rand()%11;

printf("%d = %d \n", i,nNumber[i]);

}

replaceFunc(nNumber, 6);// 위치입력

for(int i = 0 ; i < 15; i++)

{

printf("\n%d = %d \n", i,nNumber[i]);

}

return a.exec();

}

'Computer Language > C|C++' 카테고리의 다른 글

C++ 기반  (0) 2022.10.02
리스트 구현 문제  (0) 2022.10.02

타이머 기능을 사용하게 되었다 꼭 타이머로 구현해야 하는 것은 아니지만 별도의 쓰레드로 동작하면서 특정 기능을 주기적으로 호출해야 하는 기능이 필요하게 되어 .Net에서 제공하는 타이머 기능을 조사했다 


목적 : 특정 작업을 주기적으로 실행 하기 위해 사용 


일단 동작하고 있는 프로세스 내부에서 기존의 작업과 별도로 동작해야 할 필요가 있는 경우 멀티 쓰레드 환경을 고려하게 된다 

두가지 조건을 만족해야 한다 

1. 다중 작업 처리 가능 

2. 특정 시간마다 이벤트 발생 


위 2가지를 고려하고 쓰레드 생성과 타이머 조사를 비교 했을 때 어느 것이 우위에 있는지 비교 분석을 하자면 

타이머의 장점 

 - Thread.Sleep 은 동작하는 프로세스에 인터럽트하는  강제로 프로세스를 멈추게 하는 성향이 있기 때문에 시스템 전체에 영향을 준다 (쓰레드의 단점) 

 - Thread를 만들어서 Sleep을 사용하지 않고 (Sleep을 사용하면 Timer 기능을 하는 Thread는 물론 시스템 전체가 멈춘다)

   Thread와 비교했을 때 반복(Interval) 간격을 옵션으로 간단하게 정할 수 있다 (2번 조건 만족)  

   시간 계산을 측정해서 구현할 수 있겠지만 Timer에서는 Interval을 설정만 하면 되기 때문에 사용하기 편리하다 (라이브러리 처럼 사용 가능) 

 - Timer는 Callback 개념으로 지정된 시간 후에 Callback을 하는 개념이라 상대적으로 부하가 적게 걸린다 ( 출처:http://nowonbun.tistory.com/139 )

   이 말은 정확한 증거는 아니지만 Thread에서는 Sleep()을 통해 다른 Thread로 옮겨가게 되지만 예를 들면 Sleep(10) 과 같이 작은 수치의 밀리세컨드를 지정하더라도 성능에 영향을 미치게 되지만 CallBack 함수로 호출되는 Timer의 경우는 상대적으로 부하가 적게 걸린다는 말로 보인다.  


타이머라는 것은 기본적으로 시간의 흐름을 연속적으로 측정하기 위함이기 때문에 굳이 Thread로 만들지 않더라도 .NET이 제공해주는 라이브러리 클래스를 사용하면 간단하고 편리하게 구현할 수 있다 (Thread Pool 관리도 자동으로 해준다는?? 부분이 있다)  


.NET 프레임워크에서 제공하는 타이머에는 3가지가 있다 

1. System.Windows.Forms.Timer 

2. System.Threading.Timer

3. System.Timers.Timer


MSDN + 검색한 내용의 간단한 설명이다 (출처: http://blog.daum.net/starkcb/117)


1) System.Windows.Forms.Timer

사용자가 정의한 간격마다 이벤트를 발생시키는 타이머를 구현합니다. 

이 타이머는 Windows Forms 응용 프로그램에서 사용할 수 있도록 최적화되었으며 창에서 사용해야 합니다


윈도우 폼 타이머라고 한다 윈도우 응용프로그램과 동일한 Thread에서 동작한다

즉, Timer는 UI Thread 상에서 동작한다 - 즉 Mutil Thread 환경이 아니라 단일 Thread 환경이다

반복주기를 설정하고 반복 주기마다 실행되는 이벤트를 등록한다


2) System.Threading.Timer


지정된 간격으로 메서드를 실행하는 메커니즘을 제공합니다


쓰레드 타이머라고 한다

UI Thread와 Work Thread로 나뉘어 진다 

UI Thread - 평소에는 유후 상태, 기다리다가 메시지 들어오면 메시지 루프에서 메시지 처리 (MFC가 기억난다...) 

Work Thread - background 작업, 메시지 루프는 사용하지 않는다 

멀티쓰레드로 두 쓰레드는 독립적으로 수행된다 

닷넷의 ThreadPool에서 관리한다 

이벤트 대신 Callback 매서드를 사용한다 

만약 반복 실행 작업이 UI Thread와 관련이 생기면 Cross Thread 문제가 발생한다 (Invoke, BeginInvoke를 통해 핸들링할 수 있다) 

메시지가 스레드에서 펌프되지 않을 경우에 유용

- 이 말은 Windows Form Timer의 경우 Message Loop에서 UI의 특정 이벤트에 반응하는 방식이 아니라 UI Thread와는 별개로 Background로 작업이 진행 될 때 즉 Work Thread로 사용될 때 유용하다는 말이다

  

3) System.Timers.Timer


응용 프로그램에 되풀이 이벤트를 생성합니다


서버타이머라고 한다 

Synchronizing Object 속성에 따라 달라진다 

Synchronizing Object 속성을 form 객체로 하면 UI Thread 상에서 동작 

Synchronizing Object 속성을 지정하지 않으면 Work Thread 상에서 동작 

Multi Thread 환경에서 Work Thread를 구현해서 사용 

시스템 시간(서버)을 사용해서 프로그램 리소스(시간)를 사용하는 System.Thrading.Timer 보다 정확

Thread 사이를 이동하면서 발생한 이벤트를 처리 가능 

주기마다 실행되는 이벤트를 등록하고 쓰레드 타이머와 마찬가지로 UI Thread를 핸들링 하기위해서는 Invoke, BeginInvoke를 이용해야 한다 

코드형태는 윈도우 폼 타이머와 비슷하다 


.NET의 타이머를 보다 보면 UI Thread와 Work Thread 간 크로싱 에러나 별도의 동작하는 Work Thread 얘기가 나오는데 간단히 생각하면 싱글 쓰레드인지 멀티 쓰레드인지 생각하면 된다 UI Thread에서만 동작하길 원하면 Windows.Forms.Timer를 사용하면 되고 멀티쓰레드 환경에서 독립적으로 동작하길 원한다면 System.Threading.Timer나 System.Timers.Timer 를 사용하면 된다 더 자세한 내용은 아래서 이어 가겠다 


두가지 측면에서 더 살펴보자면 

1. 사용법상의 차이점 

3가지 방법의 간단한 사용법을 쓰면 


1. System.Windows.Forms.Timer 사용법

- 객체 생성

System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

- 반복 주기 및 작업 설정

timer.Interval = 1000; //주기 설정

timer.Tick += new EventHandler(timer_Tick); //주기마다 실행되는 이벤트 등록

void tmrWindowsFormsTimer_Tick(object sender, System.EventArgs e)
{
      //수행해야할 작업

}

- Timer 시작

timer.Enable = true 또는 timer.Start();

- Timer 중지

timer.Enable = false 또는 timer.Stop();

 

2. System.Threading.Timer 사용법

- 객체 생성

Timer 객체를 생성할 때, 반복적으로 실행하게 될 메서드를 콜백 메서드로 등록

System.Threading.Timer timer = new System.Threading.Timer(CallBack);

- 반복 주기 및 작업 설정

이 Timer 에는 Change 메서드가 있는데, 이 메서드는 dueTime과 period 를 입력받습니다

dueTime은 Timer 가 시작하기 전 대기(지연)시간이며 period는 반복 주기입니다
timer.Change(0, 1000);

그리고 반복 실행 작업이, 
윈도우 응용프로그램의 UI Thread와 연관된다면, Cross Thread 문제가 발생하기 때문에 Invoke나 BeginInvoke를

통해 핸들링 해야 합니다.

앞서, Timer 객세 생성시 등록한 콜백 메서드에서 BeginInvoke를 통해 UI 쓰레드를 핸들링 할 수 있습니다

 

delegate void TimerEventFiredDelegate();

void CallBack(Object state)
{
    BeginInvoke(new TimerEventFiredDelegate(Work));
}
        
private void Work()
{

     //수행해야할 작업(UI Thread 핸들링 가능)
}

 

- Timer 시작

위의 Change 메서드의 dueTime 이 0 이므로 그 즉시 시작된다. Start와 같은 별도의 시작 명령이 존재하지 않음

- Timer 중지
timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);

dueTime와 period 를 무한대로 잡아서 Timer 가 실행되지 않도록 하는 것이 중지하는 것과 같습니다


3. System.Timers.Timer 사용법

- 객체 생성

System.Timers.Timer timer = new System.Timers.Timer();

 

- 반복 주기 및 작업 설정
timer.Interval = 1000;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);  //주기마다 실행되는 이벤트 등록


이 Timer 역시 UI Thread를 핸들링 하기 위해서 Invoke 나 BeginInvoke를 이용해야 합니다
delegate void TimerEventFiredDelegate();
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    BeginInvoke(new TimerEventFiredDelegate(Work));            
}

private void Work()
{

     //수행해야할 작업(UI Thread 핸들링 가능)
}

 

- Timer 시작

timer.Enable = true 또는 timer.Start();

- Timer 중지

timer.Enable = false 또는 timer.Stop();


Windows.Forms.Timer 를 제외하고는 UI Thread에서 만들어진 컨트롤에 접근하려면 

Cross Thread 문제가 있으므로 마샬링?? 된 호출(Invoke / BeginInvoke)를 이용해야 하는 차이점이 있다고 한다??


2. 수행되는 Thread환경의 차이점 

먼저 UI Thread 라는 것의 환경에 대해 알아야 한다. 윈도우 응용프로그램 중 예를 들면 Button이나 ListView나 ComboBox 등 각종 컨트롤이 생성되고 핸들링 되는 것은 UI Thread 안에서 동작하게 된다 


이와 별도로 Work Thread라는 것이 있는데 기본(Default Thread) 이외에 개발자가 별도의 쓰레드를 생성해서 작업을 실행하면 이것을 Work Thread라고 한다. 또한 UI Thread 입장에서는 .NET의 Thread Pool에 의해 실행되는 쓰레드도 Work Thread로 볼 수 있다고 한다 

쓰레드가 다르면 쓰레드의 고유번호가 다르기 때문에 

System.Threading.Thread.CurrentThread.IsThreadPoolThread 속성은 현재 쓰레드의 고유 식별자 값을 가져온다고 한다 


1. System.Windows.Forms.Timer 의 쓰레드 환경 

- 기본 쓰레드의 고유 번호를 확인한다

윈도우응용프로그램 생성자나 기타 이벤트에서 아래 코드를 기입합니다

MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

 

- Timer 쓰레드의 고유번호를 확인한다

Timer 의 Tick 이벤트에서 다음의 코드를 기입합니다

void timer1_Tick(object sender, EventArgs e)
{
     MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

     //수행해야할 작업
}


결과적으로 두 쓰레드는 동일한 고유번호를 반환하게 된다고 한다 이런 이유에서 윈도우 응용프로그램의 기본 쓰레드인 UI Thread 안에서 Timre가 동작한다고 짐작할 수있다 즉 멀티 쓰레드 환경이 아닌 싱글 쓰레드라고 생각되는 점이다... 

그러면 궁금한점... 싱글 쓰레드면 왜 Windows.Forms.Timer를 쓰는 것인지?? 이해가 어렵다.. Timer는 기본 적으로 특정 작업에 특화되려면 Background에서 동작하는게 필수라고 생각했는데 왜 Single Thread로 만들었는지 의외였고 UI 단일 Thread에서는 Timer가 Message Loop에서 동작했다 아마도 UI에서만 Thread 변경없이 Message(화면 Event)가 들어오면 반응하도록 설계하지 않았을가 싶다. 


2. System.Threading.Timer의 쓰레드 환경

- 기본 쓰레드의 고유 번호를 확인한다

윈도우응용프로그램 생성자나 기타 이벤트에서 아래 코드를 기입합니다

MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

 

- Timer 쓰레드의 고유번호를 확인한다

CallBack 메서드에서 다음과 같이 코드를 기입합니다

void CallBack(Object state)
{
      MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

      BeginInvoke(new TimerEventFiredDelegate(Work));
}


이 경우는 서로 다른 쓰레드 번호를 반환한다 즉 UI Thread와 Work Thread가 분리되어 있다 

이 두 쓰레드는 독립적으로 수행되기 때문에 멀티 스레드의 환경을 가진다 앞의 Forms.Timer 객체와는 달리 Callback 매서드에서 시간이 오래 걸리는 작업을 수행해도 프로그램이 대기상태에 빠지지 않는다 

참고로 이 Timer는 닷넷의 Thread Pool 에서 관리한다 


3. System.Timers.Timer 의 쓰레드 환경 

위에서 한번 적었지만 이 Timer는 UI Thread에서 수행될 수도 있고 Work Thread에서 수행 될 수도 있다고 한다 Synchronizing Object 속성을 Form으로 하면 UI 이고 지정하지 않으면 Work 이다  


아래와 같이 SynchronizingObject 속성의 설정 여부에 따른 ManagedThreadid 값을 확인해 보기 바랍니다

timer.SynchronizingObject = this;

 

타이머 쓰레드의 고유번호를 알기 위해 Elapsed 이벤트에

MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

를 확인해 보세요


중요한 것은 Timer Thread를 어디에서 동작할 것인가 이다 그리고 Work Thread에서 UI Thread 전환 시 크로스 쓰레드 문제가 발생하기 때문에 Invoke와 같은 코드로 극복이 가능하나 Invoke는 강제 동기화 이기 때문에 시스템 저하를 가져올 수 있다 결국 UI Thread에서는 Forms.Thread를 사용해야 Invoke로 인한 성능저하가 없다는 것이다. 

그렇다면? 멀티 쓰레드로 동작하지 않는 것과 Invoke를 사용해서 강제 동기화를 하면서 다중 작업처리를 하는 것의 차이는 시스템이 복잡할 경우 테스트나 비교를 정확히 해봐야 알 것 같다 

 

2번 Threading.Timer와 Timers.Timer의 공통점은 멀티 쓰레드로 동작할 수 있다는 것이다 

그렇다면 차이점은 무엇일까? 

일단 Threading.Timer는 Work Thread에서만 동작이 가능하지만 Timers.Timer는 UI or Work 모두가 가능하다 

그리고 Therading.Timer는 프로그램 리소스 즉 프로세스 자체에 인터럽트가 걸리면 같이 멈추거나 느려지지만 Timers.Timer는 시스템 시간(OS??)에 영향을 받기 때문에 프로그램이 느려져도 TImer 스레드는 시간에 맞추어 실행이 된다 


하나의 예를 들어보자면 이 글작성 당시 듀랑고라는 넥슨 게임이 오픈 초기라 대기열에 따른 부하에 의해 게임 시작이 어렵다 즉 app 자체가 느려짐에 따라 다른 기능들도 모두 영향을 받는다면 타이머도 같이 느려져야 하는지 아니면 타이머는 정상적으로 체크를 해야하는지에 대한 고민이 생긴다. 

일단 나의 결론은 타이머 성격의 작업은 리얼타임의 일정한 시간에 이루어 져야 한다는 것이다. 만약 대기에 의해 서버를 늘리거나 외부 환경이 변경되었을 때 그것을 타이머로 체크한다면 app 자체의 시간이나 속도와는 상관없이 변경된 환경을 체크해야 할 것이다. 이런 경우라면 Threading.Timer가 아니라 Timers.Timer를 적용해서 작업자체에 대한 주기성을 지키는 것이 더 빨리 대처하는 타이머라고 생각된다. 


----------------------------------------------------------------------------------------------------------------- 참고 자료 (http://blog.daum.net/starkcb/117)

타 차이점 및 요약, 참조

아래 표는 msdn magazine에 소개된 세 Timer 의 차이점에 대한 표입니다

우리가 알아 본 내용 이외에도, 쓰레드 안정성(동기화 문제)에 대한 내용도 있습니다

 

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 

System.Windows.Forms

System.Timers

System.Threading

Timer event runs on what thread?

UI thread

UI or worker thread

Worker thread

Instances are thread safe?

No

Yes

No

Familiar/intuitive object model?

Yes

Yes

No

Requires Windows Forms?

Yes

No

No

Metronome-quality beat?

No

Yes*

Yes*

Timer event supports state object?

No

No

Yes

Initial timer event can be scheduled?

No

No

Yes

Class supports inheritance?

Yes

Yes

No

* Depending on the availability of system resources (for example, worker threads)



마지막으로 MSDN에 대한 내용도 같이 옮긴다 

서버 타이머, Windows 타이머 및 스레드 타이머

Visual Studio 및 .NET Framework에는 세 개의 타이머 컨트롤 즉, 도구 상자의 구성 요소 탭에서 볼 수 있는 서버 기반 타이머, 도구 상자의 Windows Forms 탭에서 볼 수 있는 표준 Windows 기반 타이머 및 프로그래밍 방식으로만 사용할 수 있는 스레드 타이머가 있습니다. 

Windows 기반 타이머는 Visual Basic 1.0 이상의 버전에 있으며 지금까지 크게 변경되지 않았습니다. 
이 타이머는 Windows Forms 응용 프로그램에서 사용하도록 최적화되어 있습니다
서버 기반 타이머는 일반 타이머를 서버 환경에서 최적으로 실행되도록 업데이트한 것입니다. 


스레드 타이머는 이벤트 대신 콜백 메서드를 사용하는 간단한 소형 타이머로서 스레드 풀 스레드에서 제공합니다.

Win32 아키텍처에는 UI 스레드와 작업자 스레드라는 두 종류의 스레드가 있습니다. 
UI 스레드는 대부분의 시간을 유휴 상태로 보내며 메시지 루프에 메시지가 도착할 때까지 기다립니다. 메시지가 도착하면 
이 메시지를 처리하고 다음 메시지가 도착할 때까지 기다립니다. 이에 비해 작업자 스레드는 백그라운드 처리를 수행하는 데 사용하며 메시지 루프를 사용하지 않습니다. 

Windows 타이머와 서버 기반 타이머는 모두 Interval 속성을 사용하여 실행됩니다. 
스레드 타이머의 간격은 Timer 생성자에서 설정됩니다. 



스레드에서 타이머를 다루는 방식을 보면 알 수 있듯이 각 타이머의 용도는 서로 다릅니다.

  • Windows 타이머는 UI 스레드가 프로세싱을 수행하는 데 사용하는 단일 스레드 환경을 위해 설계되었습니다. Windows 타이머의 정확도는 55밀리초로 제한되어 있습니다. 이 일반 타이머는 사용자 코드에서 사용할 수 있는 
    UI 메시지 펌프가 필요하며 항상 동일한 스레드에서 실행되거나 다른 스레드로 마샬링됩니다. 
    이 기능은 COM 구성 요소의 성능을 저하시킵니다. 

  • 서버 기반 타이머는 다중 스레드 환경에서 작업자 스레드와 함께 사용하도록 설계되었습니다. 두 스레드는 서로 다른 아키텍처를 사용하므로 서버 기반 타이머가 Windows 타이머보다 정확합니다. 
    서버 타이머는 스레드 사이를 이동하면서 발생한 이벤트를 처리할 수 있습니다. 

  • 스레드 타이머는 메시지가 스레드에서 펌프되지 않는 경우에 유용합니다. 
    예를 들어, Windows 기반 타이머는 운영 체제의 타이머 지원 기능에 의존하며 드에서 메시지를 펌프하지 않을 경우에는 타이머 관련 이벤트가 발생하지 않습니다. 이 경우에는 스레드 타이머가 보다 더 유용합니다.

Windows 타이머는 System.Windows.Forms 네임스페이스에, 서버 타이머는 System.Timers 네임스페이스에 그리고 스레드 타이머는 System.Threading 네임스페이스에 있습니다.


추가로 몇몇 자료들을 더 보면 Threading.Timer 보다 Timers.Timer가 더 Thread Safe 하다고 되어 있다 

그 이유는 SynchronizingObject 를 통해 Thread를 제어할 수 있는것이 더 safe 해서 인지 짐작만 된다 


추가로 더 조사한 내용을 올리면 아래 링크에서  

https://stackoverflow.com/questions/1416803/system-timers-timer-vs-system-threading-timer


CLR Via C#", Jeff Ritcher 는 그의 저서에서 System.Timers.Timer의 경우 비추천했다는 말이 나온다 UI와 연관되어 사용하는 것이라는 이유라던데 사무실에 있는 책같은데 한번 확인해 봐야겠다 그리고 추가적으로 .Net Core에서는 사라졌기 때문에 Work Thread로 사용하려면 결국 System.Threading.Timer를 사용해야 한다는 이유가 된다. 위의 Thread Safe 하다는 증거를 찾지 못한다면 System.Timers.Timer 의 장점은 사라져 보인다.    


추가로 .Net 타이머에 대해 비교한 내용이다 한번 읽어 보자 

https://web.archive.org/web/20150329101415/https://msdn.microsoft.com/en-us/magazine/cc164015.aspx


http://robertgreiner.com/2010/06/using-stopwatches-and-timers-in-net/



'Computer Language > C#|.Net' 카테고리의 다른 글

이스케이프 문자 처리 법  (0) 2018.01.06
Dynamic, Template, Generic or Interface  (0) 2017.10.18

별건 아니다 

이스케이프 문자를 처리하는 방법 중에 간단한 법이 있는데 간단히 말하면 ignore 시키는 것이다 


//  \\ : 백슬래시(\) 기초 하나를 표현 

// 한글입력기에서는 : \ 이지만 영문입력기에서는 슬래시(/)의 반대 

Console.WriteLine(" \\ : 백슬래시 표현 ");

      

// 백슬래시 다음에 나오는 문자는 이스케이프 문자로 본다.

// 그래서 두개를 붙여서 \를 표현한다 

Console.WriteLine(" C:\\Home\\MyRoom\\Default.cs");

      

// @를 붙이면 자동으로 이스케이프 문자를 무시한다 그래서 원래 쓰던 방식으로 써도 문제가 없다 

// 파이썬에서는 정규식을 쓸때 r을 붙여서 이스케이프를 무시한다 패턴에 /를 사용하는데 이스케이프 때문에 //를 매번 붙이면 복잡해진다

// r은 Row String이라는 규칙을 말한다. 

// 닷넷의 정규식에도 Ignore Escape 가 있다 

// 어쨌든 특정 문자 하나를 처리하기 위해 Escape On/Off 방식으로 처리한다는 점이 중요하다

Console.WriteLine(@"C:\Home\MyRoom\Default.cs");


      

// 문자열 앞에 @ 기호를 붙이면 문자열 자체로 본다.

// 그래서 아래와 같이 공백이 붙으면 공백도 문자의 일부로 보고 처리하니 아래와 같은 점은 주의하자

Console.WriteLine(

  @" 

       C:\Home\MyRoom\Default.cs

   ");


간단한거 하나 남기려 해도 쉽지않다... 블로그에 올리는 방식을 좀 고민해 봐야겠다  

초안만 작성된게 수십장인데.. ㅋㅋ 

어쨌든.. 결과는 아래와 같이 나온다 


'Computer Language > C#|.Net' 카테고리의 다른 글

.Net Timer Class 비교(3가지) 1  (0) 2018.01.29
Dynamic, Template, Generic or Interface  (0) 2017.10.18

Dapper 사용 중에 발생한 의문

세가지(, Dynamic은 키워드로 사용)

 

Dynamic Object var는 뭔가 공통점이 있어서 따로 검색

http://blog.naver.com/PostView.nhn?blogId=dotnethelper&logNo=60178694913 (var의 타입변경이 안된다고 적혀있음)

http://www.dotnettricks.com/learn/csharp/differences-between-object-var-and-dynamic-type

https://stackoverflow.com/questions/21080346/difference-between-object-dynamic-and-var

 

dynamic != object 이다

object를 이해하려면 unboxing/boxing도 이해해야 함


하나 의문점 

- var 는 선언 시점에 타입을 정한다고 하고 정해진 이후에는 타입을 변할 수 없다고 해서 형변환이 안되는가 싶어 테스트 해봤는데 형 변환이 됨

 왜 그런 말이 적혀있는지는 모르겠네..

 

어쨌든 공통점은 유연한 프로그래밍?? 

이런것이 가능하게 설계된 키워드들이라는 것

바인딩이 느슨하다고 생각하기도 싶지만 var는 컴파일 시점에 이미 타입을 확인하므로 이런점은 다르다

 

그러면 <T> <dynamic> 이것은 무엇인가?? 

그리고 결과적으로 보면 인터페이스를 사용하는 경우와 Dynamic을 사용하는 경우가 겹칠 수 있다

이유: 상속관계가 아니지만 dynamic을 사용해서 상속된 객체처럼 사용할 수는 있다 (근데 비추다 명확하지가 않을 것 같다)


어쨌든 <T> interface를 사용해서 해당 타입에 대해 사용을 하는 것이라고 볼 수 있고

<dynamic> 도 컴파일 때 타입확인이 아니라 실행 때 확인하며 유연한 제너릭을 제공??? 하는 느낌이다


그렇다면 모든 객체를 받아 올 때 <dynamic>은 만능인가? 끝이 없다.. 

여기서 일단 정리 

- 두개가 공통점이 많으면서 dynamic이 더 한계가 없는(더더 유연한) 느낌이다 

 

'Computer Language > C#|.Net' 카테고리의 다른 글

.Net Timer Class 비교(3가지) 1  (0) 2018.01.29
이스케이프 문자 처리 법  (0) 2018.01.06

+ Recent posts