자료형

- 기초 자료형 int, double 이런거

- 파생 자료형 포인터, 구조체

- 사용자 정의 자료형 typedef enum

 

구조체가 필요한 이유?

서로 다른 자료형의 개별변수를 묶는 방법이 바로 구조체임

 

구조체와 배열의 다른점은?

배열은 같은 자료형이고

구조체는 서로 다른형을 묶을수있음

 

구조체 선언

 

구조체 선언은 변수 선언은 아님

구조체를 정의하는 것은 와플이나 붕어빵을 만드는 틀을 정의하는것임. 

와플이나 붕어빵을 실제로 만들기위해서는 구조체 변수를 선언해야됌.

 

구조체 선언들

 

// x값과 y값으로 이루어지는 화면의 좌표

struct point {

  int x;  // x 좌표

  int y;  // y 좌표

};

// 복소수

struct complex {

  double real;  // 실수부

  double imag;  // 허수부

};

// 날짜

struct date {

  int month; 

  int day;

  int year;

};

// 사각형

struct rect {

  int x;

  int y;

  int width;

  int length;

};

// 직원

struct employee {

  char name[20];  // 이름

  int age;  // 나이

  int gender;  // 성별

  int salary;  // 월급

};

 

구조체 정의와 구조체 변수 선언

 

중괄호를 이용하여 초기값을 나열함

struct student {
	int number;
    char name[10];
    double grade;

}
struct student s1 = {24, "kim", 4.3};

 

구조체에서 멤버를 접근할때에는 . 을 사용하여서 접근함

struct student {
	int number;
    char name[10];
    double grade;
};

int main(void)
{
	struct student s; // 구조체 변수 선언
    
    s.number = 201910794;
    strcpy(s.name, "홍길동");
    s.grade = 4.3;
    
    printf("학번: %d\n", number);
    printf("이름: $c\n", name);
    pirntf("학점": %f\n", grade);
    
    return 0;
}

출력결과

학번: 201910794

이름: 홍길동

학점: 4.3

 

구조체를 멤버로 가지는 구조체

 

struct date {
	int year:
    int month;
    int day;
}

struct student {
	int number;
    char name[10];
    struct date dob; //구조체 안에 구조체 포함
    double grade;
};

struct student s1;

s1.dob.year = 1983;
s1.dob.month =3;
s1.dob.day = 29;

 

같은 구조체 변수끼리 대입은 가능하지만 비교는 불가능

struct point {
	int x;
	int y;
};

int main(void)
{
	struct point p1 = {10, 20};
	struct point p2 = {30, 40};

	p2 = p1;			// 대입 가능(메모리 복사)

	if( p1 == p2 )			// 비교 -> 컴파일 오류!!( 변수 비교)
		printf("p1와 p2이 같습니다.")

	if( (p1.x == p2.x) && (p1.y == p2.y) )	// 올바른 비교( 멤버 비교)
		printf("p1와 p2이 같습니다.")
}

변수의 멤버까지 들어가서 비교를 해야됌

 

구조체 배열이란?

같은 구조체를 여러개 모은것

 

struct student {
	int number;
	char name[10];
	double grade;
}; 

int main(void)
{
	struct student list[100];	// 구조체의 배열 선언


	list[2].number = 24;
	strcpy(list[2].name, "홍길동");
	list[2].grade = 4.3;
}

 

구조체 배열의 초기화

struct student list[3] = {
	{ 1, "Park", 3.42 },
	{ 2, "Kim", 4.31 },
	{ 3, "Lee", 2.98 }
};

 

이런식으로 구조체 배열에 입력값을 넣어줄수있음

#define SIZE 3

struct student {
	int number;
	char name[20];
	double grade;
}; 
int main(void)
{
	struct student list[SIZE];
	int i;

	for(i = 0; i < SIZE; i++)
	{
		printf("학번을 입력하시오: ");
		scanf("%d", &list[i].number);
		printf("이름을 입력하시오: ");
		scanf("%s", list[i].name);
		printf("학점을 입력하시오(실수): ");
		scanf("%lf", &list[i].grade);
	}

	for(i = 0; i< SIZE; i++)
	       printf("학번: %d, 이름: %s,  학점: %f\n", list[i].number, list[i].name, list[i].grade);
	return 0;
}

 

구조체와 포인터

 

구조체를 가리키는 포인터

struct student s = {24, "kim", 4.3};
struct student *p;

p = &s;

printf("학번=%d 이름=%s 학점=%f \n", s.number, s.name, s.grade);
printf("학번=%d 이름=%s 학점=%f \n", (*p).number, (*p).name, (*p).grade);
printf("학번=%d 이름=%s 학점=%f \n", p->number, p->name, p->grade); // ->연산자는 구조체 포인터로 구조체 멤버를 접근할때 사용

 

-> 연산자

(*p).number = p가 가리키는 구조체 변수의 멤버 number

p -> number = p가 가리키는 구조체 변수의 멤버 number

 

// 포인터를 통한 구조체 참조
#include <stdio.h>

struct student {
	int number;
	char name[20];
	double grade;
}; 

int main(void)
{
	struct student s = { 20070001, "홍길동", 4.3 };
	struct student *p;

	p = &s;

	printf("학번=%d 이름=%s 학점=%f \n", s.number, s.name, s.grade);
	printf("학번=%d 이름=%s 학점=%f \n", (*p).number,(*p).name,(*p).grade);
	printf("학번=%d 이름=%s 학점=%f \n", p->number, p->name, p->grade);

	return 0;
}

결과는 모두 다 동일하게 나옴

 

포인터를 멤버로 가지는 구조체

struct date {
	int month;
	int day;
	int year;
};

struct student {
	int number;
	char name[20];
	double grade;
	struct date *dob; //포인터를 멤버로 가짐
};

int main(void)
{
	struct date d = { 3, 20, 1990 };
	struct student s = { 20190001, "Kim", 4.3 };

	s.dob = &d;

	printf("학번: %d\n", s.number);
	printf("이름: %s\n", s.name);
	printf("학점: %f\n", s.grade);
	printf("생년월일: %d년 %d월 %d일\n", s.dob->year, s.dob->month, s.dob->day);

	return 0;
}

 

구조체 변수를 함수의 인수로 전달하는 경우에는 구조체의 복사본이 함수로 전달됨

따라서, 함수에서 새로운 구조체 변수가 만들어져서 구조체 멤버가 복사되는거임

구조체 크기가 크면 그만큼 시간과 메모리가 소요됨.

 

구조체 포인터를 함수의 인수로 전달하는 경우에는 구조체의 주소가 전달됨

시간과 공간 절약할수있지만 원본 훼손의 가능성이 있음

 

구조체를 반환하는 경우

struct student create() {
	struct student s;
	s.number = 3;
	strcpy(s.name, “park”);
	s.grade = 5.0;
	return s;
}
int main(void) {
	struct student a;
	a= create();

	return 0;
}

이렇게 하면 복사본이 반환됨(구조체 변수 s가 변수 a로 복사)

 

void create(struct student *p) {
	p->number = 3;
	strcpy(p->name, “park”);
	p->grade = 5.0;
}
int main(void) {
	struct student a;
	create(&a);

	return 0;
}

이렇게하면 주소가 반환됨(구조체 포인터 변수 p를 사용하여 변수 a값 변경)

 

공용체 - 같은 메모리 영역을 여러개의 변수가 공유, 공용체를 선언하고 사용하는 방법은 구조체와 비슷

union example {
	char c;	// 같은 공간 공유
	int i;		// 같은 공간 공유
};

공용체의 메모리 크기는 멤버중 가장 큰 크기

 

#include <stdio.h>

union example {
	int i;
	char c;
};

int main(void)
{
	union example v;

	v.c = 'A';
	printf("v.c:%c   v.i:%i\n", v.c, v.i );

	v.i = 10000;
	printf("v.c:%c   v.i:%i\n", v.c, v.i);

}

 

이렇게하면 출력값은

v.c:A v.i:-858993599

v.c:† v.i:10000

이렇게 나오게되는데 왜 두번째 값의 결과가 저렇게 나올까하고 찾아보니

공용체의 특성 때문임

공용체는 여러멤버가 같은 메모리 위치를 공유하게되는데 이는 모든 멤버가 메모리의 시작 위치를 공유한다는 의미.

따라서 하나의 멤버에 값을 할당하면 다른 멤버들도 같은 메모리를 바라보게됨.

 

공용체에 대한 타입 필드 사용

 

#include <stdio.h>
#include <string.h>
#define STU_NUMBER 1
#define REG_NUMBER 2

struct student {
	int type;
	union {
		int stu_number;		// 학번
		char reg_number[15];	// 주민등록번호
	} id;
	char name[20];
};

void print(struct student s)
{
	switch(s.type)
	{
		case STU_NUMBER:
			printf("학번 %d\n", s.id.stu_number);
			printf("이름: %s\n", s.name);
			break;
		case REG_NUMBER:
			printf("주민등록번호: %s\n", s.id.reg_number);
			printf("이름: %s\n", s.name);
			break;
		default:
			printf("타입오류\n");
			break;
	}
}

공용체는 저 두개를 다 사용하지않을때만 사용해야됌 만약 두개를 다 사용해야한다하면 공용체를 쓰면안댐

 

#define STU_NUMBER 1
#define REG_NUMBER 2

struct student {
	int type;
	union {
		int stu_number;			
        char reg_number[15];	
	} id;
	char name[20];
};

int main(void)
{
	struct student s1, s2;

	s1.type = STU_NUMBER;
	s1.id.stu_number = 20190001;
	strcpy(s1.name, "홍길동");

	s2.type = REG_NUMBER;
	strcpy(s2.id.reg_number, "860101-1056076");
	strcpy(s2.name, "김철수");

	print(s1);
	print(s2);
}

공용체는 이렇게 타입별로 나눠서 쓸수있음. 단 두개를 동시에쓰면안댐

 

 

열거형 - 변수가 가질 수 있는 값들을 미리 열거해 놓은 자료형

예시) 요일을 저장하고있는 변수는 {일요일, 월요일, 화요일, 수요일, 목요일, 금요일, 토요일}중의 하나의 값만

가질 수 있다

 

열거형의 선언

enum days {SUN, MON, TUE, WED, THU, FRI, SAT};

열거형의 변수 선언

enum days today;

today = SUN;

열거형 안에있는 기호상수값만 사용할수있어서 프로그램의 오류를 막아줌

 

열거형이 필요한이유?

- 오류를 줄이고 가독성을 높힘, 기호상수라 의미를 좀 더 쉽게 알수있기때문

 

열거형의 초기화

enum days { SUN, MON, TUE, WED, THU, FRI, SAT };	// SUN=0, MON=1, ...
enum days { SUN=1, MON, TUE, WED, THU, FRI, SAT };	// SUN=1, MON=2, ...
enum days { SUN=7, MON=1, TUE, WED, THU, FRI, SAT=6 };// SUN=7, MON=1, ...

만약 값을 지정하지 않으면 0부터 시작함

 

#include <stdio.h>
enum days { SUN, MON, TUE, WED, THU, FRI, SAT };
char *days_name[] = { 
"sunday", "monday", "tuesday", "wednesday", "thursday", "friday",
"saturday" };

int main(void)
{
	enum days d;
	d = WED;
	printf("%d번째 요일은 %s입니다\n", d, days_name[d]);
	return 0;
}

출력값:

3번째 요일은 wednesday입니다

기호상수값이 출력되는게아님 프로그램 내에서 컴파일에서 3을 대표하는 값으로 사용하고있는것임

 

정수형: 사람이 기억하기 어려움

기호 상수: 오타날수있음

열거형: 컴파일러가 열거형 멤버 이외의 것을 방지함

 

typedef의 개념

- typedef는 기본 자료형에 새로운 자료형을 추가한것

typedef unsigned char BYTE;
BYTE index;		// unsigned char index;와 같다.
  
typedef int INT32;
typedef unsigned int UINT32;
  
INT32 i;		// int i;와 같다.
UINT32 k;		// unsigned int k;와 같다.

 

구조체로 새로운 타입을 정의할 수 있음

struct point {
	int x;
	int y;
};
typedef struct point POINT;
POINT a, b;

 

typedef float VECTOR[2];

- VECTOR은 실수 2개로 이루어진 1차원 배열

typedef int MATRIX[10][10];

- MATRIX는 정수 100개로 이루어진 2차원 배열

 

typedef와 #define 비교

typedef쓰는이유?

이식성(오류가 안남)을 높혀줌.

코드를 컴퓨터 하드웨어에 독립적으로 만들수있음

- int형은 2바이트이기도 하고 4바이트,  int형 대신에 typedef을 이용한 INT32 INT16을 사용하게 되면 확실하게 2바이트인지 4바이트인지를 지정할 수 있다.

 

포인터의 활용

 

이중포인터: 포인터를 가리키는 포인터

int i = 10; // i는 int형 변수
int *p = &i; // p는 i를 카리키는 포인터
int **q = &p; // q는 포인터p를 가리키는 이중 포인터

 

 

이중포인터의 해석

*q는 p와 동일함

*p, **q는 i와 동일함

 

// 이중 포인터 프로그램
#include <stdio.h>

int main(void)
{
	int i = 100;
	int *p = &i;
	int **q = &p;

	*p = 200;
	printf("i=%d\n", i);

	**q = 300;
	printf("i=%d\n", i);

	return 0;
}

출력값: 

i=200

i=300

 

#include <stdio.h>

void set_pointer(char **q);
int main(void)
{
	char *p;
	set_pointer(&p);

  printf("오늘의 격언: %s \n", p);
	return 0;
}

void set_pointer(char **q)
{
	*q = "All that glisters is not gold.";
}

**q = &p

*q와 p가 같음

따라서 출력값은 

오늘의 격언: All that glisters is not gold

 

포인터 배열이란? 포인터를 모아서 배열로 만든것

int *ap[10]는 이런 배열이 된다

0 1 2 3 4 5 6 7 8 9
int* int* int* int* int* int* int* int* int* int*

 

정수형 포인터배열

int a = 10, b = 20, c = 30, d = 40, e = 50;

int* api[5] = { &a, &b, &c, &d, &e };

 

이런식으로 포인터가 지정됨.

 

2차원배열을 사용하여서 문자열을 저장하면 밑에 그림처럼 낭비되는 공간이 생성됨.

char fruits[4 ][10] = {

  "apple",

  "blueberry",

  "orange",

  “melon"

};

 

문자형 포인터 배열을 ragged배열 이라고함

char* fruits[ ] = {

  "apple",

  "blueberry",

  "orange",

  “melon"

};

 

이렇게하면 가변배열이라서 낭비되는공간이 없음

#include <stdio.h>

int main(void)
{
	int i, n;
	char* fruits[ ] = {
		"apple", 
		"blueberry",
		"orange",
		"melon"
	};

	n = sizeof(fruits)/sizeof(fruits[0]);	// 배열 원소 개수 계산

	for(i = 0; i < n; i++)
		printf("%s \n", fruits[i]);

	return 0;
}

이렇게 배열의 크기를 구해서 반복문으로 출력하면 출력값은

apple

blueberry

orange

melon

 

 

배열 포인터란? 배열을 가리키는 포인터임

아까 포인터배열이랑 헷갈리면 안됌

 

 

포인터배열과 배열포인터 비교

포인터 배열은 포인터를 원소로하는 배열을 만든것

배열 포인터는 기본형을 원소로하는 배열을 가리키는 포인터

#include <stdio.h>
int main(void)
{	int i, j;
	int a[2][3] = { 
		{1, 2, 3}, 
		{4, 5, 6} };
	int* ap[2];

	for (i = 0; i < 2; i++) {
		ap[i] = a[i];
		for (j = 0; j < 3; j++)
			printf("%d ", ap[i][j]);
		printf("\n");
	}
	printf("\n");
	for (i = 0; i < 2; i++) {
		for (j = 0; j < 3; j++)
			printf("%d ", (*(ap+i))[j]);
		printf("\n");
	}
	printf("\n");
}
int(*pa)[3];
	pa = a;
	for (i = 0; i < 2; i++) {
		for (j = 0; j < 3; j++)
			printf("%d ", pa[i][j]);
		printf("\n");
	}
	printf("\n");
	for (i = 0; i < 2; i++) {
		for (j = 0; j < 3; j++)
			printf("%d ", (*pa)[j]);
		printf("\n");
		pa++;
	}

	return 0;
}

이거 이해하고 넘어가기

아직 이해는 못함

 

함수 포인터: 함수를 가리키는 포인터

 

#include <stdio.h>

// 함수 원형 정의
int add(int, int);
int sub(int, int);

int main(void)
{
	int result;
	int (*pf)(int, int);			// 함수 포인터 정의 
	
	pf = add;			// 함수 포인터에 함수 add()의 주소 대입
	result = pf(10, 20);		// 함수 포인터를 통한 함수 add() 호출
	printf("10+20은 %d\n", result);

	pf = sub;			// 함수 포인터에 함수 sub()의 주소 대입
	result = pf(10, 20);		// 함수 포인터를 통한 함수 sub() 호출
	printf("10-20은 %d\n", result);

	return 0;
}

int add(int x, int y)
{
	return x+y;
}

int sub(int x, int y)
{
	return x-y;
}

 

결과값: 

10+20은 30

10-20은 -10

포인터함수는 반드시 *pf에서 괄호먼저 써줘야됌

 

 

함수 포인터의 배열

int (*pf[5]) (int, int);

배열이 *보다 우선순위가 높으므로 이건 포인터들의 배열임 근데 이 포인터들의 배열이 함수 포인터가 됨.

 

이렇게 배열안에있는 포인터들이 함수 포인터가됨.

 

// 함수 포인터 배열
#include <stdio.h>

// 함수 원형 정의
void menu(void);
int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);

void menu(void)
{
	printf("=====================\n");
	printf("0. 덧셈\n");
	printf("1. 뺄셈\n");
	printf("2. 곱셈\n");
	printf("3. 나눗셈\n");
	printf("4. 종료\n");
	printf("=====================\n");
}

int main(void)
{
	int choice, result, x, y;
	// 함수 포인터 배열을 선언하고 초기화한다.
	int (*pf[4])(int, int) = { add, sub, mul, div };

	while(1) 
	{
		menu();
		printf("메뉴를 선택하시오:");
		scanf("%d", &choice);

		if( choice < 0 || choice >=4 )
			break;
		printf("2개의 정수를 입력하시오:");
		scanf("%d %d", &x, &y);

		result = pf[choice](x, y);	// 함수 포인터를 이용한 함수 호출
		printf("연산 결과 = %d\n",result);
	}			
	return 0;
}

int add(int x, int y)
{
	return x + y;
}

int sub(int x, int y)
{
	return x - y;
}

int mul(int x, int y)
{
	return x * y;
}

int div(int x, int y)
{
	return x / y;
}

 

이렇게 함수 포인터를

int (*pf[4])(int,int) = {add, sub, mul, div} 로 하여서 계산할수있다

 

함수 포인터도 인수로 전달이 가능함.

 

 

다차원 배열과 포인터

2차원 배열 int m[3][3]

1행 > 2행 > 3행 >  순으로 메모리에 저장됨(행 우선방법)

같은 행 내에서는 열순서로 저장됨

 

#include <stdio.h>
int main(void)
{
	int m[3][3] = { 10, 20, 30, 40, 50, 60, 70, 80, 90 };

	printf("m = %p\n", m); //%p ->%u
	printf("m[0] = %p\n", m[0]);
	printf("m[1] = %p\n", m[1]);
	printf("m[2] = %p\n", m[2]);
	printf("&m[0][0] = %p\n", &m[0][0]);
	printf("&m[1][0] = %p\n", &m[1][0]);
	printf("&m[2][0] = %p\n", &m[2][0]);

	return 0;
}

m = 1245020

m[0] = 1245020

m[1] = 1245032

m[2] = 1245044

&m[0][0] = 1245020

&m[1][0] = 1245032

&m[2][0] = 1245044

여기서 결과값이 이렇게 나오는데 저 %p라는 것은 배열의 주소를 출력함 근데 우리가 보기 편하게 %u로 해서 주소를 찍어봤는데

m을 찍으면 1245020이라는 값이 나오는데 이건 배열의 시작 주소임(m의 시작주소)

그럼 int형은 4바이트니까 3x3배열이니 m[1]을 찍으면 첫 배열 시작주소(m[0])보다 4*3을 더한 1245032

라는 값이 나옴 마찬가지로 또 m[2]을 찍어보면 거기서 12를 더한 주소 값이 출력됨

그 밑에있는 것들도 마찬가지로 그렇게 주소값이 찍힘

m[0]은 배열 m의 1행의 시작 주소이며 m[1]은 배열 m의 2행의 시작주소임

 

 

int m[3][3]; *[] 연산자는 1:1 대응

- 2차원() 원소

m: int[][]m의 주소, m[0]:int[], m[0]주소, m[0][0]:int

m+1:int[][] =>m+1= &*(m+1)= &m[1]

*(m+1), m[1]:int[] => *(m+1)= m[1], m[1]= *(m+1)

(m+1)[0]:int[] =>(m+1)[0]= *(m+1+0)= m[1]

                         (m+1)[1]= *((m+1)+1)= m[2]

-1차원() 원소

*(m+1)[1]= *((m+1)[1])= *(*(m+1+1))= *(m[2])= m[2][0]

(*(m+1))[1]= m[1][1]

*(m[1]+2)= m[1][2]

*m[1]+2 = m[1][0]+2

아잠만 이거 이해 안되네 뭐지

아 이거 *(m+1)은 m[1]이라고 생각하고 배열이랑 포인터 같이있으면 포인터에 괄호가 쳐져있지않는이상

배열이 먼저임 그래서 (m+1)[1]이면 그냥 더해 그래서 m[2]이렇게 되는거 외우고 (*(m+1))[1] 이렇게 되어있으면

포인터먼저니까 m[1][1]이렇게댐

걍 다 외우자...

 

행의 평균을 구해보자면

#define ROWS 3
#define COLS 3

double get_row_avg(int m[][COLS], int r)
{
	int *p, *endp;
	double sum = 0.0;

	p = &m[r][0]; //m[r]+0
	endp = &m[r][COLS];//m[r]+COLS

	while( p < endp )
		sum += *p++;

	sum /= COLS;
	return sum;
}

이 밑에있는 그림을 참고해서 해보자

우선 포인터로 p랑 endp라는 포인터 변수를 선언

그리고 행의 평균을 구하려면 합이있어야돼서 sum 실수도 선언

그럼 포인터 p에 배열m의 입력받아서 평균을 구해줄 행의 수인 r행과 연결시켜줘r행의 첫번째 인덱스 0 이겠지?

근데 걍 m[r]이라고 해도 노상관.

그리고 endp라는 포인터변수에 cols를 저기서 전역변수값으로 3을줬자나 그걸 넣어주고 r값을 행에넣어주면

저 endp의 주소는 r행의 마지막 인덱스에서 한나 더 플러스된곳의 주소자나

그리고 이제 p는 주소값이지? *p면 저 배열이랑 연결되어있으니까 배열안의 값인데 그냥 p는 주소값이자나

그럼 결과적으로 p에는 r행의 첫번째 인덱스의 주소값이 들어가있고

endp에는 r행의 마지막 인덱스보다 한 인덱스 더 뒤에있는 인덱스의 주소값이 들어있게된거자나

그럼 r행의 배열의 총 합을 구하려면

저 r행렬들을 다 더해야되자나 근데 아까 행렬들의 주소값을 살펴봤을때 행은 위에서부터 아래로 열은 왼에서 오로 갈수록 주소값이

더 커진다했지 그래서 그걸 이용해서 while문으로 p가 endp보다 작을때동안

sum이라는 변수에 *p(p가 아니라 *p니까 배열안에있는 값)값을 더하고 p의 값을 ++한다(주소인p를 ++함으로서 다음 인덱스로 넘어간다) 를 반복해버리면 저 행렬들안에있는 모든 값들을 더한 sum값이 구해지겠지

그럼 그 총 합값을 열의 크기인 cols로 나눠주면 저 행의 값들의 평균이 나오겠찌!!!!

 

 

그럼 한번 전체원소의 평균을 구해보자

#define ROWS 4
#define COLS 3

double get_total_avg(int m[][COLS])
{
	int *p, *endp;
	double sum = 0.0;

	p = &m[0][0];//p=*m, m[0]
	endp = &m[ROWS-1][COLS];

	while( p < endp )
		sum += *p++;

	sum /= ROWS * COLS;
	return sum;
}

아까와 마찬가지로 포인터 변수 두개 선언해주고 p에 

배열의 첫번째 인덱스 주소 연결해주고 endp에 배열의 마지막 인덱스 주소 연결해주기

그리고 p가 endp보다 작을때동안 sum에 *p의 값들을 넣어주고 하나씩 주소를 더해서 옮기는걸해줌

sum += *p++;

그 후 다 더한 sum값을 rows*cols로 나눠줌

그럼 전체 배열원소의 평균값을 구할수있음

 

 

const 포인터

const를 붙이는 위치에 따라서 의미가 달라진다.

const char *p; 는 p가 가리키는 내용이 변경되지 않음을 나타낸다

char *const p; 는 포인터 p가 변경되지않음을 나타냄;

 

 

volatile 포인터

volatile은 다른 프로세스나 스레드가 값을 항상 변경할 수 있으니 값을 사용할때마다 다시 메모리에서 읽으라는 것을 의미함

 

void 포인터(제네릭 포인터)

순수하게 메모리의 주소만 가지고있는 포인터

가리키는 대상물은 아직 정해지지않음

예) void *vp;

*vp;, vp++; vp--; 같은 모든 연산은 오류임.

*(int *)vp;

//void형 포인터를 Int형 포인터로 변환함.

 

그럼 이러한 void 포인터는 어디에 사용하는가?

void 포인터를 이용하면 어떤 타입의 포인터도 받을수있는 함수를 작성할수있음

 

main()함수의 인수

지금까지의 main 함수의 형태는 

int main(void) {

.

.

}

이런 형태였음

근데 외부로부터 입력을 받는 main()함수의 형태는

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

{

.

.

}

이런 형태임

 

인수 전달 방법

C: \cprogram> mycopy src dst

#include <stdio.h>

int main(int argc, char *argv[])
{
	int i = 0;

	for(i = 0;i < argc; i++)
		printf("명령어 라인에서 %d번째 문자열 = %s\n", i, argv[i]);

	return 0;
}

출력값:

c:\cprogram\mainarg\Debug>mainarg src dst

명령어 라인에서 0번째 문자열 = mainarg

명령어 라인에서 1번째 문자열 = src

명령어 라인에서 2번째 문자열 = dst

c:\cprogram\mainarg\Debug>

 

mile2km.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	double mile, km;

	if( argc != 2 ){
		printf("사용 방법: mile2km 거리\n");
		return 1;
	}
	mile = atof(argv[1]);
	km = 1.609 * mile;
	printf("입력된 거리는 %f km입니다. \n", km);
	
	return 0;
}

출력값:

e:>mile2km 10

입력된 거리는 16.090000 km입니다.

 

lab: qsort() 함수 사용하기

qsort()는 데이터가 저장된 배열을 정렬하는데 사용되는 라이브러리 함수이다.

 

void qsort(void *base, size_t nitems, size_t size, int (*compare)(const void*, const void*));

 

base: 정렬될 배열의 주소

nitems: 원소들의 개수(배열의 크기)

size: 각 원소들의 크기(바이트 단위)

compare: 2개의 원소를 비교하는 함수

 

#include <stdio.h>
#include <stdlib.h>

int values[] = { 98, 23, 99, 37, 16 };

int compare(const void * a, const void * b) {
	return (*(int*)a - *(int*)b);
}

int main() {
	int n;

	qsort(values, 5, sizeof(int), compare);

	printf("정렬한 후 배열: ");
	for (n = 0; n < 5; n++)
		printf("%d ", values[n]);
	printf("\n");

	return(0);
}

 

출력값:

정렬한 후 배열: 16 23 37 98 99

계속하려면 아무 키나 누르십시오 . . .

 

스트림이랑 파일같은것들...

 

 

#include <stdio.h>

int main( void )
{
	if( remove( "sample.txt" ) == -1 )
		printf( "sample.txt를 삭제할 수 없습니다.\n" );
	else
      		printf( "sample.txt를 삭제하였습니다.\n" );
	return 0;
}

여기서 remove를 했는데 -1이면 삭제를 못했다뜻임

 

기타 함수들

 

파일의 입출력함수

#include <stdio.h>
int main(void)
{
	FILE *fp = NULL;
 
	fp = fopen("sample.txt", "w");
	if( fp == NULL )
		printf("파일 열기 실패\n");
	else 
		printf("파일 열기 성공\n");

	fputc('a', fp);
	fputc('b', fp);
	fputc('c', fp);
 	fclose(fp);
	return 0;
}

이 코드에서는 sample.txt가 존재한다는걸 기준으로 하였으므로 

출력값은

파일 열기 성공

sample.txt안에있는 값은: abc

 

#include <stdio.h>
int main(void)
{
        FILE *fp = NULL;
        int c;
        fp = fopen("sample.txt", "r");
         if( fp == NULL )
               printf("파일 열기 실패\n");
        else 
               printf("파일 열기 성공\n");
 
        while((c = fgetc(fp)) != EOF )
	putchar(c);
        fclose(fp);
         return 0;
}

fgetc를 이용하여서 파일이 eof즉 -1 정보가 없기전까지 하나씩 c에 넣어서 putchar로 c를 출력하는것이다

 

gets_s, puts, fgets_s, fputs

 

gets_s(): 표준 입력에서 문자들을 개행문자('\n')이나 파일 끝 (eof)를 만나기 전까지 가져와서 str에 저장함

개행문자는 문자열에 포함되지않으며 대신 널문자('\0')는 문자열 맨 마지막에 자동적으로 추가된다.

 

puts(): 표준 출력(stdout)에 쓴뒤 자동으로 개행문자 ('\n')도 추가

 

fgets_s(): 개행문자는 문자열에 포함된다.

 

fputs(): 자동으로 개행문자도 추가되지 않는다.

 

#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
	FILE *fp1, *fp2;
	char file1[100], file2[100];
	char buffer[100];
 
	printf("원본 파일 이름:  ");
	scanf("%s", file1);	
 
	printf("복사 파일 이름:  ");
	scanf("%s", file2);	
 
	// 첫번째 파일을 읽기 모드로 연다.
	if( (fp1 = fopen(file1, "r")) == NULL )
	{
		fprintf(stderr,"원본 파일 %s을 열 수 없습니다.\n", file1);
		exit(1);
	}

// 두번째 파일을 쓰기 모드로 연다.
	if( (fp2 = fopen(file2, "w")) == NULL )
	{
		fprintf(stderr,"복사 파일 %s을 열 수 없습니다.\n", file2);
		exit(1);
	}
 
	// 첫번째 파일을 두번째 파일로 복사한다.
	while( fgets(buffer, 100, fp1) != NULL )
		fputs(buffer, fp2);	
 
	fclose(fp1);
	fclose(fp2);
 
	return 0;
}

출력값:

원본 파일 이름:  a.txt

복사 파일 이름:  b.txt

 

이거는 a파일을 b파일로 복사하는건데

a파일은 읽기모드로 열고 b파일은 쓰기모드로 연다 물론 각각의 경우에대해서 null이라면 열수없다고 출력하고

프로그램이 종료되게해준다

이상없이 두 파일이 다 열렸다면 while문을 통하여 최대 100자까지 버퍼에 쏜다그후 쓰기파일에 있는 그대로 

문자를 적는다.

 

 

슬라이드 30페이지 부터

이진파일의 쓰기와 읽기

이진 파일의 예시

이진 파일의 모드

 

이진파일을 쓰려면?

fwrite()

일단여기까지하고

 다음으로 넘어감 

 

 

전처리 및 다중 소스파일

 

전처리기란? 컴파일하기에 앞서서 소스 파일을 처리하는 컴파일러의 한부분

지시어 의미
#define 매크로 정의
#include 파일 포함
#undef 매크로 정의 해제
#if 조건이 참일 경우
#else 조건이 거짓일 경우
#endif 조건 처리 문장 종료
#ifdef 매크로가 정의되어 있는 경우
#ifndef 매크로가 정의되어 있지 않은 경우
#line 행번호 출력
#pragma 시스템에 따라 의미가 다름

 

단순 매크로 정의

#define MAX_SIZE 100

: 기호상수 max_size를 100으로 정의한다.

 

단순 매크로의 장점

프로그램의 가독성을 높힌다, 상수 변경이 용이하다

 

단순 매크로의 예시

#define PI   3.141592  // 원주율

#define TWOPI   (3.141592 * 2.0)  // 원주율의 2

#define MAX_INT   2147483647  // 최대정수

#define EOF   (-1)  // 파일의 끝표시

#define MAX_STUDENTS   2000  // 최대 학생수

#define EPS   1.0e-9  // 실수의 계산 한계

#define DIGITS   "0123456789"  // 문자 상수 정의

#define BRACKET   "(){}[]"  // 문자 상수 정의

#define getchar()   getc(stdin)    // stdio.h에 정의

#define putchar()   putc(stdout)  // stdio.h에 정의

 

#include <stdio.h>
#define AND	&&
#define OR 	||
#define NOT	!
#define IS		==
#define ISNOT	!=

int search(int list[], int n, int key)
{
	int i = 0;

	while( i < n AND list[i] != key )
		i++;
	if( i IS n )
		return -1;
	else 
		return i;
} 
int main(void)
{
	int m[] = { 1, 2, 3, 4, 5, 6, 7 };
	printf("배열에서 5의 위치=%d\n", search(m, sizeof(m) / sizeof(int), 5));
	return 0;
}

 

출력값:

배열에서 5의 위치=4

여기보면 단순 매크로로 사칙연산을 정의 해주고있다

 

함수 매크로란? 매크로가 함수처럼 매개 변수를 가지는 것

#define SQUARE(x) ((x) * (x))

#define SUM(x, y)                 ((x) + (y))

#define AVERAGE(x, y, z)     (( (x) + (y) + (z) ) / 3 )

#define MAX(x,y)                  ( (x) > (y) ) ? (x) : (y)

#define MIN(x,y)                  ( (x) < (y) ) ? (x) : (y)

 

주의할점

1. 함수 매크로에서는 매개 변수를 괄호로 둘러싸는게 좋다

#define SQUARE(x)     x*x  // 위험 !!

v = SQUARE(a+b);

v = a + b*a + b;

이런식으로 의도한것과 다른 결과가 나올수있기때문

 

2. 매크로 이름과 괄호 사이에 공백이있으면 안된다

#define ADD (x, y) ((x)+(y))

이렇게 ADD랑 (x,y)사이에 공백이 있으면 ADD를 기호상수로 인식

 

장점

함수 호출 단계가 필요없어서 실행 속도가 빠르다.

 

매크로를 한줄 이상으로 연장하는 방법은?

#define PRINT(x) if( debug==1 && \

  mode==1 ) \

  printf(“%d”, x);

이런식으로 끝마다 \ 를 붙여주고 마지막엔 세미콜론

 

// 매크로 예제
#include <stdio.h>
#define SQUARE(x)  ((x) * (x))
 
int main(void)
{
	int x = 2;
 
	printf("%d\n", SQUARE(x));	
	printf("%d\n", SQUARE(3));	
	printf("%f\n", SQUARE(1.2));	// 실수에도 적용 가능	
	printf("%d\n", SQUARE(x+3));	
	printf("%d\n", 100/SQUARE(x));	
	printf("%d\n", SQUARE(++x));	
 
	return 0;
}

출력값:

4

9

1.440000

25

25

16

이런식으로 매크로 함수를 선언해준후 사용할땐 실수에도 적용이 가능하며 나누기도된다

 

#연산자

PRINT(x)와 같이 호출하면 x=5와 같이 출력하는 매크로 작성

다음과 같이 정의된 매크로를 살펴보자

#define PRINT(exp) printf(#exp " = %d\n", exp);

이 매크로는 매크로 호출시에 전달된 표현식 ('exp')를 문자열로 변환하여 출력하는 역할을 한다

예를들어서 

int x = 10;

PRINT(x);

이렇게 매크로를 호출하면 전처리기는 #exp부분을 전달된 표현식('x')의 문자열로 변환함

따라서 실제 코드는 다음과같은 코드로 처리됨

printf("x=%d\n", x);

 

즉 #exp는 전달된 표현식을 큰 따옴표로 감싸 문자열로 변환하는 역할을 함. 이렇게하면

매크로가 호출될때 전달된 변수 또는 표현식의 이름을 출력할때 유용함

 

내장 매크로란? 미리 정의된 매크로

내장 매크로 설명
__DATE__ 이 매크로를 만나면 현재의 날짜(월 일 년)로 치환된다.
__TIME__ 이 매크로를 만나면 현재의 시간(::)으로 치환된다.
__LINE__ 이 매크로를 만나면 소스 파일에서의 현재의 라인 번호로 치환된다.
__FILE__ 이 매크로를 만나면 소스 파일 이름으로 치환된다.

 

printf("컴파일 날짜=%s\n", __DATE__);

printf("치명적 에러발생 파일이름=%s 라인 번호=%d\n", __FILE__, __LINE__);

이런식으로 사용할 수 있음.

 

assert()

디버깅 모드에서의 에러검출을함

단, 릴리즈 모드에서는 동작하지않음

#include <assert.h> 를 포함시켜야됌

assert(조건식) : 조건식이 false일때 assert() 호출 후 프로그램 종료됨.

#include <assert.h>
int main(void)
{
int score = -1;
assert(score > 0);

return 0;
}

출력값:

Assertion failed: score > 0,

file C:\D\수업\C프로그램\prog\Project16\Project1\test.c, line 28

계속하려면 아무 키나 누르십시오 . . .

 

 

비트 관련 매크로

#include <stdio.h>

#define GET_BIT(w, k) (((w) >> (k)) & 0x01)
#define SET_BIT_ON(w, k) ((w) |= (0x01 << (k)))
#define SET_BIT_OFF(w, k) ((w) &= ~(0x01 << (k)))

int main(void)
{
	int data=0;
	SET_BIT_ON(data, 2);
	printf("%08X\n", data);	
	printf("%d\n", GET_BIT(data, 2));	
	
	SET_BIT_OFF(data, 2);
	printf("%08X\n", data);
	printf("%d\n", GET_BIT(data, 2));		
	return 0;
}

처음보면 어려울수있겠지만 생각해보면 쉬움 

우선 매크로 함수를 살펴보면

GET_BIT(w,k) 함수는 주어진 정수 w의 k 번째 비트를 가져오는 매크로임.

SET_BIT_ON(w,k) 함수는 주어진 정수 w의 k번째 비트를 1로 설정하는 매크로임.

SET_BIT_OFF(w,k) 함수는 주어진 정수 w의 k번째 비트를 0로 설정하는 매크로임.

그럼 이제 main 함수를 살펴보자

우선 data를 0으로 초기화 시켜주고 setbiton을 사용하여서 data의 2번째 비트를 1로 만들고있음

근데 이게 2번째비트라고해서 뒤에서부터 2번째가아니라 2번 민다고 생각하면됨

즉 3번째 비트임.

그래서 이걸 16진수로 출력을 해주면 

00000004라는 값이 나옴. 원래 00000000이였는데 두번 밀어서 세번째 비트를 1로 만들어서 00000100(=4)

저값을 16진수로 출력한거임.

그 후 getbit을 사용해서 data의 2번째 비트(2번밀린 비트 3번째)를 출력해보면 1이 나옴

그리고 setbitoff를 사용해서 data의 두번째 비트(3번째 비트)를 0으로 만들어주고 

아까와 마찬가지로 16진수로 출력해보면 00000000 이렇게나옴 왜냐면 아까 1로된게 0으로되어서

다시 저렇게됨. 그 후 getbit을 사용해서 data의 2번째 비트(세번째 비트)를 출력해보면 0으로 바뀌어서 0이 출력됨.

 

#ifdef 

 

#define DEBUG

int average(int x, int y)
{
#ifdef DEBUG
	printf("x=%d , y=%d\n", x,y); //DEBUG가 정의되어있으니 컴파일에 포함
#endif
	return (x+y)/2;
}

 

 

저 #ifdef뒤에 오는 DEBUG가 정의되어있으면 #ifdef와 #endif사이의 코드가 동작을한다

만약 DEBUG가 정의되어있지않으면 저 사이에있는 코드는 동작하지않는다.

이때 정의되는 DEBUG의 값은 상관이없다 그저 정의가 되었는지 안되어있는지가 중요하다.

 

이렇게 정의가되어있지않으면 저 사이의 코드가 동작안함.

 

#ifndef, #undef

 

#ifndef는 어떤 매크로가 정의되어있지않으면 코드가 동작하는거임 예를들어서 만약

LIMIT이라는게 정의되어있지않으면

이렇게 ifndef와 #endif사이의 코드가 돌아가니까 LIMIT를 정의해주는 방식으로 사용 가능

 

#undef는 매크로의 정의를 취소함.

 

#if

기호가 참으로 계산되면 사이의 코드가 동작됨.

조건은 정수 상수여야하고, 논리,관계연산자 사용가능함

실수나 문자열은 비교못함.

 

#if DEBUG==1

    printf("value=%d\n",value);

#endif

 

이렇게 매크로 DEBUG의 값이 1이면 #if 와 #endif사이에 있는 코드들을 동작함.

 

정렬 알고리즘을 선택할때 유용함

#define SORT_METHOD 3
 
#if (SORT_METHOD == 1)
...    // 선택정렬구현
#elif (SORT_METHOD == 2)
...    // 버블정렬구현
#else
...    // 퀵정렬구현
#endif

 이렇게 정의할때 매크로에 번호를 매겨서 정렬 알고리즘을 선택할수있음.

 

#if (AUTHOR == KIM) // 가능!! KIM은 다른 매크로

#if (VERSION*10 > 500 && LEVEL == BASIC) // 가능!!

#if (VERSION > 3.0) // 오류 !! 정수만 사용

#if (AUTHOR == "CHULSOO") // 오류 !! 실수나 문자열은 사용 못함

 

#if 0 을 사용하여서 주석처리를 할수도있음

#if 0	// 여기서부터 시작하여
void test()
{
/* 여기에 주석이 있다면 코드 전체를 주석 처리하는 것이 쉽지 않다. */
sub();
}
#endif	// 여기까지 주석 처리된다.

이렇게 if에 조건 대신 0을 넣어서 주석을 처리할수있음 #if와 #endif사이를 주석처리할수있음

 

 

다중 소스 파일

 

단일 소스 파일:

파일의 크기가 너무 커진다. 소스 파일을 다시 사용하기가 어렵다.

 

다중 소스 파일:

서로 관련된 코드만을 모아서 하나의 소스 파일로 할 수 있음

소스 파일을 재사용하기가 간편함.

 

헤더 파일을 사용하지않으면 함수원형 정의를 매번 있는대로 많이 써놔야됌

근데 헤더파일을 사용하게되면 간단해짐

 

 

비트 필드 구조체

구조체의 정수형의 멤버 변수를 비트단위로 나누어서 사용

: 사용 메모리 크기를 절약(내장 시스템)

정수형: (unsigned, signed) char, short, int, long

사용법: 

struct tag {

   정수형 변수명: 비트수;

}

 

멤버가 비트 단위로 나누어져있는 구조체

struct 태그이름 {

  자료형 멤버이름1: 비트수;

  자료형 멤버이름2: 비트수;

  ...

};

struct product {

  unsigned style : 3;

  unsigned size  : 2;

  unsigned color : 1;

};

 

비트 필드의 장점

메모리가 절약된다. 

ON 또는 OFF의 상태만 가지는 변수를 저장할 때 32비트의 int형 변수를 사용하는 것보다는

1비트 크기의 비트 필드를 사용하는 편이 훨씬 메모리를 절약한다.

 

회원가입한 사용자에게 회원 탈퇴를 할때 탈퇴 사유를 받기로했고 탈퇴 사유를 번호로 매겨서 쿼리스트링 파라미터로 서버에 넘겨주는 방식으로 했다.

근데 여기서 delete메서드를 사용했는데 

이런식으로 헤더에는 엑세스 토큰을 넣어주고 경로변수로 이유들을 쿼리스트링으로 주기로 하였다.

 

근데 탈퇴 과정에서 401에러가 뜨면서 토큰이 존재하지않는다는 에러가 자꾸 났다.

// 당시 에러
{
    "isSuccess": false,
    "code": "JWT_IS_NOT_EXIST",
    "message": "InsufficientAuthenticationException"
}

분명 헤더에 엑세스토큰을 넣었고 삭제 url 또한 문제가 없었다.

 

근데 생각해보니 delete 메서드를 보면 삭제할때 전달해주는 정보가 메서드안에 reason밖에 없었다. 그래서 혹시몰라서 header 파라미터 또한 넣어서 구동을 해주니 정상작동이 되었다.

 

// 당시 회원 탈퇴 요청
const response = await api.delete('/user/withdraw', {
        params: {
          reasons: reason,
        },
        headers: {
            Authorization: `${accessToken}`,
          }, 
      });

 

어찌보면 당연한 이야기다 삭제할정보인 토큰을 안넣다니....

이걸로 한가지 깨달은건 헤더에 토큰이 넣어져있다고 delete요청을 보낼때 헤더에있는 정보들이 삭제되는게 아닌 따로 파라미터로 넣어줘야 한다는 것을...!

앞으로는 이런 오류가 발생하지않도록 주의해야겠다...(이걸로 2일을 날림...)

jwt 방식의 인증(보안) 강화방식인 Access Token과 Refresh Token

Access Token만을 이용한 인증방식은 만약 제 3자가 탈취당할 경우 보안에 취약하다 따라서 Access Token은 발급된 이후,

서버에 저장되지않고 token 자체로 검증하며 사용자의 권한을 인증하기에 Access Token이 탈취되면 token 만료 이전에 token을

획득한 사람은 누구나 권한 접근이 가능해진다.

 

jwt는 발급한 후 삭제가 불가능 하기에 접근에 관여하는 token에 유효시간을 부여하는 식으로 탈취 문제에 대해 대응 해야한다.

이렇게 token 유효기간을 짧게 하면 토큰 남용을 방지하는것이 해결책이 될 수 있지만, 유효 기간이 짧은 token의 경우

그만큼 사용자는 로그인을 자주해서 새로운 token을 발급받아야하는 단점이있다. but 그렇다고 유효시간을 길게하면 토큰을 탈취당하면

보안에 더 취약해진다.

따라서 유효기간을 짧게하면서 좋은 방법은 Refresh Token을 적용하는 것이다.

 

Refresh Token은 Access Token과 똑같은 jwt임. 단지 Access Token은 접근에 관여하는 token, Access Token은

재발급에 관여하는 token 즉 둘의 역할이 다르다.

 

그럼 한번 생각해보자 처음에 로그인을 했을때, 서버를 로그인이 성공하면 클라이언트에게 Access Token과 Refresh Token을 동시에

발급한다.

그럼 서버는 데이터베이스에 Refresh Token을 저장하고, 클라이언트는 Access Token과 Refresh Token을 쿠키, 세션 혹은 웹스토리지에 저장하고 요청이 있을때마다 이 둘을 헤더에 담아서 보낸다.

 

Refresh Token은 긴 유효시간을 가지면서, Access Token이 만료됐을때 새로 재발급 해주는 열쇠가 된다.

따라서 만일 만료된 Access Token을 서버에 보내면, 서버는 같이 보내진 Refresh Token을 데이터베이스에 있는것과

비교하여 일치하면 다시 Access Token을 재발급하는 원리이다. 

만일 사용자가 로그아웃을 하면 저장소에서 Refresh Token을 삭제하여 사용이 불가능하도록 하고 새로 로그인하면 서버에서

다시 재발급해서 데이터베이스에 저장한다.

 

How it works

Refresh Token의 유효기간: 2주,

Access Token의 유효기간: 1시간,

사용자가 api요청을 하다가 1시간이 지남,

갖고있는 Access Token은 만료되게됨.

그러면 Refresh Token의 유효기간 전까지는 Access Token을 새롭게 발급받을 수 있게됨.

즉, Refresh Token은 접근에 대한 권한을 주는것이 아닌 Access Token 재발급에만 관여하는 것.

만일 Refresh Token의 유효기간이 만료됐다면 사용자는 새로 로그인을 해야함.

Refresh Token도 탈취 가능성을 배제할순 없기에 이역시 적절한 유효기간 설정이 필요함(대부분 2주)

 

재발급 원리

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

1. 기본적으로 로그인 과정을 하면 엑세스 토큰과 리프레시 토큰 모두 발급.

이때 리프레시 토큰만 서버측 데베에 저장하며, 리프레시 토큰과 엑세스 토큰을 쿠키 혹은 웹스토리지에 저장

 

2. 사용자가 인증이 필요한 api에 접근하고자 하면 가장 먼저 토큰 검사

이때 토큰 검사하면서 각 경우에 대해서 토큰의 유효기간을 확인하여 재발급 여부 결정

 

case1. 엑세스 토큰과 리프레시 토큰 모두가 만료된 경우

-> 에러발생(재 로그인하여 둘다 새로 발급)

 

case2. 엑세스 토큰은 만료되었지만 리프레시 토큰은 유효한 경우

-> 리프레시 토큰을 검증하여 엑세스 토큰 재발급

 

case3. 엑세스 토큰은 유효하지만, 리프레시 토큰은 만료된 경우

-> 엑세스 토큰을 검증하여 리프레시 토큰 재발급

 

case4. 엑세스 토큰과 리프레시 토큰 모두가 유효한 경우

-> 정상처리

 

tip.

[refresh token을 검증하여 access token 재발급]
클라이언트(쿠키, 웹스토리지)에 저장되어있는 refresh token과 서버 DB에 저장되어있는 refresh token 일치성을 확인한 뒤 access token 재발급한다.

[access token을 검증하여 refresh token 재발급]
access token이 유효하다라는 것은 이미 인증된 것과 마찬가지니 바로 refresh token 재발급한다. 

 

3. 로그아웃시, 엑세스토큰과 리프레시 토큰 모두 만료시킴

 

 

사물인터넷(IOT): 현실세계와 가상세계에 존재하는 사람, 사물, 공간, 데이터 등 모든 것들이 인터넷으로 연결되어

서로 소통하고 작용하는 지능형 서비스 인프라

 

수많은 사물들이 정보기술 흡수하여 스마트화, 무수한 유형의 빅데이터 창출, iot와 공간정보의 결합으로 공간의 스마트화,

산업간 경계가 사라짐, 비즈니스 모델의 변혁

 

초연결사회: ICT기술을 바탕으로 사람, 프로세스, 데이터, 사물이 서로 연결됨으로써 지능화된 네트워크가 구축되고,

이를 통해 새로운 가치와 혁신 창출이 가능해진 사회

- 네트워킹을 기반으로 인간의 생활양식과 가치관에 거대한 변화를 일으키는 사회적 패러다임의 진화

- 사회적 연결이 극대화되는 사회(사람 - 사물 - 정보간 실시간 상호작용)

- 인간 둘러싼 다양한 요소들이 연결되어서 시공간적 제약 극복하여 가치 창출

- 무생물 객체끼리도 소통 가능

- 지능,혁신적 서비스 바탕으로 인간중심의 행복사회

 

시간,장소,객체의 제약이없는 사회

 

인간의 욕구는 경제성장과 삶의 질 향상을 위해 지속적으로 발전, 다양한 기술의 발전 및 사회 패러다임 변화

시대별로 사회적 가치 변화하면서 인간 욕구는 보다 인간중심적이고 수준높은 생활 영위

생존,안전 > 쾌적한 생홯

관계,애정 > 공생발전

자기존중 및 자아실현 > 창조적 가치 창출

 

초연결 사회의 모습

- 첨단기술의 발전, 디지털 중심의 생활양식

- 사용자 중심의 생활

- 소통, 공유

- 다채로운 창조문화

- 인간중심의 독창성이 더해지는 사회

 

농업경제 > 산업경제 > 디지털경제 > 초연결경제

 

사물통신(M2M): 물류, 자판기, 주차장, 교통신호, 바코드, cctv등

만물인터넷(IoE): 클라우드, 스마트 그리드, 스마트 공장, 빅데이터, gps

사물인터넷(IOT): 모바일, 콘텐츠, 2차원 바코드, nfc

 

국제개발협력: 개발도상국의 빈곤퇴치와, 경제 사회개발의 모든 활동을 포괄하는 개발을 실현하기위한 국제사회의 

광범위한 협력

 

지능정보기술의 발전에 따른 글로벌 네트워크의 변화는?

답: 시간, 장소, 사물의 제약이 사라졌고,인간을 둘러썬 다양한 요소들이 결합되어서 새로운 가치를 창출할수있게되었다.

초연결 구조란 어떤 의미를 담고 있다고 생각되는가?

답: 시간, 장소, 사물의 제약이 사라졌고, 인간을 둘러싼 다양한 요소들이 결합되어 새로운 가치를 창출하며 무생물끼리도

소통하는 사회적 연결이 극대회되는 의미

 

한국의 추진 현황과 변화양상

한국은 유,무선 인터넷 이용률이 매우 높고, 세계 최고수준의 초연결사회를 만드는 기반을 다지고있으며,

선진국보다 기술은 모자라지만, ICT 사업의 수준은 높음

 

한국 ict패러다임 변화

기존 ict관점: ict자체에 목적화

현재 ict관점: ict의 수단화, 기반화

 

초연결 사회로의 변화

과거: 신산업 창출, 신시장 개척

현재: 생산성 향상, 융합화 가속, 신기술 개발

미래: 글로벌화 촉진, 사회/문화 동질화

 

1.산업사회(산업혁명): 자본주의, 산업기술, 산업사회

2.정보화 사회(정보화 혁명): 신 자유주의, 정보기술, 정보사회

3. 초연결 사회(초연결 혁명): 인본주의, 초연결 기술 , 초연결 사회

 

초연결사회 추진 원칙:

- 적극적 참여와 상호협력

- 일자리 확보

- 꿈실현 가능 토양 마련

 

주요추진사항:

정부: 국민이 필요로하는 공공데이터,서비스기반 개방

민간: 창의적 아이디어를 바탕으로 혁신적 서비스 제공

 

정부: 사회의 공공성, 소통을 증진하고 합리적 의사결정 지원

민간: 데이터등 사회적 자본 확충

 

DAC란?

개발 도상국을 돕기 위해 조직된 oecd산하기관

 

ODA란?

공적개발원조, 개발도상국에 대한 공적자금

- 정부 또는 정부의 원조기관에 의해 공여

- 개발도상국의 경제발전에 기여

- 공여조건이 개발도상국에게 부담되지않도록 무상부분 일정비율 이상

 

한국국제협력단(koica)

설립목적: 우리나라와 개발도상국가와의 우호관계유지, 상호교류 증진하기위해

 

일본국제협력기구(jaica)

설립목적: 개도국지원

 

MDGs란?

천년개발목표로 불리는 종합적인 빈곤 감소 계획(빈곤이 화폐소득의 과소만으로 해석할수없다는 측면)

 

개발이란?

자유무역중심보다 산업화가 국익에 더 도움이됨.

2차 세계대전후 개발에 대한 논의는 파괴된 열강의 재건과 신생독립국의 후진성 수정이라는 

중요한 과제를 중심으로 진행

빈곤국의 경제적 성장을 위한 인프라 구축, 후진성 벗어나기 위한 제도구축등은

근대계량경제학의 경제 통계를 중심으로 실행

공여국이 수원국에 일방적으로 원조하는 관계: 국제개발원조라고했었음

 

1950년대 이전

공적개발 원조의 역사

국가간 원조가 대규모의 국제적 원조로 전환하게된건 2차 세계대전 이후 본격화된 냉전체제

2차 세계대전 이후 개발원조는 전쟁중 적국의 점령으로 경제가 파탄난 국가들에 대한 긴급원조로 시작

미국은 트루먼 선언을 통해 미국을 중심으로 한 자본주의 진영과 소련을 중심으로한 사회주의 진영은

각 진영의 세력확대를 위해 원조 활발히 추진

 

1950년대

마샬플랜: 2차대전 종식 후 미국의 대 유럽 원조 진행 1952년 전후 사회 경제적 인프라 제건과

구축부터 시작하여 경제발전을 이끌어내는 계기마련

 

1960년대 남북문제의 대두와 un의 노력

이데올로기보다 남북 문제가 더 중요한 원조의 정책적 과제로 등장.

남북 문제를 해결하기 위해 공여국 간의 다자간 협력이 활발해진 시기

DAC성립: 국제 사회는 원조의 국제적 조정과 협의를 위한 기구 필요.

DAC가 개발원조의 회계적 기준을 정립

 

1970년

선진국과 개도국간의 경제차이 격화

절대 빈곤 수치, 개도국내의 소득격차 급증

일부 개도국으로부터 선진국의 부 재분배해야한다는 의견

세계경제위기로 인해 선진국의 원조감소 -> 개도국 개발의 성과 안보임

 

 1980년

un 개발 10년 계획과 브란트 보고서: 세계개발기금 창설

남북 정상회담

신자유주의(세계화, 탈규제, 민영화, 노동유연화) 흐름 유입

 

1990년

냉전 종식과 세계화(소련 붕괴)

인권문제의 범 세계적인 해결책을 찾고자함.

환경문제의 대두와 지속가능한 개발

참여적 개발등장(수혜자 주도의 개발 방법)

 

2000년대

천년개발목표 달성위해 노력 가속화

정보통신 기술의 발전은 기업의 생산등 전분야에서 생산성을 높이고 사회적 거래비용 낮춤

정보통신기술에 대한 접근성 및 정보격차는 개발협력의 중점과제

선진국과 개도국간의 정보화격차 4배이상

무상원조 및 비구속성 원조의 확대: 수원국의 변제 의무가 없는 무상원조 확대

인도적 차원의 빈민감소 목적과 개발원조의 질적개선에 더욱 효율적이라는데 동의

 

국제관계의 변화는 어떤 방향으로 진행된다고 생각하는가?

연도별로 보았을때 국제 제도의 혁신, 경제적 상호의존성 강화, 국제안보의 다양화등

더 다양한 방향으로 진행될것이라고 생각이들며, 이런 변화는 국제 사회가 더 효과적으로

협력하고 글로벌 도전과제에 대처하기 위한 새로운 기회

 

국제협력이 어떤점에서 필요하다고 생각되는가?

감염병 대유행, 국제 테러리스트같은 글로벌 문제에 대응가능, 전쟁과 충돌 예방하며,

국제 안전과 안정 유지하는데에 필요함. 국제무역 촉진하고 경제적 상호의존성 키우는데

중요한 역할을 하며 개도국간의 협력을 촉진하여 국제적으로 빈곤과 불평등을 줄이는데 있어서 필요

개발이란?

'감추어져 있던것 혹은 쌓여있던것이 드러나다'

잠재되어있던 능력 혹은 동력이 드러나면서 변화를 가져오는것을 의미

사회의 성장, 진보를 의미

국민 총생산, 국민 소득의 증가

인간의 복지와 자유를 증진하는데 필요한 조건에는 교육,의료, 정치적 자유, 인권의 신장등도 포함

아마티아 센: 분배와 성장의 조화를 강조

개발은 진정한 자유를 확대하는것

개발의 과정은 인간 자유의 방해 요소를 제거해나가는 과정

 

MDGs:un천년개발목표

-절대 빈곤 및 기아퇴치, 보편적 초등교육, 평등, 아동 사망률 감소, 에이즈및 말라리아 질병퇴치, 환경보전

 

빈곤선은 생존에 필요한 최소한의 소비에 의해 결정

즉 인간의 기본적 욕구 접근 방법에 기초한 프로그램은 생산적 투자보단 빈곤탈출에 필요한 최소한의 소비실현에 초점

 

역량개발: 개발 원조방식의 한계를 인지하고 효과적 원조와 지속가능한 개발에 고심.

 

전통적 패러다임과 기술협력의 문제점:

1950년대부터 1990년대까지 개발은 대체로 개도국이 선진국의 개발과정을 모방. 선진국도 그들의 노하우를

투입하거나 이전하는것에 그침 이는 공여자의 아이디어를 수혜자가 배우고 따르는 권력구조를 탈피하지못함

 

새로운 패러다임과 역량개발:

종래의 개발원조는 수혜자의 수요보다 원조제공자의 공급에 맞춤

역량개발을 기관의 임무와 기능의 핵심으로 지정

 

전통적 패러다임과 새로운 패러다임 비교

전통적 패러다임:

 

역량이란?

oecd/dac: 사람들,기관들,사회 전체가 하고자 하는 일을 성공적으로 해낼수있는 능력

undp: 개인과 기관 및 사회가 지속가능한 방식으로 자신의 직분과 역할을 수행하고 문제를 해결하며 목표를 설정하고 달성 할 수 있는 능력

세계은행: 각국이 개발목표를 달성하도록 하는 개인,기관,관행의 결합체

 

역량개발의 개념 및 핵심:

역량개발의 핵심은 환경과 상황에 따라 변모하고 오랜시간동안 포괄적으로 이루어지는 역동적인 과정

 

 

ODA란?

공적개발원조로써 정부에 의해 개도국 또는 국제기구에 공여되는 것으로, 개도국의 경제,사회발전과 복지향상에

기여하기 위해 제공되는 자금 또는 기술협력

 

ODA의 세가지 충족 조건

- 공적기관에 의해 개도국, 국제기구, 또는 개발 NGO에 공여될 것

- 개도국의 경제개발 및 복지증진에 기여하는 것을 주목적으로 할것

- 차관일 경우, 양허성 재원이어야하며 증여율이 최소한 25퍼센트가 될것

 

ODA지원 목적

- 인도주의적 동기(이상주의): 인간의 보편적 가치의 실현을 위한 도덕적 의무

- 국가안보, 외교적 동기(현실 주의): 수원국으로부터 국제적 지지 기대, 국제 사회에서의 리더십과 영향력

- 경제적 동기(현실 주의): 원조를 통해 공여국의 제품 수출 및 기업 진출, 자원확보

- 모든 사람들간의 연대(상호 이익론): 세계적 과제를 관리하는데 필요한 가치의 확장 환경보호, 마약등

 

ODA의 필요성

- 개발 협력의 수혜자

- 높은 대외의존도와 개도국의 중요성: 자원빈국인 우리나라의 경우 특히 개도국에 대한 의존도 높음

개도국 지원을 통한 수출시장 및 자원의 안정적확보는 국가 생존에 직결

- 외교통상 및 경제진출 수단: 국제사회에서 관계를 만드는 국가적 비즈니스, 개도국과의 협력관계 증진 통해

수출기반 및 지지기반 확대로 나타남. 배출한 학생 귀국 연수생들이 각자 나라에서 지도적 인사로 성장하면 우리에게 도움

- 국제사회에서 우리의 역할 수행: 국제사회에서 우리나라의 능력에 걸맞는 책임과 역할 분담 필요

- 국제협력사업의 환류: 공존의 미덕을 자원으로 상호이해를 생산하는 벤처 비즈니스

 

개발협력의 동기

- 인도적 동기: 빈곤층이 겪는 빈곤과 인간적 고통에 대한 동정적 대응

- 원조국측의 자국 이익(진보된 이기심): 개도국의 번영이 선진국의 상품이나 서비스시장확대에 결부

정치적 안정과 사회적 응집력의 향상은 테러, 범죄 위험 감소

- 모든 사람들간의 연대의식: 전세계인이 협력하여 공동문제 해결하고 공동희망 추구

 

 

우리나라의 공적개발원조 현황

1945~1999년까지 약 128억불 수혜(현가치 600억불)

한국 1987~2009년까지 약 40억불 지원(이건 공여 한것)

지구촌 최초로 수원국에서 공여국으로 전환 2009년에 oecd/dac가입

 

한국전쟁(1950~1953): 수백만의 인명피해, 생산능력의 2/3파괴

전쟁후 분단국으로 남겨짐 전쟁이후 연간 예산의1/3이 국방비로 지출

 

50년전(최빈국)

미국 국제개발원조청: 한국은 밑빠진 독에 물붓기"

 

오늘날(공여국)

gdp규모 세계 12위 교역규모 세계 13위

2010년 dac가입 g20개최

 

한국의 개발협력 연대별 흐름

1960년대: 한국최초의 ODA프로그램 실시

1970년대: 외교적 고려, 개발원조 실시

1980년대: EDCF설립, 유상원조 실시

1990년대: koica설립, 무상원조 실시

2000년대: ODA선진화 새천년개발목표 MDGS선언

 

ODA사업 유형

유형 1. 국별협력사업

건출, 시설물 등 하드웨어와 제도구축등 소프트웨어가 결합된 사업

통합적 개발 프로젝트

-프로젝트형: 건축, 시설물등 하드웨어와 제도구축,전문가파견, 연수생 초청등 소프트웨어가 결합된 사업

건물, 시설물 및 기자재등의 물적 전문가 파견, 연수생 초청등의 인적 협력수단을 결합해 협력수단과

다년간 지원하는 수단

예시사례: 에티오피아 식수위생환경 개선사업

사업목적: 안전한 식수 접근권 향상 및 수인성 질병예방, 화장실 개선 활동으로 위생환경 개선

기간: 2013~2018 875,000달러

-개발컨설팅: 소프트웨어 및 제도구축 지원에 특화된 사업으로 통상 건축사업을 포함하지않는 사업중 컨설팅,전문가파견등 소프트웨어위주 구성

 

유형 2. 글로벌 연수사업

경제 사회발전 과정에서 축적한 경험과 기술을 협력 대상국에 전수해 역량개발과 제도구축지원, 수원국의 인적 자원

개발 및 개발 과제 대처능력 제고 기여

연수 사업 형태: 일반연수, 공동연수, 협력연수, 석사학위연수, 국별연수

발굴 및 형성: 국내 정부부처 제안, 수원국 요청, koica자체발굴

koica 글로벌 연수 사업 브랜드 씨앗(ciat)

 

유형3. 인재양성사업

대한민국 청년들이 글로벌 인재로 성장하는 발판 마련하고 양질의 일자리에 안착하도록 지원하는 사업

- WFK봉사단: 대한민국 국민이 개도국 현장에서 지식과 경험의 공유를 통해 지역사회의 변화 촉진을 유발하여 개도국의

경제 사회 발전에 기여 40여개국 1600명 활동중

 

- 개발협력인재

koica yp, 대학생 봉사단, 글로벌 연수사업 모니터링 인턴, 국제개발협력 인턴십, koica 다자협력전문가,

글로벌 협력의사, 월드프렌즈 코디네이터, 개발협력 코디네이터

 

프로젝트형 > 프로그램형 사업 추세

프로젝트 원조사례: 아르빌 시범학교, 몽골 재난방지연구센터, 필리핀 미곡종합처리장

라오스 중고등학교 교과서 보급

 

기술협력: 우리의 개발경험과 기술을 전수(연수생 초청, 전문가 파견, 봉사단 파견, 개발조사)

 

대한민국의 위상

-2개 ODA 국제회의 개최

- g20정상회담 개최

- 부산 세계개발원조총회

'한강의 기적'을 실현

 

국가브랜드위원회의 비전과 전략

기여하는나라, 존중받는나라, 함께하는 사회, 세계적인 기업

공여국의 단기적 이익 추구보다는 수원국 중심의 원조의 효과성 증진을 목표로 장기적으로 접근해야

궁극적으로 공여국의 국가 이미지 및 브랜드 가치가 형성됨.

 

koica

기관 성격: 외교부 산하의 정부 출연기관

목적: 개도국 경제사회발전 지원을 통한 국제협력 증진에 기여, 개도국과의 우호적인 관계

주요사업: WFK 봉사단 파견, 글로벌 연수, 프로젝트 및 개발컨설팅 사업(국별협력사업)

 

 

요약정리:

초연결 사회란: ICT기술을 바탕으로 사람, 프로세스, 데이터 사물이 서로 연결됨으로서 지능화된 네트워크가

구축되고 이를통해 새로운 가치와 혁신이 창출되는 사회

 

추진현황: 유무선 인터넷 이용률 매우 높음, 데이터 기반의 실시간 처리가 가능형태로 변모여부 관건, 

세계 최고수준의 초연결사회 기반, 기술은 선진국 대비 낮지만, ict융합 수준의 사업은 높음

 

ict 패러다임의 변화: 

기존: ict 자체에 목적화, ict보급에 역점

향후: ict의 수단화, 기반화, ict활용에 역점

 

과거: 신산업 창출, 신시장 개척

현재: 생산성 향상, 융합화 가속, 신기술 개발

미래: 글로벌화 촉진, 사회 문화 동질화

 

초연결망사회 비전: 인간중심사회, 초연결사회, 창조사회

가치: 조화,연결,참여,혁신,신뢰

목표1: 사람의 시각에 맞먹는 인공 시각을 만든다.

목표2: 한정된 범위에서 특정한 임무를 달성하는 인공 시각을 만든다.

 

목표 1로의 접근:

사람 시각 원리 밝혀내서 컴퓨터로 모방

지식표현, 학습, 추론, 창작 같은 인공지능이 필수

하지만, 다양한 변형이 발생해서 어려움(기하학적 변환, 광도변환)

예를들어 우리는 움직이면서 시야가 광도나 각도가 변할때 다 인지하면서 판단하는데 계속

그러면 광도나 기하학적으로 변하잖아? 그런거임.

 

목표 2로의 접근:

특정 상황에서 특정 임무를 수행하는 시스템 구축

성공한 시스템은 많은 응용 현장에서 쓰고있음

컴퓨터 비전이 사람보다 뛰어난 경우는?:엔진 실린더 정밀 측정, 칩 검사

하지만 여전히 이런거말고는 다양한 변환으로인한 문제아서 어려움

 

계층적 처리

영상 > 전처리 > 특징 추출 > 해석 > 고급 묘사

여기서 전처리 > 특징추출이 저급비전

해석이 고급비전

근데 저 고급이라고해서 어려운 정도가아니라 저급이 더 어려움 마치 파이썬보다 c가 더 어렵듯이 ㅎㅎ..

 

전처리: 주로 영상 처리

특징 추출: 에지, 선분, 영역, 텍스처, 지역 특징등을 검출하고 특징 벡터 추출

해석: 응용에 따라 다양한 형태

 

문제해결도구:

-자료구조와 알고리즘(배열,트리,그래프,등등)

-수학(선형대수, 미적분학,확통)

-기계학습

 

컴퓨터 비전 시스템 설계 과정: 문제 이해 > 데베 수집 > 알고리즘 설계,구현 > 성능 평가

-문제이해: 직관적이고 철저한 이해 중요(합리적 제약 조건 수립 필수: 눈이 다보이게 얼굴을 가까히 대던가)

-데이터 베이스 수집: 질적,양적으로 우수해야 고성능 시스템 제작 가능

데이터 베이스 = 학습 집합 + 테스트 집합

수집 방법: 직접 수집(많은 비용 들지만 개발자에게 자산), 인터넷에서 다운(고품질 데이터 풍부)

 

-알고리즘 설계와 구현:

선택의 중요성과 어려움: 새로운 알고리즘 개발 or 기존 알고리즘 중 주어진 문제에 적합한 것 선택

선택 방법: 데베를 이용하여 성능 실험 해보기

 

- 성능 평가

인식 성능 측정: 부류가 심한 불균형일때 부적절

(예시, 칩 검사에서 불량률이 0.1퍼센트라면 임의 짐작 분류기의 정인식률은 99.9퍼센트)

혼동 행렬: 오류 경향을 세밀하게 분석하는데 사용

총 15개의 얼굴중 12개를 옳게 검출 (참긍정 n11=12), 세개의 얼굴 못찾음(거짓부정 n12=3), 

얼굴 아닌 곳을 얼굴로 검출한곳 두개(거짓 긍정 n21=2), 따라서 정확률은 12/14 재현율 12/15

측정은 24/29

 

영상처리(컴퓨터 비전의 전처리 과정): 주어진 목적을 달성하기 위해 원래 영상을 새로운 영상으로 변환

 

사람의 눈을 카메라로 비유한다면 수정체는 렌즈, 망막은 필름(ccd)에 해당

 

2차원 영상공간을 m x n으로 샘플링한다면

이렇게 되는데 이건 12 x 12로 샘플링 한것임.

그리고 명암을 L단계로 양자화 한것임.(명암은 [0,L-1]사이에 분포)

 

영상좌표계

화소의 위치는 x=(j,i) 혹은 x=(y,x)로 표기

영상은 f(x) 혹은 f(j,i)로 표기

히스토그램: [0,L-1]사이의 명암값이 각각 영상에 몇번 나타나는지 표시

히스토그램 h, 정규화 히스토그램

 

히스토그램 용도

- 영상의 특정 파악

- 히스토그램 평활화: 히스토그램을 평평하게 만들어 주는 연산(명암의 동적 범위를 확장하여 품질 향상시켜줌)

누적 히스토그램 c(.)를 매핑함수로 사용

히스토그램의 매핑표를 봐보자

이렇게 매핑표를 보면 처음은 히스토그램의 입력값이고 두번째 h라인은 정규화된 값 c라인은 누적값 

그리고 c x 7(L-1) 은 L-1의 값인 7을 곱해준 값이다 그리고 마지막에 출력될때는 반올림하여 출력한다.

이때 반올림할때는 0.5이상은 하나 위로 업 시켜서 반올림.

 

이렇게 매핑표를 적용시킨 결과를 보면 원래 전영상에서는 동적 범위가 [2,6] 이였는데 히스토그램 평활화를 하고난 후에는

영상의 동적 범위가 [1,7] 로 보다 넓어진걸 확인할 수가 있다(품질 향상)

 

그치만 이렇게 히스토그램을 평활화해서 시각적 느낌이 나빠질 수도 있다.

따라서 영상처리 연산은 분별력을 가지고 활용 여부를 결정해야한다.

 

히스토그램 역투영: 히스토그램을 매핑함수로 사용하여 화소값 -> 신뢰도 값으로 변환

예를들어 얼굴 검출을 보면 모델 얼굴에서 구한 히스토그램 hm은 화소의 컬러 값을 얼굴에 해당하는 신뢰도 값으로

변환해준다.

실제로는 비율 히스토그램 hr을 사용한다.

히스토그램 역투영 결과

얼굴 영역은 높은 신뢰도값, 손 영역도 높은 값을 가진걸 보임

한계: 비슷한 색 분포를 갖는 다른 물체를 구별 못함, 검출 대상이 여러 색 분포를 갖는 경우 오류 가능성이있음.

장점: 배경을 조정할 수 있는 상황에 적합함.(이동과 회전에 불변, 가림에 강인함)

 

이진화: 명암 영상을 흑과 백만 가진 이진 영상으로 변환

임계값 방법: 두 봉우리 사이의 계곡을 임계값 T로 결정.(자연 영상에는 계곡 지점 결정이 어려움)

 

오츄 알고리즘: 이진화 했을때 흑그룹과 백그룹의 각각이 균일해야 좋다는 원리에 근거

- 균일함의 정도인 균일성은 분산으로 측정(분산이 낮을수록 균일성 높음)

분산의 가중치 합을 목적함수로 이용한 최적화 알고리즘

t-1번째의 계산결과를 t번째에 활용하여 빠르게 계산

 

화소의 모양과 연결성

화소는 어떤 모양인지 한번 생각해보자

화소의 연결성은?

 

영상처리의 세가지 기본연산

1. 점연산:

오직 자신의 명암값에 따라 새로운 값을 결정

점연산을 식으로 쓴다면? 대부분은 k=1(즉 한장의 영상을 변환)

선형 연산:

비선형 연산(감마수정(모니터나 프린터 색상 조절에 사용)):

디졸브:

 

2. 영역 연산: 이웃 화소의 명암값에 따라 새로운 값 결정

상관:

원시적인 매칭 연산(물체를 윈도우 형태로 표현하고 물체를 검출)

보면 앞에서부터 화소 3개씩 순차적으로 떼어네어서 (하나씩 옆칸으로 밀기)밑에 윈도우랑 하나씩 곱해서 다 더한값을 밑에다가 출력

위에 예시에서는 최대값 29를 갖는 위치 6에서 물체가 검출됨.

 

컨볼루션:

윈도우를 뒤집은 후 상관 적용(아까 적용한 윈도우를 뒤집어서 상관처럼 적용함)

임펄스 반응

 

2차원

이것도 마찬가지로 아까 상관에서처럼 한픽셀씩 옮겨가면서 상관연산을 적용해주는데 차이점은 2차원이라는거

마찬가지로 컨볼루션은 윈도우를 반대로한것

수식으로 쓰면?

컨볼루션 예제

컨볼루션은 선형연산

- 박스와 가우시안은 스무딩 효과

- 샤프닝은 명암 대비 강조 효과

- 수평 에지와 수직 에지는 에지 검출 효과

 

비선형 연산

메디안 필터:

솔트페퍼 잡음에 효과적임(솔트 페퍼 잡음이 영상에 막 솔트페퍼 뿌려놓은거마냥 이상한 점같은거 생기는거)

메디안은 가우시안에 비해 에지보존 효과가 뛰어남

 

3. 기하 연산: 일정한 기하 연산으로 결정된 화소의 명암값에 따라 새로운 값 결정

동차좌표와 동차행렬

동차좌표:

동차 행렬:

 

동차행렬을 이용한 기하변환

예를들어서 어떤 점을 y방향으로 3, x방향으로 2만큼 이동시키는 동차행렬 H는 다음과같다.

밑에 식은 동차좌표 x와 동차행렬 H를 이용한 기하변환임.

풀어서 쓰면,

다음 그림의 삼각형을 y방향으로 3, x방향으로 2만큼 이동시킨후 30도 회전시켜보자.

먼저 이동 변환을 구하려면 T(3,2)가 필요하다. 꼭지점 x1=(3,5)를 동차좌표로 확장하여 x'1=(3,5,1)로 만들고 

아까한 이동연산을 적용한다.

이렇게 연산의 결과로 x'1=(6,7,1)을 얻었는데 마지막 요소를 제거하여 2차원 좌표로 바꾸면 x'1=(6,7)이 된다.

나머지 두점 x2 와 x3도 같은 과정으로 변환한후 이동한 삼각형을 그려보면 가운데 회색 삼각형과 같다.

이제 이동을 했으니 삼각형을 30도 회전 시켜야한다.

회전을 계산하는데 필요한 행렬인 R(30도)를 꼭지점 x'1에 적용하면 다음과같은 결과를 얻는다.

x''1=(8.6962,3.0622,1)의 결과를 얻는다. 나머지 두점도 계산하고 결과를 그려보면 맨 아래에 진한 회색의

삼각형의 결과가 나온다.

 

그렇다면 왜 동차좌표를 사용할까?

- 복합 변환을 이용한 계산 효율

예) 이동 후 회전은 두번의 행렬곱셈이지만 복합 변환을 이용하면 한번의 곱셈임

아까 변환에 필요한 행렬 T(3,2)와 R(30도)를 곱하면 다음과 같다.

이제 이 행렬을 원래 삼각형의 세개의 꼭지점에 각각 대입하면 최종 변환된 삼각형을 얻을 수 있다.

꼭지점 x1=(3,5)에 적용한 결과는 다음과 같다.(행렬 곱을 두번한 것과 결과가 동일함)

임의의 점 (cx,cy)를 중심으로 회전한다면 이 연산을 적용하면됨.

영상에 적용할때 전방변환은 심한 에일리어싱 현상이 발생하여

후방변환을 이용한 안티 에일리어싱 현상을 일으킴.

 

보간에 의한 안티 에일리어싱:

실수 좌표를 반올림하여 정수로 변환하는 과정에서 에일리어싱 발생함.

주위 화소값을 이용한 보간으로 안티 에일리어싱 진행

-양선형 보간

이런식으로 좀 더 가까운 곳에 있는 화소와 좀 먼곳의 있는 화소의 거리비율을 통해서 값을

얼마나 참조할것인지 비율로 결정하는것이 양선형 보간임.

 

초근접 이웃, 양선형 보간, 양3차보간의 비교

이렇게 초근접 이웃으로 진행하게되면 회전을 하였을때 픽셀이 깨져보일수가있음.

 

다해상도:

해상도를 줄이거나 늘리는 연산

응용: 멀티미디어 장치에 디스플레이, 물체 크기 변환에 강인한 인식

업샘플링과 다운 샘플링

피라미드 방법: 샘플링 비율 0.5로 다운 샘플링 반복

피라미드 구축 연산을 식으로 쓰면,

에일리어싱 발생(화소에 따라 100퍼센트 또는 0퍼센트 만큼 공헌)

 

Burt&Adelson 방법:

모든 화소가 50퍼센트씩 공헌

 

 

모폴로지: 원래 생물학에서 생물의 모양 변화를 표현하는 기법

수학적 모폴로지: 컴퓨터 비전에서 패턴을 원하는 형태로 변환하는 기법

 

-이진 모폴로지:

몇가지 대표적인 구조 요

팽창, 침식, 열기, 닫기 연산

 

컬러

RGB모델:

길이가 1인 정육면체로 색을 표현함.

rgb모델로 영상 표현

 

HSI 모델:

이중 콘으로 색 표현

가장 단순한 방법은 세 체널을 독립적으로 처리

하지만 독립적으로 적용하면 적절하지 않은 경우: 히스토그램 평활화

 

에지의 유용성

- 물체의 경계를 표시해줌

- 매칭에 용이한 선분이나 곡선으로 변환가능

 

에지의 한계

실종된 에지(거짓 부정), 거짓 에지(거짓 긍정) 발생

이 오류들을 어떻게 최소화 할것인가?

 

에지검출의 원리

- 물체 내부나 배경은 변화가 없거나 작은 반면, 물체 경계는 변화가 큼

- 이 원리에 따라 에지 검출 알고리즘은 명암, 컬러, 또는 텍스처의 변화량을 측정, 변화량이 큰곳을 에지로 검출함

 

디지털 영상의 미분

1차원:

연속 공간에서의 미분

디지털 공간에서 미분

이에 해당하는 마스크  -1,1 <- 에지 연산자

디지털 영상에서 미분할때 1차미분은 이 -1,1 에지 연산자로 컨볼루션 하는거처럼 해주면됌

정리

1차 미분 마스크: -1,1

계단 에지와 램프 에지

 

자연 영상에서는 주로 램프 에지가 나타남

계단에지는 급격하게 바뀔때 관찰되고 램프에지는 서서히 변할때 관찰

2차 도함수를 구할땐 1차도함수를 구한것에 1,-2,1마스크를 컨볼루션 하는거처럼 해주면됌

정리:

1차 도함수 구하는 마스크: -1,1

2차 도함수를 구하는 마스크: 1,-2,1

 

렘프에지에서 미분을 하게되면

이런식으로 결과가 나오게 되는데 우리는 여기서 에지를 검출할때

1차 미분에서 봉우리 또는 2차 미분에서 영교차를 찾음

두꺼운 에지에서 위치 찾기 적용

 

현실에서는 잡음때문에 스무딩이 필요함.

에지 검출 연산

 

여기있는게 에지 연산자임

소벨 마스크를 이용한 에지 검출(소벨 마스크를 이용한 에지 검출이니 위에있는 에지 연산자중에 소벨을 사용)

 

여기를 보자 저 화살표들은 신경쓰지말고 한번 그레이디언트와 에지 강도, 에지 방향을 계산해보자

우선 그레이디언트:

위에있는 소벨 마스크를 사용하여 y와 x에 대하여 각각의 그레이디언트를 구해주자

우리가 구해야할건 (5,3)에있는 화소에 대해서 구하는거니까 그 화소를 중심으로

my에지 연산자는 y 그레이디언트를 구할때 쓰고

mx에지 연산자는 x 그레이디언트를 구할때 써보자

그렇다면 이렇게 결과가 (-4,2)로 나온다.

그럼 이제 에지 강도를 구해보자 에지 강도를 구하는 식은 위에 나와있드시

이거임 그럼 우리가 구한 값들을 넣어서 계산해주면

이렇게 값이 나옴

다음은 그레이디언트 방향. 그레이디언트 방향을 구하는 식은 위에 나온대로

이렇게임.

그럼 우리가 구한 값들을 대입해서 계산해주면

이렇게 나옴.

에지 방향은 그레이디언트 방향과 수직이니 26.6도임

그럼 에지 방향을 위에있는 양자화한 그림에 대입하면 1이나옴.

양자화한 그림:

빨간색이 그레이디언트 방향이고 그와 수직인 파란색인 화살표가 에지방향임 에지 방향을 잘 보면 양자화했을때 1로나옴.

여기서 26.6도라고해서 우리가 보는기준이아니라 화살표들 기준임.

 

가우시안을 사용하는 이유?

미분은 잡음을 증폭시키므로 스무딩 적용이 중요함.

미분에 의한 잡음 증폭

-세타를 조절하여 다중 스케일 효과, 에지의 세밀함 조절 가능

가우시안:

저기있는 세타로 스케일을 조절함.

 

LOG필터 

Marr-Hildreth 에지 검출 알고리즘

- 2차미분에서 영교차 검출

라플라시안 필터:

log필터: 입력영상에 가우시안 G를 적용한 후, 라플라시안을 다시 적용하는 두단계의 비효율성

- 계산시간 과다, 이산화에 따른 오류 누적

 

예제로 한번 알아보자

여기 간단한 8x8 영상이있다 여기에

세타에 따른 log 연산자

세타 = 0.5 의 3x3 크기의 log 연산자를 적용하여 얻은 영상이 밑에있는것이다.

여기 동그라미로 표시된곳의 화소의 영교차 여부를 따져보자

마주보는 네개의 이웃 쌍중에는 남-북, 북서 - 남동의 두개 쌍의 부호가 다르자 이들을 구성하는 화소의

값 차이는 각각 7.6442와 5.2379이다. 만일 임계값 T=1.0으로 설정했다면 둘다 T를 넘으므로 이 화소는 영교차 점이된다.

밑에있는 그림은 T=1.0으로 설정하여 구한 에지 영상이다.

 

Canny 에지(에지 검출을 최적화 문제로 해결)

세가지 기준

- 최소 오류율: 거짓 긍정과 거짓 부정이 최소(즉 없는 에지가 생성되거나, 에지를 못찾는 경우를 최소로)

- 위치 정확도: 검출된 에지는 실제 에지의 위치와 가급적 가까워야한다.

- 에지 두께: 실제 에지에 해당하는 곳에는 한 두께의 에지만 생성해야함.

 

캐니에지 검출 알고리즘

비최대 억제: 이웃 두 화소보다 에지 강도가 크지 않으면 억제됨

두개의 임계값 Thigh와 Tlow를 사용하여 거짓 긍정 줄임

에지 추적은 Thigh를 넘는 화소에서 시작, 추적 도중에는 Tlow 적용

 

에지의 연결과 표현

이렇게 보면 위에있는 좌표를 잘보면 에지열은 연결되어있는 좌표들을 순서대로 표현하고있고

체인코드는 연결된 좌표들의 방향을 표시하고있음.

 

세선화: 2~3두께 에지를 1두께로 변환

최소 8-연결성 보장

 

SPTA 세선화(껍질을 한번 벗기는 연산)

a의 전환횟수는 2이고, b의 전환횟수는 3, c의 전환 횟수는 2이다.

 

선분 근사

두 끝점을 잇는 직선으로부터 가장 먼 점 까지의 거리 h임계값 이내가 될때까지 선분분할을 재귀적으로 반복.

두 끝점을 잇는 저 선분 보이지? 그 선분으로부터 가장 먼곳에 있는 점의 길이 h가 임계값 이내가 될때까지

선분을 계속 분할한다는 뜻임.

 

허프 변환

에지 연결 과정 없이 선분 검출(전역 연산을 이용한 지각 군집화)

영상공간 y-x를 기울기 절편 공간 b-a로 매핑

 

RANSAC: 인라이어를 찾아 어떤 모델을 적합시키는 기법

-선분 검출에 적용(모델은 직선의 방정식 y = ax+b)

 

대응점 찾기

같은 장면을 다른 시점에서 찍은 두 영상에서 대응하는 점의 쌍을 찾는 문제

파노라마, 물체인식/추적, 스테레오 등 컴퓨터 비전의 중요한 문제 해결의 난초

검출 > 기술 > 매칭 세 단계로 해결

 

무엇을 특징점으로 쓸것인가?

에지? : 에지 강도와 방향 정보만 가지므로, 매칭에 참여하기에 턱없이 부족

 

다른곳과 두드러지게 달라 풍부한 정보 추출 가능한 곳

- 에지 토막에서 곡률이 큰 지점을 코너로 검출

2000년대에 사라짐(더 좋은 대안 떠오름)

 

- 지역특징이라는 새로운 물줄기

명암 영상에서 직접 검출, 의식전환: 코너의 물리적 의미 -> 반복성

 

지역 특징: <위치,스케일,방향,특징 벡터> = ((y,x),s,o,x)로 표현

 

지역 특징이 만족해야할 특성:

반복성, 분별력, 지역성, 정확성, 적당한 양, 계산효율

이들은 길항관계임(하나가 올라가면 하나가 내려감 모든걸 충족시킬수없는 관계)

따라서 응용에 따라 적절한 특징을 선택해야함

 

인지실험

대응점을 찾기가 쉬운(좋은)점은? -> 사람에게 쉬운곳이 컴퓨터에게도 쉬움

좋은 정도를 어떻게 수량화할까? -> 여러 방향으로 밝기 변화가 나타나는 곳일수록 높은 점수

a만큼 뚜렷한 밝기변화가 나타나는 곳이 없음

 

모라벡 알고리즘

제곱차의 합으로 밝기 변화 측정

제곱차 합 계산

현재 이 그림은 삼각형을 가진 12 x 12 영상임. 현재 조사하고 있는점은 (5,3)에 위치한 b이고, 마스크는 모든

값이 1인 3x3크기의 박스형. 이때 오른쪽으로 한 화소만큼 이동시킨 s(0,1)을 계산해보면 다음과 같이 4라는 값을

얻음.

 

이 맵을 관잘해보면 

a와 같은 코너에서는 모든 방향으로 변화가 심함.

b와 같은 에지에서는 에지 방향으로 변화 적지만, 에지에 수직 방향으로 변화 심함.

c와 같은 곳은 모든 방향으로 변화 적음.

즉, a에 높은값 c는 아주 낮은 값 b는 그 사이 값을 부여하는 함수를 만들면 됨.

 

특징 가능성 값 C

C = min(S(0,1), S(0,-1), S(1,0), S(-1,0))

모라벡 함수의 한계:

한 화소만큼 이동하여 네 방향만봄, 잡음에 대한 대처방안 없음

 

해리스 코너: 가중치 제곱차의 합을 이용한 잡음 대처

 

 

 

시스템:

컴퓨터에 의해 처리가 가능한 형태로 자료를 변환하여 입력하고, 그 자료를 저장,처리,가공하여 필요한 시점에

정보를 출력할 수 있도록 설계되고 구현된 정보 체계

시스템 개발에는 누가 참여하는가?

 

 

SDLC 모형의 5단계

sdlc란 소프트웨어 개발 수명 주기임.

이건 시스템 엔지니어링, 정보 시스템, 또는 소프트웨어 공학에서 정보 시스템을 계획, 개발, 시험, 채용하는 과정임.

분석,설계,구현,테스트,유지보수 단계로 이루어짐

 

SDLC 모형의 장단점

장점:

1. 시스템 개발의 각 단계가 비교적 명확함.

2. 각 단계들 간에 유기적인 연관성을 가지고 있어 쉽게 적용할 수 있음.

 

단점:

1. 충분한 분석을 기반으로 개발이 진행되지 않으면 테스트,유지보수 단계에서 문제점이 노출

-> 이를 개선하는데 많은 비용과 시간이 소요됨.

2. 대형 프로젝트는 개발기간동안 외부환경이나 내부 정책이 변화할 소지가 크며, 이를 개선하려면 이전 단계로

되돌아가 변경관리를 해야 하므로 막대한 시간과 비용 듬.

 

 

프로토타입 모형

사용자의 요구사항을 정확히 파악하기 위해 소프트웨어 시제품을 만들어 최종 결과물을 예측하는 모형.

요구사항 수집, 개략 설계, 시제품 구축, 요구사항 평가 수정, 생산품 개발 단계로 이루어짐.

 

프로토타입 모형의 장단점

장점:

1. 개발 초기에 미리 결과물을 확인할 수 있다는 점에서 사용자의 이해를 도움

2. 개발 초기 단계에서 수정,보완할 사항을 미리 파악할 수 있음.

3. 분석 및 설계 과정에 사용자가 동참하여 즉각적인 피드백 가능

 

단점: 

1. 일회적 프로젝트나 대규모 프로젝트의 개발에는 적용하기 어려움

2. 불완전한 요구사항 바탕으로 시제품 만들기에, 결과적으로 불완전한 시스템 산출하여 수정,보완에 많은 인력,시간 소요

 

 

프로젝트 관리자의 활동

제안서 작성, 프로젝트 계획과 일정 수립, 비용산정, 모니터링과 중간평가, 실무자 선정과 평가, 보고서 작성과 발표

 

프로젝트 계획

 

프로젝트 계획과 함께 수립해야 할 계획들

품질계획: 품질을 위한 계획과 표준을 설명

검증계획: 시스템 검증을 위한 접근방법, 자원, 일정을 설명

구성관리계획: 구성관리 방안과 구조를 설명

유지보수계획: 유지보수를 위한 요구사항, 비용, 노력을 설명

인력개발계획: 팀원들의 경험 축적과 기술개발 설명

 

 

1. 프로젝트 일정 수립(언제 뭐 개발할건지 다짜야됌)

2. 품질관리(설계품질(사용자의 요구 반영한것), 적합품질로 나뉨(설계 명세서대로 이행))

개발주기 전체에 걸쳐 시행하는 검열, 검토, 테스트등을 말함.

3.위험관리

위험관리 프로세스

1단계 위험 인식

2단계 위험 분석

3단계 위험 계획

4단계 위험 모니터링

 

 

기능 모델링

 

구조적 분석 방법론:

1980년대부터 활용 시작

현재 요구사항 분석에 가장 많이 활용

사용 도구로는 자료흐름도, 자료사전, 소단위 명세서등이 있음

 

구조적 분석 방법론의 특징:

매우 간결

이해하기 쉬움

검증 가능

체계적

 

SADT:

시스템 구조를 계층적으로 기술

 

이 SADT는 다음과 같은 사항을 수행하기 위한 방법론 제공:

대규모이고 복잡한 문제를 구조적으로 생각하게 함.

각 작업자의 노력,역할 나누고 또 통합해서 팀으로서 효과적으로 활동하게함.

명료,정확한 표기법에 의해서 인터뷰,분석,설계의 결과 전달.

 

SADT표기법

 

PSL/PSA:

정보처리 시스템에 대한 요구사항 분석과 문서화를 지원하는 시스템

 

동적모델링

 

실시간 시스템:

제한된 시간 내에 외부에서 주어진 사건에 응답하고 자료를 처리하는 시스템

 

실시간 시스템의 예:

통신 시스템, 비행기 운행관리 시스템, 자동차 속도 조절장치, 원자력 발전소의 원자로 제어장치

 

상태변화도:

시스템의 제어흐름과 동작의 순서를 나타낸 도식

 

 

정보 모델링

시스템에 필요한 엔티티를 정의, 엔티티 사이의 연관성을 규명

대표적 도구: ERR 모델

 

객체지향 모델링

데이터와 행위를 하나로 묶어 객체를 정의하고 추상화

 

요구사항 조사방법

관찰 조사: 실제 현장 방문

질문지 조사: 질문지로 정보수집

면담 조사: 보편적,중요한 정보수집 방법 직접대화통해 시스템 문제점및 개선 요구사항등을 파악 가능

 

요구사항 조사내용

조직에 대한 정보

현재 사용중인 제반 서식

시스템 인프라

현재 운영중인 시스템

 

기존 검토회의의 문제점

참석자의 역할과 책임 불명확

산출물보다 사람평가 경향

검토회의 목적 불분명

 

구조적 검토회 효과

역할,책임 명확하게 정의

참여자들 갈등 해소

분명한 목표

개발초기 문제점 발견 가능

프로젝트 진척도 확인가능

공동 책임의식 고취

 

구조적 검토회의 참석자 역할

산출물 발표자: 검토회의 참석자들에게 산출물 설명

중재자: 검토회의가 효율적으로 순조롭게 진행되도록 회의 계획, 진행 조정

서기: 검토회의에서 발견된 오류나 기타 문제점 기록

산출물 검토자: 산출물 검토, 표준화,유지보수 요원있음

사용자 대표: 요구사항 충족확인, 피드백,조언

 

제안요청서: 전문 개발업체에게 개발을 의뢰할 경우 작성하는 문서

제안서: 제안요청서를 받은 후보 개발업체들이 작성(개발업체의 사업 수행 능력을 간접적으로 보여줄 수 있는 문서)

 

사업수행 계획서: 제안요청서를 바탕으로 사업수행에 필요한 제반 계획사항들을 명확히 기술하는 문서

-산출물계획, 일정계획, 품질관리계획, 보고계획, 위기관리 및 보안대책, 교육계획, 주관기관 협조 요청사항

 

설계명세서: 설계과정에서 산출된 각종 설계 문서를 뜻함.

-시스템 구조도, 데이터베이스 설계문서, 프로그램 작성 지침, 인터페이스 설계문서등이 포함됨.

 

전통적인 모델의 문제점 구현

폭포수 모델의 문제점

사용자 요구 파악과 명세의 어려움(정확한 요구를 상대방에게 전달하기 어려움)

일정을 못 맞출 위험(이전단계에 모든것이 완료되었을때만 다음단계로 진행)

요구 변경을 수용하기 어려움(변경에 필요한 비용이 뒤로 갈수록 커짐)

품질 확보가 어려움(프로젝트 종료 시점에 테스트를 함)

 

반면 애자일 모델짧은 개발주기를 가지고있음.(단기간에 개발하여 중간 결과를 확인가능)

우선순위가 높은 필수 기능부터 만들고, 요구하는 기능을 순차적으로 추가 하면서 개발,

비전이 기능의 규모를 결정,  분업화를 추구하지 않으며 하나의 팀과 모든일은 팀의 책임

 

애자일

고객 중심

큰 프로젝트를 작게 분할

스크럼 - 실제 구현하고 싶은 유저 스토리라는 짧은 문장으로 표현

 

테스트 중심

테스트 반복 및 일상화

 

피드백

올바른 목표를 향하여 가고있는지 정기적으로 고객에게 물음

 

프로젝트관리

작업 추정 - 각 반복주기(스프린트)의 유저 스토리에 대하여 규모와 복잡도를 고려한 점수화

진행속도 - 각 스프린트에 대하여 평균 유저 스토리 점수로 진행 속도 예측

진도 측정 - 유저 포인트 수를 추적하는 그래프

 

애자일 프로세스

스프린트 계획 -> 스프린트 -> 스프린트 리뷰 -> 회고를 반복

 

스프린트 계획: 실현하고자 하는 것을 짧은 문장으로 표현한 유저 스토리 집합

스프린트: 스프린트 계획에서 합의한 스토리에 대하여 설계 및 구현, 테스트

데일리 스크럼: 개발자 전원이 수행하는 간단한 회의(작업전 아침실시, 진도확인 및 작업상황,문제점 파악)

스프린트 리뷰: 생성된 결과물을 발주자 측과 확인(구현을 계획한 스토리가 완료되었는지 판단)

스프린트 회고: 스프린트에 대하여 좋았던 일이나 과제 확인, 검토 측정,돌아보기

 

 

 

1. 프로젝트 비전 공유(sw 프로젝트 시작전 중요한일)

- 비전 공유, 역할 분담

- 한팀이 되는 것이 중요(서로 도와주는 것을 소중하게 여김)

 

2. 개발 팀 구성

-  프로덕트 오너(sw 개발 책임자, 명세 결정 권한)

- 스크럼 마스터(스크럼의 리더)

- 팀원(개발 담당)

 

3. 인셉션 덱

- 프로젝트 전체의 배경, 목적, 방향, 가치관에 대한 인식 맞추는 작업

- 짧은 문장 안에 프로젝트에 대한 생각을 담아 개발 작업의 기준으로 삼음

- 10가지 활동을 통한 개념 정립

 

4. 08 스크럼

- 프로덕트 백로그 작성

- 스프린트 계획

- 스프린트 활동

- 스프린트 리뷰

- 스트린트 회고

 

5. 프로덕트 백로그 작성

- 백로그: 프로젝트를 위하여 남은 일

- 우선 순위가 지정된 아직 실행되지 않은 일

- 구현, 버그 수정, 인프라 변경 등 활동 목록

프로덕트 백로그의 변경: 우선순위 항목의 순서는 언제든 바뀔 수 있음, 항목은 언제나 추가 될 수 있음,

항목은 언제든 분할 될 수 있음, 항목은 언제든 삭제될 수 있음

 

6. 유저스토리 매핑

사용자의 행동에 기초하여 sw에 담아야 할 기능을 도출하는 작업

고객 중심 뷰

1. 사용자 행동을 카드 1장에 하나씩 작성

2. 유저 스토리를 시간 순으로 나열

3. 유저 스토리에 제목 지정

4. 기본 기능을 나열

5. 상세한 유저 스토리로 분할

 

7. 유저 스토리 작성

한장의 카드에 who는 what을한다. 왜냐면 why하고 싶으니까

유저 스토리를 시간순으로 정렬(같은 시간에 일어나는 것이 있으면 병렬로 나란히 배치)

 

8. 유저 스토리에 백본(척추)을 지정

9. 기본 기능을 나열

- 각 주제 밑에 필요한 기본 기능(에픽)을 찾아 나열

에픽: 상세화 하면 하나 이상의 유저 스토리가 될 수 있는 기본 기능

 

10. 상세한 스토리로 분할

- 필요한 경우 에픽을 스토리에서 분할

- 또는 에픽이 다른 에픽과 통합

 

11. 스프린트 계획

- 스프린트 주기(1~4주)동안 구현이 가능한 것을 프로덕트 백로그중에서 범위로 정하여 개발 시

12. 스프린트 회의

13. 플래닝 포커

1. 기준 스토리 결정

2. 백로그 아이템 규모 추정

 

14. 추정값 결정

1. 추정할 스토리의 기능요구를 프로덕트 오너가 설명

2. 개발 멤버 전원이 스토리의 개발에 필요한 추정값을 카드에서 선택

3. 만장일치인가?

4. yes라면 추정값으로 결정

5. no라면 불일치하는 추정값을 낸 이유를 듣기시작(그 후 2번으로)

 

15. 스프린트 활동

페어 프로그래밍:

2명씩 1대의 모니터를 사용하여 프로그래밍 작업 수행 ->

코드 작성하는 드라이버, 코딩 가이드하고 리뷰하며 테스트하는 네비게이터가 협동하여 구현 ->

정기적으로 역할 교대

 

16. 데일리 스크럼

- 선채로 15분정도 진행

- 작업의 진행 과정이 아니라 서로의 상황 공유하기 위한 회의

 

17. 태스크 보드

- 스프린트 백로그의 진행 상황이 어떤 상태인지를 나타내기 위한 도구

 

18. 스프린트 리뷰

- 스프린트가 끝난 후에 현재 시점에 완성된 결과물에 대해 검토

 

19. 완료의 정의

20. 프로덕트 백로그 검토

- 다음 스프린트 계획

- 백로그 재검토(우선순위 조정 or 상세화)

- 완료되지 않은 스토리를 전체 스토리 풀에 추가하여 우선순위를 다시 매김

- 우선순위가 높은 스토리가 다음 스프린트의 작업 대상

 

21. 스프린트 회고

- 행동이나 생각 공유 가능하며 팀 성장 촉진

- 문제점 파악 가능

- 3가지 질문(잘된것(계속 해야될것), 개선하여야할것(멈춰야할것), 다음스프린트에서 개선할 사항(시작할것))

22. 회고에 대한 자세

- 적극적으로 임하기

- 개인화 하여 말하지 않기

- 실천 사항에 집중

- 비밀 유지

- 창의적인 마인드

 

 

 

시스템 분석 설계 접근 방법론

구조적 방법론, 객체 지향 방법론으로 구분

 

구조적 방법론:

시스템의 대표적인 기능 찾아내고 대표기능 수행하기 위한 서비스기능을 찾아가는 방법(하향식)

예시: 사람의 구조적인 분석

대표기능 밥먹기: 서브기능(이빨로 자른다 > 목구멍으로 넘긴다 > 식도를 타고 내려간다 > 위에서 녹인다 >

장에서 흡수한다 > 항문에서 배출한다)

서브기능: 장에서 흡수한다(세부기능: 모세혈관이 흡수한다 > 심장이 보낸다 > 동맥을 통해 근육과 내장기관에 보낸다)

 

구조적 분석 기법:

- 자료 흐름도(DFD):

외부객체(시스템 외부에서 시스템과 정보 주고받는 사용자등 외부객체), 프로세스(시스템안에서 정보 처리,변환),

데이터 항목(프로세스 사이의 정보 흐름), 자료 저장소(데이터 베이스)

특징: 도형 중심 표현, 하향식, 다차원적, 제어흐름 중요시 하지않음

효과: 요구사항 쉽게 문서화, 사용자와 분석가 사이 공용어 역할, 일관성 있음

4가지 구성요소: 처리(입력되는 자료흐름을 출력되는 자료흐름으로 변환), 자료흐름(자료흐름도에서 구성요소들 간의 접속관계),

자료 저장소(머무는 자료군의 집합), 단말(상세한 자료흐름도 이해할수있게 사각형의 단말)

자료흐름도 작성 7가지 원칙:

- 자료 보존의 원칙: 어떤 처리의 출력 자료흐름은 반드시 입력 자료흐름을 이용해 생성.

- 최소 자료 입력의 원칙: 어떤 처리가 출력 자료흐름을 산출하는데 반드시 필요로 하는 최소의 자료흐름만 입력

- 독립성의 원칙: 자기의 처리는 오직 자신의 입력 자료와 출력 자료 자체에 대해서만 알면됨.

- 지속성의 원칙: 자료 흐름 기다릴때 제외하고는 다시 시작,멈추면안됨

- 순차 처리의 원칙: 처리에 입력되는 자료흐름의 순서는 출력되는 자료흐름에서도 지켜져야함.

- 영구성의 원칙: 자료저장소의 자료는 제거되지 않아야함

- 자료 변환의 원칙: 자료 본질의 변환

작성 절차:

- 시스템 경계의 입출력 식별

- 시스템 경계 내부의 작성

- 자료흐름의 명명

 

 

- 소단위 명세(mini-spec):

분할이 완료된 자료 흐름도의 프로세스가 어떤 기능을 수행하는지 기술한 것

더이상 쪼개지지않는 최하위 프로세스를 설명함.

 

- 자료사전(DD):

자료 흐름도에 나타난 데이터 정보를 모아놓음으로써 개발자나 사용자들이 편리하게 사용할 수 있도록 한것

 

 

객체 지향 방법론:

시스템의 구성 요소들을 찾아내고 구성 요소들간의 연관성을 증명해내는 과정(상향식)

예시: 사람의 객체지향적인 분석

 

객체 모델링:

뇌, 척수,.. / 눈,코,귀... / 이빨,목구멍,식도,장,위,.... / 근육,내장기관,....

동적 모델링:

음식물: 초기상태 > 잘린상태 > 녹은 상태 > 흡수상태 > 배출상태

기능 모델링:

[초기 상태] 이빨로 자른다 > [잘린 상태] 위로 전달해 위액으로 녹인다 > [녹은 상태] 소장으로 전달한다 >

[흡수 상태] 대장으로 전달한다 > [배출 상태]

 

객체지향적 분석 기법:

- 유스케이스:

고객의 요구사항을 알아내는데 효과적이다.(이해 관계자 찾아내고 역할에 따라 나누어 엑터로분류)

각 use case에 대하여 시나리오를 만들고 상호작용하는 환경, 배경등을 표현 가능

 

- 정보 모델링:

UML의 다이어그램으로 나타내어 클래스의 속성과 관계만 표현된 다이어그램 얻어낼 수 있다.

 

-동적 모델링:

객체의 상태나 동작의 변화 혹은 객체 사이의 상호작용에 관심을 두고 오퍼레이션을 찾는 과정

 

-기능 모델링:

처리과정을 다이어그램으로 표현

 

 

시스템 분석 설계 접근 방법론의 특징:

 

구조적 방법론:

상하관계가 명확하고 결합도가 강하므로 어떻게 처리가 진행되는지 따라가기 쉬워서 사소한 기능 수정에 매우 편리

하지만 새로운 기능 추가하거나 재활용 하려면 각 기능을 뜯어고쳐내야해서 상당한 노력 소모

 

객체지향 방법론:

개체는 독립적(응집성 높고, 결합도 낮음)이라 개별적인 객체의 작동 파악 쉬움

하지만 전체의 작동원리 알기 위해서객체간 연관관계에 대한 이해가 필요해서 객체간 관계정립없이 작업 진행 어려움

하지만 객체 요소들이 독립적이라 새 기능 추가하거나 재활용 할때는 기존 구조에 영향 안주고 수정 가능

 

구조적 방법론의 개요:

도형화된 도구를 이용해 정형화된 분석 절차에 따라 사용자 요구사항을 파악하고 문서화하는 분석 기법

사용하는 도구로는 자료흐름도, 자료사전, 소단위 명세서등이 대표적

 

구조적 분석의 기본원리: 추상화원칙, 정형화원칙, 분할 정복, 계층적 구조의 개념

 

모형화 도구를 사용하는 이유?

비용 감소, 위험도 최소화, 시스템분석가가 제대로 이해하고 문서화했는지 검증 가능, 낮은 비용으로 모형 구축,

쉽게 이해가능, 생각 정형화 가능

 

모형화 도구 특성

도형적 모형: 시스템을 설명할때 텍스트보다 도형을 통해 더 잘 설명 가능

하향식 분할 모형: 각각의 구성 부분을 독자적으로 표시하고, 시스템 모형을 한부분에서 다른 부분으로 간단히 연결할 수 있어야댐

최소중복모형, 투명적 모형, 다양한 모형

 

구조적 분석의 4단계 절차:

- 현 물리적 모형화 (업무 수행 절차 및 환경을 있는 그대로 모형화)

- 현 논리적 모형화 (의존적인 물리적 특성 제거해 모형화)

- 신 논리적 모형화 (새로운 시스템에서 수행될 모든 기능, 필요 자료에 대한 모형 구축)

- 신 물리적 모형화 (현실적 물리적 환경 감안해 최종 모형 제시)

10장 배열

배열 선언

int scores[10];

배열의 가장 큰 장점은 반복문을 사용하여서 배열의 원소를 간편하게 처리할 수 있음

 

score[0] = 0;

score[1] = 0;

score[2] = 0;

score[3] = 0;

score[4] = 0;

 

이래되는걸

 

#define SIZE 5

...

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

    score[i] = 0;

 

이래버리면됨.

 

배열의 초기화

이런식으로 초기값을 일부만 주면 나머지 원소들은 0으로 초기화됨.

배열의 초기화가 주어지지 않은 경우에는 초기값 개수가 배열의 크기가됨.

 

배열의 이름: 상수주소

int scores[] = { 10, 20, 30, 40, 50, 60 };

int i, size;

size = sizeof(scores) / sizeof(scores[0]);

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

        printf("%d ", scores[i]);

 

배열의 크기와 원소들을 알고싶다면 이런식으로 하면됌

sizeof(scores)를하면 24가 나옴 배열의 크기는 6이고 int형이니 4 * 6 하면 24

그리고 여기에 sizeof(scores[0])를하면 원소하나의 크기가 나옴(int)

그래서 이렇게 두개를 나누면 배열의 크기가 나오고 이 크기를 반복문을 돌려서 원소들을 배열에서 하나씩

볼수있음.

 

근데 배열을 복사할때는

int score1[SIZE];

int score2[SIZE];

score1 = score2;  

이런식으로 복사하면 컴파일 오류가 나게됨

 

int score1[SIZE];

int score2[SIZE];

int i;

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

         score1[i] = score2[i]; 

그래서 이렇게 반복문을 통해서 일일이 다 복사해야됌.

 

배열을 비교할때도 마찬가지로 이렇게 배열을 통째로 한번에 비교하려고하면 에러가남.

#include <stdio.h>

#define SIZE 5

int main(void)

{

        int i;

        int a[SIZE] = { 1, 2, 3, 4, 5 };

        int b[SIZE] = { 1, 2, 3, 4, 5 };

        if( a == b )                  // ① 올바르지 않은 배열 비교

                printf(올바른 결과입니다.\n");

        else

                printf("잘못된 결과입니다.\n");

 

그래서 이렇게 하는게아니라 아까 배열 복사처럼 일일이 반복문을 통하여 비교를 해야됌

for (i = 0; i < SIZE ; i++) // ② 올바른 배열 비교

{

  if ( a[i] != b[i] )  {

  printf("a[]b[]는 같지 않습니다.\n");   

    break;

  }  

}

if (i>=SIZE)

  printf("a[]b[]는 같습니다.\n");

 

그렇다면 배열로 최소값 찾기를 해보자

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#define SIZE 5

int main(void)

{

  int prices[SIZE] = { 0 };

  int i, minimum;

   srand( (unsigned)time( NULL ) );

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

    prices[i] = (rand()%100)+1

  minimum = prices[0];

  for(i = 1; i < SIZE; i++) {

  if( prices[i] < minimum )

  minimum = prices[i];

  }

  printf("최소값은 %d입니다.\n", minimum);

  return 0;

}

최소값을 찾기전 우선 배열에 랜덤값을 다 넣어줘야해서 srand 함수를 사용하여 배열에 랜덤값들을 다 넣어준다.

그 후 미니멈 변수를 선언해주고 초기값은 배열의 첫값으로 넣어준다. 그 후 배열의 크기를 미리 설정해둔 전역변수

size로 반복문을 실행하여서 미니멈값이랑 배열값이랑 비교를해서 더 작은값을 미니멈에 넣어준다.

 

#include <stdio.h>

#define STUDENTS 5

int get_average(int scores[], int n);     // ①

int main(void)

{

        int scores[STUDENTS] = { 1, 2, 3, 4, 5 };

        int avg;

        avg = get_average(scores, STUDENTS);

        printf("평균은 %d입니다.\n", avg);

        return 0;

}

int get_average(int s[], int n)      // ②

{

        int i;

        int sum = 0;

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

                sum += s[i];

        return sum / n;

}

이런식으로 평균을 구해주는 함수를보면 배열의 주소가 s[]로 전달되는걸 볼 수 있음.

 

변수의 값을 서로 교환할때는 어떻게 해야될까?

우리가 원래 알던방식인

int x= 1, y=2;
다음과 같이 하면 안됨
x = y;// x기존값은 파괴된다!
y = x;
 
올바른 방법
int x= 1, y=2, temp;
temp = x;
x = y;
y = temp;

이런식으로 변수의 값을 교환해주면됨.

배열에서는?

 

다음과 같이 하면 안됨

 

score[i] = score[least];

         // score[i]기존값은 파괴된다!

score[least] = score[i];

 

올바른 방법

 

temp = list[i];

list[i] = list[least];

list[least] = temp;

이런식으로 배열도 마찬가지로 해주면됨.

 

순차탐색

배열에서 순차탐색을 할땐 우선 배열의 크기를 알아야하고 이 크기를 통하여 사용자에게 탐색할

변수를 입력받은후 반복문을 통하여 배열안의 값과 사용자에게 받은 값을 비교하여서 순차적으로 

탐색하는것이 순차탐색임.

그냥 말그대로 처음부터 탐색한다는 뜻

 

#include <stdio.h>

#define SIZE 10

int main(void)

{

  int key, i;

  int list[SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

  printf("탐색할 값을 입력하시오:");

  scanf("%d", &key);

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

  if(list[i] == key)

    break;

  if (i<SIZE)

    printf("탐색 성공 인덱스= %d\n", i);

  else

  printf("탐색 실패\n");

  return 0;

}

 

이진탐색

이진탐색은 쉽게말하자면 정렬된 배열의 중앙에 위치한 원소와 비교 되풀이하는 것임.

이 이진 탐색 함수는 파라미터로 배열과 배열의 크기와 탐색할 값을 받아서 이루어짐.

int binary_search(int list[], int n, int key)

{

  int low, high, middle;

  low = 0;

  high = n-1;

  while( low <= high ){   // 아직 숫자들이 남아있으면

  printf("[%d %d]\n", low, high);  // 하한과 상한을 출력한다.

  middle = (low + high)/2;  // 중간 위치를 계산한다.

  if( key == list[middle] )  // 일치하면 탐색 성공

  return middle;

  else if( key > list[middle] )        // 중간 원소보다 크다면

  low = middle + 1;  // 새로운 값으로 low 설정

  else

  high = middle - 1;  // 새로운 값으로 high 설정 

  }

  return -1;

}

 

 

2차원 배열

int s[10];       // 1차원 배열

int s[3][10];    // 2차원 배열

int s[5][3][10]; // 3차원 배열

2차원 배열 선언

#include <stdio.h>

#include <stdlib.h>

#define ROWS 3

#define COLS 5

int main(void)

{

  int s[ROWS][COLS]; // 2차원 배열 선언

  int i, j; // 2개의 인덱스 변수

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

  for (j = 0; j < COLS; j++)

  s[i][j] = rand() % 100;

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

  {

  for (j = 0; j < COLS; j++)

  printf(" % 02d ", s[i][j]);

  printf("\n");

  }

  return 0;

}

이런식으로 랜덤 수를 2차원 배열로 들어가게끔해서 2차원 배열을 선언할 수 있음

int s[3][5] = { //int s[][5];

  {  0,  1,  2,  3,  4 }, // 첫 번째 행의 원소들의 초기값

  { 10, 11, 12, 13, 14 }, // 두 번째 행의 원소들의 초기값

  { 20, 21, 22, 23, 24 } // 세 번째 행의 원소들의 초기값

};

이런식으로도 가능!

 

2차원 배열을 함수로 전달하는 방법은?

#include <stdio.h>

#define  YEARS   3

#define  PRODUCTS   5

int sum(int grade[][PRODUCTS]);

int main(void)

{

  int sales[YEARS][PRODUCTS] =

    { {1, 2, 3},

      {4, 5, 6},

                   {7, 8, 9} };

  int total_sale;

  total_sale = sum(sales);

  printf("총매출은 %d입니다.\n", total_sale);

  return 0;

}

 

int sum(int grade[][PRODUCTS])

{

  int y, p;

  int total = 0;

  for(y = 0; y < YEARS; y++)

  for(p = 0; p < PRODUCTS; p++)

  total += grade[y][p];

  return total;

}

이런식으로 sum이라는 함수에서 파라미터로 배열을 받아서 total에 모든 배열의 값들을 다 더한값을 넣어주면서

그 값을 리턴하여 배열에 있는 모든 값들의 합을 알 수 있다.

 

 

11장 포인터

포인터: 주소를 가지고 있는 변수

변수는 메모리에 저장됨. 메모리는 바이트 단위로 엑세스됨.

변수의 크기에 따라서 차지하는 메모리 공간이 달라짐.(char형(1바이트), int형(4바이트))

int main(void)

{

  int i = 10;

  char c = 69;

  float f = 12.3;

}

변수의 주소를 계산하는 연산자: &

변수 i의 주소: &i

int main(void)
{
	int i = 10;
	char c = 69;
	float f = (float)12.3;

	printf("i의 주소: %u\n", &i);	    // 변수 i의 주소 출력, %p
	printf("c의 주소: %u\n", &c);  // 변수 c의 주소 출력
	printf("f의 주소: %u\n", &f);   // 변수 f의 주소 출력
	return 0;
}

이런식으로 변수의 주소를 출력하게되면

이런식으로 값이 나오게됨.

 

포인터는 변수의 주소를 가지고 있는 변수임.

포인터의 선언

 

&는 주소연산자임.

*는 간접 참조 연산자임(포인터가 가리키는 값을 가져옴)

 

int i = 10;  // 정수형 변수 i 선언

int *p;  // 포인터 변수 p 선언

p = &i;    // 변수 i의 주소가 포인터 p대입

printf(“%d, %d\n”, i, *p); //10, 10

변수 i의 주소가 포인터 p로 대입이되었다.

출력해보면 변수 i의 값은 10이고, 포인터변수 p에 *를 붙힌 *p는 p가 가리키는 값을 가져오니 p는

i를 가리키니 i인 10이 출력된다.

 

printf(“%u, %u, %u\n”, &i, p, &p); //4, 4, 12

&i는 i의 주소 4이고,

p는 아까 변수 i의 주소가 p로 대입되었으니 마찬가지로 4이다.

&p는 포인터변수 p의 주소이므로 그림에 나온거처럼 12이다.

 

 

char c = 'A';    // 문자형 변수 c

float f = 36.5;    // 실수형 변수 f

double d = 3.141592;  // 실수형 변수 d

char *pc = &c;    // 문자를 가리키는 포인터 pc

float *pf = &f;    // 실수를 가리키는 포인터 pf

double *pd = &d;    // 실수를 가리키는 포인터 pd

 

#include <stdio.h>

int main(void)
{
	int i = 10;
	double f = 12.3;
	int *pi = NULL;
	double *pf = NULL;

	pi = &i;
	pf = &f;

	printf(“%u %u\n”, pi, &i);
	printf(“%u %u\n”, pf, &f);
	return 0;
}

pi: 변수 i의 주소값이 출력됌

&i: 변수 i의 주소값이 출력됌

pf: 변수 f의 주소값이 출력됌

&f: 변수 f의 주소값이 출력됌

 

*pi : 변수 i의 값인 10이 출력됌

*pf: 변수 f의 값인 12.3이 출력됌

 

&pi: 포인터 변수 pi의 주소값이 출력됌

&pf: 포인터 변수 pf의 주소값이 출력됌

 

int i;
char c;
double d;

int *p= &i;
char *pc= &c;
double *pd= &d;

 

int *p = &i;가 뭐랑 같냐면

p = &i; 이거랑 똑같음 (헷갈 ㄴㄴ)

 

 

*p = 8;  // 변수 i8을 저장 (4 byte)

*pc = 8;  // 변수 c8저장 (1 byte)

*pd = 8;  // 변수 d8저장 (8 byte)

 

*p는 i의 값인데 *p에 8을 넣으면 마찬가지로 i에 8이 들어감.

*pc는 문자열c의 값인데 *pc에 8을 넣으면 마찬가지로 문자열 c에 8이 들어감.

*pd는 실수형 d의 값인데 *pd에 8을 넣으면 d에 8이 들어감.

 

#include <stdio.h>

int main(void)
{
	int i = 3000;
	int *p=NULL;	

	p = &i;	
	
	printf("i = %d\n", i);	// 변수의 값 출력
	printf("&i = %u\n", &i);	// 변수의 주소 출력

	printf("p = %u\n", p);	
	printf("*p = %d\n", *p);	
			
	return 0;
}

여기서 뭐가 출력되는지 한번 봐보자

i: 3000 출력

&i: 변수i의 주소값 출력

p: 변수 i의 주소값 출력

*p: 변수 i의 값인 3000출력

 

그냥 쉽게 말하자면 포인터 변수에 *붙혀져있으면 포인터가 가리키는 변수의 값이고

*안붙혀져있으면 포인터가 가리키는 변수의 주소값임

 

#include <stdio.h>

int main(void)
{
	int x=10, y=20;
	int *p;

	p = &x;
	printf("p = %d\n", p);
	printf("*p = %d\n\n", *p);

	p = &y;
	printf("p = %d\n", p);
	printf("*p = %d\n", *p);
	return 0;
}

p=&x; 일때

 p: 변수 x의 주소값

*p: 변수 x의 값인 10

 

p=&y; 일때

p: 변수 y의 주소값

*p: 변수 y의 값인 20

 

 

int x= 10;

float *p= &x; //경고 발생

*p= 20;

printf("%d, %f\n", x, *p);

근데 이런식으로 형 안맞추고 써버리면 경고발생함

 

#include <stdio.h>
int main(void)
{
	int i=10;
	int *p;

	p = &i;
	printf("i = %d\n", i);

	*p = 20;
	printf("i = %d\n", i);
	return 0;
}

p=&i;일때 

i: i의 값인 10 출력

 

*p = 20을 하면

*p는 변수 i의 값임 근데 여기에 20을 대입해준거니까 결론적으로는

i=20이랑 같은말임. 즉 이후에 i의 값을 출력하면 20을 넣었으니

20이 출력됌.

(이런식으로 포인터를 통하여 변수의 값 변경할 수 있음)

 

포인터 사용시에는 초기화가 안된 포인터를 사용하면 안됨.

int main(void) 
{ 
	int *p; 	// 포인터 p는 초기화가 안되어 있음
	*p = 100; 	// 위험한 코드, 컴파일시 경고만 발생하고 실행시 오류 발생
	return 0;
}

포인터p가 가르키는 위치가없음

그래서 포인터가 아무것도 가리키고 있지 않은 경우에는 NULL로 초기화 시켜줘야함.

int *p = NULL;

  if (p!=NULL) //pNULL이 아닌 것을 확인 후 사용 

 

포인터 타입과 변수의 타입은 일치해야함.

 

포인터가 가능한 연산은 증가, 감소, 덧셈, 뺄셈 연산임.

자기 크기만큼 증가됌

#include <stdio.h>
int main(void)
{
	char *pc; 
	int *pi;
	double *pd;

	pc = (char *)10000;			
	pi = (int *)10000;			
	pd = (double *)10000;			
	printf("증가 전 pc = %d,  pi = %d,  pd = %d\n", pc, pi, pd);

	pc++;
	pi++;
	pd++;
	printf("증가 후 pc = %d,  pi = %d,  pd = %d\n", pc, pi, pd);
	printf(“pc+2 = %d, pi+2 = %d, pd+2 = %d\n”, pc+2, pi+2, pd+2);
	return 0;
}

 

포인터를 초기화 할땐 이렇게 하나봄

pc = (char *)10000;

암튼 이렇게하면 초기화가되고 출력해주면 초기값인 10000이 나옴

근데 이제 ++연산 후에 값들을 출력해주면

증가후:

pc: 10001, pi: 10004, pd: 10008

 

*p++: p가 가리키는 위치에서 값을 가져온후! p를 증가한다.

(*p)++: p가 가리키는 위치의 값을 증가한다

 

 

그렇다면 이걸 이해했으면 다음을 해석해보자

#include <stdio.h>

int main(void)
{
	int i = 10, j;
	int *pi = &i;

	printf("i = %d,  pi = %p\n", i, pi);
	(*pi)++;	
	printf("i = %d,  pi = %p\n", i, pi);

	printf("i = %d,  pi = %p\n", i, pi);
	j= *pi++; //j= *pi; pi++;	
	printf("i = %d, j = %d, pi = %p\n", i, j, pi);

	return 0;
}

i: 변수 i의 값인 10 출력

pi: 포인터변수 pi가 가리키는 변수i의 주소값 출력

(*pi)++; 연산후 // 포인터 변수 pi가 가리키는 값에 ++ 연산을 하여라 라는뜻

i: 변수 i가 가리키는 값 +1한 값인 11 출력

pi: 아까와 동일하게 포인터변수 pi가 가리키는 변수i의 주소값 출력

j= *pi++; //j= *pi; pi++; 연산후 // 포인터 변수 pi가 가리키는 값을 j에 대입한후, pi가 가리키는 값인 변수 i의 주소값에 ++연산을 취하여라 이때 ++연산은 1을 더해지는게 아니라 포인터변수의 형만큼 더해짐 얜 지금 Int 형(4바이트)이니 4가 더해짐)

i: 아까와 동일하게 11출력

j: 포인터 pi가 가리키는 값인 변수 i의 값을 가져온거니 11출력

pi: 포인터 변수 pi가 가리키는 변수i의 값을 가져온후 j에 대입하고 난뒤 pi의 값에 ++연산을 한것이니

변수 i의 주소값 + int형(4바이트) 출력

 

포인터의 형변환

c언어에서는 꼭 필요한 경우에 명시적으로 포인터의 타입을 변경할 수 있지만 권장하진않음.

 

#include <stdio.h>

int main(void)
{
	int data = 0x0A0B0C0D;
	char *pc;
	pc = (char *)&data;
	
	for (int i = 0; i < 4; i++) {
		printf(“*(pc + %d) = %02X \n”, i, *(pc + i));
	}
	return 0;
}

이것의 출력값

*(pc + 0) = 0D

*(pc + 1) = 0C

*(pc + 2) = 0B

*(pc + 3) = 0A

 

인수 전달 방법

함수 호출 시에 인수 전달 방법
1.값에 의한 호출 (복사본이 전달됨)
2. 참조(주소)에 의한 호출 (원본이 전달됨, 포인터 이용)
 
#include <stdio.h>
void swap(int x, int y);
int main(void)
{
	int a = 100, b = 200;
	printf(“a=%d b=%d\n”,a, b);

	swap(a, b);

	printf(“a=%d b=%d\n”,a, b);
	return 0;
}
void swap(int x, int y)
{
	int tmp;
	printf(“x=%d y=%d\n”,x, y);

	tmp = x;
	x = y;
	y = tmp;

	printf(“x=%d y=%d\n”,x, y);
}
출력값
a: 100, b: 200
swap(a, b);연산

x: 100, y: 200

x:200, y: 100

swap(a, b);연산후

a: 100, b:200

스왑 함수를 사용하였음에도 불구하고 ab를 다시 출력하였을때는 값이 안바뀜

왜? 이게 아까 말한 값에 의한 호출임 (복사본을 전달)

호출된 함수의 결과가 호출하는 함수로 전달이안됌.

 

그렇다면 이제 참조(주소)에 의한 호출을 살펴보자

#include <stdio.h>
void swap(int x, int y);
int main(void)
{
	int a = 100, b = 200;
	printf(“a=%d b=%d\n”,a, b);

	swap(&a, &b);

	printf(“a=%d b=%d\n”,a, b);
	return 0;
}
void swap(int *px, int *py)
{
	int tmp;

	tmp = *px;
	*px = *py;
	*py = tmp;

}

출력값

a: 100, b: 200

swap(&a, &b); 연산후

a: 200 b: 100

이렇게 참조(주소)에 의한 호출 을 사용하면 값이 바뀜 포인터에 의한 호출은 주소를 전달하기 때문

 

#include <stdio.h>
// 기울기와 y절편을계산
int get_line_parameter(int x1, int y1, int x2, int y2, float *slope, float *yintercept)
{
	if( x1 == x2 ) 
		return -1;
	else {
	  *slope = (float)(y2 - y1)/(float)(x2 - x1);
	  *yintercept = y1 - (*slope)*x1;
	  return 0;
	}
}
int main(void)
{
	float s, y;
	if( get_line_parameter(3,3,6,6,&s,&y) == -1 )
		printf("에러\n");
	else 
		printf("기울기는 %f, y절편은 %f\n", s, y);
	return 0;
}

이건 두점의 좌표를 받아서 기울기를 계산해서 출력해주는 함수인데 이렇게 참조에의한 호출을 사용하면 값이

아예 바뀜.

 

포인터와 배열

배열의 이름이 포인터임

포인터는 배열처럼 사용이 가능함

#include <stdio.h>

int main(void)
{
	int a[] = { 10, 20, 30, 40, 50 };

	printf("&a[0] = %u\n", &a[0]); 
	printf("&a[1] = %u\n", &a[1]); 
	printf("&a[2] = %u\n", &a[2]); 

	printf("a = %u\n", a); 

	return 0;
}

출력값: 

&a[0] = 1245008

&a[1] = 1245012 

&a[2] = 1245016 

a = 1245008

 

#include <stdio.h>

int main(void)
{
	int a[] = { 10, 20, 30, 40, 50 };
	printf(“a = %u\n”, a);
	printf(“a + 1 = %u\n”, a + 1);
	printf(“*a = %d\n”, *a);
	printf(“*(a+1) = %d\n”, *(a+1));
	return 0;
}

이것의 출력값은 

a = 1245008

a + 1 = 1245012

*a = 10  // *a==a[0]

*(a+1) = 20  // *(a+1)==a[1]

이것이다 출력값을 자세히 살펴보자

배열 a를 출력하면 배열 a의 주소값이 나온다

그리고 a+1을 출력하면 int형 배열답게 4가 추가된 주소값이 나온다

근데 포인터형을 붙힌 *a를 출력해보면 배열의 첫번째 값이 나온다

그리고 포인터형을 붙인 *(a+1)을 출력해보면 배열의 두번째 인덱스 값이 나온다.

아까 처음에 언급한말인 배열의 이름이 포인터임 라는 말을 잘 살펴보면 배열의 이름이 포인터라는 말이 이해가

갈것이다. 배열에 이름에 포인터를 붙혀야 배열이 가리키는 값을 출력해주니까

int *p = a 를 하면 이렇게 포인터와 연결이된다

 

#include <stdio.h>
int main(void)
{
	int a[] = { 10, 20, 30, 40, 50 };
	int *p;

	p = a;
	printf("a[0]=%d a[1]=%d a[2]=%d \n", a[0], a[1], a[2]);
	printf("p[0]=%d p[1]=%d p[2]=%d \n\n", p[0], p[1], p[2]);

	p[0] = 60;
	p[1] = 70;
	p[2] = 80;

	printf("a[0]=%d a[1]=%d a[2]=%d \n", a[0], a[1], a[2]);
	printf("p[0]=%d p[1]=%d p[2]=%d \n", p[0], p[1], p[2]);
	return 0;
}

출력값:

a[0] = 10, a[1] = 20, a[2] = 30

p[0] = 10, p[1] = 20, p[2] = 30

 

a[0] = 60, a[1] = 70, a[2] = 80

p[0] = 60, p[1] = 70, p[2] = 80

 

일반 매개변수 vs 배열 매개변수 

배열 매개변수는 포인터로 생각할 수 있다

배열의 이름은 포인터이며 함수에서 포인터를 이용하여 원본 배열을 변경할 수 잇음.

// 포인터와 함수의 관계
#include <stdio.h>

void sub(int b[], int n);

int main(void)
{
	int a[3] = { 1,2,3 };
	
	printf("%d %d %d\n", a[0], a[1], a[2]);
	sub(a, 3);
	printf("%d %d %d\n", a[0], a[1], a[2]);
	
	return 0;
}

void sub(int b[], int n)
{
	b[0] = 4;
	b[1] = 5;
	b[2] = 6;
}

출력값

1 2 3

sub(a, 3); 함수 실행후

4 5 6

 

다음 두가지 방법은 완전히 동일함

포인터가 인덱스 표기법보다 빠른 이유는?

인덱스를 주소로 변환할 필요가 없다

이렇게 배열에서 값을 가져올때 포인터로하면 즉각적으로 가져와지는데 배열인덱스로 하려면 인덱스를

주소로변환해야됌

 

단 지역변수의 주소를 반환하면 함수가 종료되어 사라지기 때문에 오류가남

 

영상처리

영상의 모든 픽셀의 값을 10씩 올려주는 코드

void brighten_image(int image[][SIZE])
{
	int r,c;
	int *p;
	p = &image[0][0]; //p= image;
	for(r=0; r<SIZE; r++){
		for(c=0; c<SIZE; c++){
			*p += 10;
			p++;
		}
	}
}
int image[5][5] = {
		{ 10, 20, 30, 40, 50},
		{ 10, 20, 30, 40, 50},
		{ 10, 20, 30, 40, 50},
		{ 10, 20, 30, 40, 50},
		{ 10, 20, 30, 40, 50}};

보면 포인터로 진행하는데 배개면수로 2차원 배열을 받아서 포인터변수 p에 배열의 주소를 할당하여서 배열의 크기

만큼 반복문을 돌려서 p에 10씩 더해주고 p스스로 ++연산을 하여 앞으로 가게 해준다.

 

포인터 사용의 장점

연결리스트나 이진트리등의 향상된 자료구조를 만들 수 있다.

포인터를 매개변수로 이용하여 함수 외부의 변수의 값을 변경할수있다.

 

문자열

문자열 표현 방법

 

문자열: 문자들이 여러개 모인 것(하나의 문자는 char형 변수로 저장)

문자열 변수: 변경 가능한 문자열을 저장할 수 있는 변수

NULL 문자('\0'): 문자열의 끝을 나타낸다.
문자열은 어디서 종료되는지 알수가 없으므로 표시를 해주어야한다.
#include <stdio.h>
int main(void)
{	
	int i;
	char str[4];
	str[0] = 'a';
	str[1] = 'b';
	str[2] = 'c';
	str[3] = '\0';		
	
	i = 0;
	while(str[i] != '\0')	{
		printf("%c", str[i]);		
		i++;
	}
	return 0;
}

이렇게 배열에 있는 문자들을 하나씩 출력해보자(NULL)문자가 나오면 반복을 종료하도록함.

출력결과: abc

 

문자배열의 초기화

문자열 출력

char str[] = “abc”;

printf(“%s”, str);

이렇게 %s로 출력하면됌

char str[] = “abc”;

printf(str);

이렇게도 가능

#include <stdio.h>

int main(void)
{
	char str1[6] = "Seoul";
	char str2[3] = { ‘i’, ‘s’ , ‘\0’};
	char str3[] = "the capital city of Korea.";
	printf("%s %s %s\n", str1, str2, str3);
	return 0;
}

출력결과: Seoul is the capital city of korea

 

#include <stdio.h>
int main(void)
{
	char src[] = "The worst things to eat before you sleep";
	char dst[100];	
	int i;
	printf("원본 문자열=%s\n", src);
	for(i=0 ; src[i] != ‘\0’ ; i++)
		dst[i] = src[i];	
	dst[i] = ‘\0’;
	printf("복사된 문자열=%s\n", dst);
	return 0;
}

출력결과: 원본 문자열=The worst things to eat before you sleep

               복사된 문자열=The worst things to eat before you sleep

 

문자열 길이 계산하기

// 문자열의 길이를 구하는 프로그램
#include <stdio.h>

int main(void)
{
	char str[30] = "C language is easy";
	int i = 0;
	
	while(str[i] != 0)
		i++;

	printf("문자열\"%s\"의 길이는 %d입니다.\n", str, i);

	return 0;
}

출력결과: 문자열 "C language is easy" 의 길이는 18입니다.

 

이런식으로 문자 배열의 각각의 원소를 개별적으로 변경하는 방법도 있지만

strcpy()를 이용하여 문자열을 문자 배열에 복사하는 방법도있음

strcpy(str, "World");

이런식으로

str배열에 저 단어를 넣는다는 의미

 

문자열 상수: HelloWorld 같이 프로그램 소스 안에 포함된 문자열

문자열 상수는 메모리 영역중에서 상수 영역에 저장됨.

char *p = "HelloWorld";

이건 무슨 의미일까?

데이터 세그먼트: 값을 변경할 수 있는 메모리 영역(스택영역)

텍스트 세그먼트: 값을 읽기만하고 변경할 수는 없는 메모리 영역(상수 영역)

즉 저 p를 통해서 텍스트 세그먼트에 새로운 값을 저장하려고하면 오류가 생김

 

#include <stdio.h>

int main(void)
{
	char *p = "HelloWorld";
	printf("%s \n", p);

	p = "Welcome to C World!"; // 가능
	printf("%s \n", p);

	p = "Goodbye"; // 가능
	printf("%s \n", p);
	// p[0] = ‘a’; // 오류가 발생한다.

	return 0;
}

출력값:

HelloWorld

Welcome to C World!

Goodbye

이런식으로는 가능

 

문자 입출력 라이브러리

getchar()의 사용

// getchar()의 사용
#include <stdio.h>
int main(void)
{
	int ch;		// 정수형에 주의
	while( (ch = getchar()) != EOF )
		putchar(ch);	
	return 0;
}

출력결과:

여기서 EOF는 end of file을 나타내는 문자.

 

_getch(), _putch()의 사용

 

#include <stdio.h>
#include <conio.h>

int main(void)
{
	int ch;
	while( (ch = _getch()) != 'q' )
		_putch(ch);
	return 0;
}

이건 버퍼를 사용하지않아서 엔터 안눌러도 입력하면 자동으로출력됌

출력값:

abc를 입력했으면 abc

 

비교

버퍼가없이 바로 받으려면 _getch()사용해야함.

 

문자 입출력 라이브러리 함수

 

gets_s()puts() 문자열 입출력

char buf[100];
gets_s(buf, 100); //사용자로부터 한줄을 입력받는다. 최대 입력개수는 100
puts(buf); //한줄을 출력한다.
#include <stdio.h>
int main(void)
{
	char name[100];
	char address[100];
	printf("이름을 입력하시오: ");
	gets_s(name, 100); //한줄 단위(여러단어)로 입력받을때 사용한다.
	printf("현재 거주하는 주소를 입력하시오: ");
	gets_s(address, 100);
	puts(name);
	puts(address);
	return 0;
}

출력값:

이름을 입력하시오: 홍길동

현재 거주하는 주소를 입력하시오: 서울시 종로구 100번지

 

홍길동

서울시 종로구 100번지

 

문자열 복사하기 (strcpy)

char dst[6];

char src[6] = "Hello";

strcpy(dst,src);

이렇게 하면 문자열이 복사됌

 

문자열 연결하기 (strcat)

char dst[12]="Hello";

char src[6] = "World";

strcat(dst,src);

//이렇게하면 dst가 "HelloWorld"가 됌.

 

// strcpy와 strcat
#include <string.h>
#include <stdio.h>

int main( void )
{
	char string[80];

	strcpy( string, "Hello world from " ); 
	strcat( string, "strcpy " );           
	strcat( string, "and " );              
	strcat( string, "strcat!" );          
	printf( "string = %s\n", string );
	return 0;
}

출력값: string = Hello world from strcpy strcat!

 

문자열 비교 (strcmp)

int result = strcmp("dog","dog");

//strcmp는 문자열 s1,s2를 비교하여 사전적인 순서에서 s1이 앞에있으면 음수가 반환되고, 같으면 0 

뒤에있으면 양수가 반환된다. (문자열이 같으면 strcmp는 0을 반환함.)

 

// strcmp() 함수
#include <string.h>
#include <stdio.h>

int main( void )
{
	char s1[80];	// 첫번째 단어를 저장할 문자배열
	char s2[80];	// 두번째 단어를 저장할 문자배열
	int result;

	printf("첫번째 단어를 입력하시오:");
	scanf("%s", s1);
	printf("두번째 단어를 입력하시오:");
	scanf("%s", s2);
    result = strcmp(s1, s2);
	if( result < 0 )
		printf("%s가 %s보다 앞에 있읍니다.\n", s1, s2);
	else if( result == 0 )
		printf("%s가 %s와 같습니다.\n", s1, s2);
	else 
		printf("%s가 %s보다 뒤에 있습니다.\n", s1, s2);
	return 0;
}

출력값: 

첫번째 단어를 입력하시오: Hello

두번째 단어를 입력하시오: World

Hello가 World보다 앞에 있습니다. //알파벳 순으로 즉 사전에서 앞에있다는 의미임.

 

 

문자검색(strchr)

char *p = strchr("dog", 'g');

//이렇게하면 g문자의 주소를 반환함.

#include <string.h>
#include <stdio.h>
int main( void )
{	char s[] = "language";	 
	char c = 'g';	
	char *p;
	int loc;

	p = strchr(s, c);
	loc = (int)(p - s);
	printf("s 주소:%u, p 주소:%u\n", s, p);
	if ( p != NULL )		
		printf( "첫번째 %c가 %d에서 발견되었음\n", c, loc );
	else
		printf( "%c가 발견되지 않았음\n", c );
	return 0;
}

p = strchr(s,c); // 이렇게하면 s안에서 문자 c를 찾아줌

만약 문자열에 찾으려는 문자가 없다면 문자가 발견되지않았음이라고 출력함

 

출력값: 

s주소: 문자열 s의 첫번째 인덱스 주소값 p주소: 찾으려하는 c(p)의 주소값

첫번째 g가 3에서 발견되었음

 

문자열 검색(strstr)

char *p = strstr("dog", "og");

//str함수는 문자열 안에서 부분문자열을 검색하는 함수임 만약 부분 문자열이 발견되면 그 위치주소를 반환함.

문자열 찾지 못하면 NULL값 반환됨.

 

#include <string.h>
#include <stdio.h>
int main( void )
{	
	char s[] = "A joy that's shared is a joy made double";
	char sub[] = "joy";	
	char *p;
	int loc;
	p = strstr(s, sub);
	loc = (int)(p - s);
	if ( p != NULL )
		printf( "첫번째 %s가 %d에서 발견되었음\n", sub, loc );
	else
		printf( "%s가 발견되지 않았음\n", sub );
}

출력값: 

첫번째 joy가 2에서 발견되었음.

 

문자열 토큰 분리(strtok)

char s[] = "Hello World";

char delimit[] = " ";

char *p = strtok(s, delimit);

//문자열을 스페이스문자를 사용하여 단어들로 분리함

//strtok()함수는 찾는 문자열의 시작위치를 내부에서 static으로 관리하고 1번째 인자가 문자열이면 해당 문자열의

처음위치로 설정하고 NULL이면 찾은 delimiters의 다음 위치가 설정됨.

 

문자열 수치 변환

문자열 "36.5"와 수치값 36.5는 컴퓨터 안에서 상당히 다르게 저장된다.

sprintf()와 sscanf()

앞에 붙은 s는 string을 의미함

문자열을 수치로 바꿀땐 sscanf, 수치를 문자열로 바꿀땐 sprintf

 

#include <stdio.h>

int main( void )
{
	char s[] = "100";
	int value;

	sscanf(s, "%d", &value);
	printf("%d \n", value);
	value++;
	sprintf(s, "%d", value);
	printf("%s \n", s);
	return 0;
}

출력값: 

100

101

 

영상파일이름자동생성 프로그램

#include <stdio.h>
#include <string.h>
int main(void)
{
	char filename[100];
	char s[100];
	int i;
	for(i=0; i < 6; i++){
		strcpy(filename, "image");
		sprintf(s, "%d", i);
		strcat(filename, s);
		strcat(filename, ".jpg");
		printf("%s \n", filename);
	}
	return 0;
}

출력값:

image0.jpg

...

image6.jpg

 

문자열을 수치로 변환하는 전용함수 

#include <stdio.h>
#include <stdlib.h>
int main( void )
{
	char s1[] = "100";
	char s2[] = "12.93";
	char buffer[100];
	int i;
	double d, result;

	i = atoi(s1);
	d = atof(s2);
	result = i + d;

	sprintf(buffer, "%f", result);
	printf("연산 결과는 %s입니다.\n", buffer);
	return 0;
}

출력값: 연산 결과는 112.93000000입니다.

//int형이랑 더블형이랑 덧셈을 하고 이걸 sprintf를 사용하여서 문자열로 바꿔주고 이걸 다시 출력함.

 

문자열의 배열

문자열이 여러개 있는경우에는 어떤 구조를 사용하여 저장하면 제일 좋을까?

문자열의 배열: char[][]

문자 포인터 배열: char *[]

 

#include <stdio.h>

int main( void )
{
	int i;
	char menu[5][10] = { // char *menu[5]=  {
		"init",
		"open",
		"close",
		"read",
		"write"
	};

	for(i = 0; i < 5; i++)
		printf("%d 번째 메뉴: %s \n", i, menu[i]);

	return 0;
}

출력값:

0번째 메뉴: init

1번째 메뉴: open

2번째 메뉴: close

3번째 메뉴: read

4번째 메뉴: write

 

#include <stdio.h>
int main( void )
{
	int i;
	char fruits[3][20];
	for(i = 0; i < 3; i++) {
		printf("과일 이름을 입력하시오: ");
		scanf("%s", fruits[i]);
	}
	for(i = 0; i < 3; i++)
		printf("%d번째 과일: %s\n", i, fruits[i]);
	return 0;
}

char fruits[3][20]; -> char *fruits[3];

즉 위에 scanf에서 입력값을받을때 배열앞에 &붙히면안됌

 

출력값:

과일 이름을 입력하시오: 사과

과일 이름을 입력하시오: 배

과일 이름을 입력하시오: 포도

0번째 과일: 사과

1번째 과일: 배

2번째 과일: 포도

 

간단한 한영사전 구현

#define ENTRIES 5

int main( void )
{
	int i, index;
	char dic[ENTRIES][2][30] = {
		{"book", "책"},
		{"boy", "소년"},
		{"computer", "컴퓨터"},
		{"lanuguage", "언어"},
		{"rain", "비"},
	};
	char word[30];
    printf("단어를 입력하시오:");
	scanf("%s", word);

	for(i = 0; i < ENTRIES; i++)
	{
		if( strcmp(dic[i][0], word) == 0 )
		{
			printf("%s: %s\n", word, dic[i][1]);
			return 0;
		}
		
	}
	printf("사전에서 발견되지 않았습니다.\n");
}

출력값: 

단어를 입력하시오: book

book: 책

 

문자 암호화하기

문자 하나씩 앞당겨서 암호화

#include <stdio.h>

void encrypt(char cipher[], int shift);

int main (void) {
	char cipher[50];
	int shift=3;
	printf("문자열을 입력하시오: ");
	gets(cipher); // 한줄 전체 입력
	encrypt (cipher, shift);
	return 0;
}
void encrypt (char cipher[], int shift) {
	int i = 0;
	while (cipher[i] != '\0') {		
		if( cipher[i] >= ‘a' && cipher[i] <= 'z'){
			cipher[i] += shift;			
			if( cipher[i] > 'z' ) 
				cipher[i] -= 26;
		}
		i++;
	}
	printf("암호화된 문자열: %s", cipher);
}

이렇게 아스키코드값을 이용하여서 해버리면됌

 

복호화하는 함수는 decrypt()임

 

행맨게임 만들기

//빈칸으로 구성된 문자열이 주어지고 사용자는 문자열에 들어갈 글자들을 하나씩 추측해서 맞추는 게임

//사용자가 문자열에 들어 있는 글자를 정확하게 입력했으면 화면에 그 글자를 출력한다.

//일정한 횟수만 시도할 수 있게 하라.

 

#include <stdio.h>
int check(char s[], char a[], char ch);
int main (void) {
	char solution[100] = "meet at midnight“;
	char answer[100] = "____ __ ________“;
	char ch;
	while(1) {
		printf("문자열을 입력하시오: %s \n", answer);
		printf("글자를 추측하시오: ");
		ch = getchar();
		if( check(solution, answer, ch) == 1 )				break;
		getchar();//fflush(stdin); // 줄바꿈 문자 제거
	}
	return 0;
}

int check(char s[], char a[], char ch)
{
	int i;
	for(i=0; s[i] != NULL; i++) {
		if( s[i] == ch )
			a[i] = ch;
	}
	if( strcmp(s, a)==0 ) 
		return 1; // 정답과 일치하는지를 검사
	else return 0;
}

 

'C' 카테고리의 다른 글

c - pointer  (0) 2023.09.15

포인터는 변수의 주소

포인터도 선언해서 쓰는면에서는 변수랑 똑같음

- 간접 참조 연산자임 (*) 주소를 가지고 변수를 접근한다라는뜻

포인터는 연산이가능( 더하기 빼기만)

포인터(변수의 주소, 주소를 가지고 있는 변수)와 배열(배열의 이름은 상수 주소)은 같이 주소를 나타냄

함수에서 파라미터값으로 포인터를 넘겨줄수도있음

 

변수는 메모리에 저장됨 메모리의 단위는 바이트

첫번째 바이트 주소는0, 두번째 바이트는 1,...

변수의 크기에 따라 메모리 공간 달라짐

char = 1바이트

int = 4

이런식으로..

메모리에 저장될때 이렇게 순차적으로 차곡차곡 저장되어지는게아니라 이런식으로 저장됌.

주소는 4바이트 단위로 저장이됨. 그래서 저런식으로 저장되는거임. 변수의 시작은 4의 배수의 단위로써 시작함.

9 10 11은 사용하지않음.

사용하지 않는 바이트임 

변수의 주소를 계산하는 연산자: &

변수 i의 주소: &i

같은 형끼리 연산을 해야됌 아니면 오류가 날수도있음.

 

포인터는의 선언

포인터는 변수의 주소를 가지고 있는 변수

int * p;  (포인터의 선언)

 

주소 연산자: &

간접 참조 연산자: * (포인터가 가리키는 값을 가져오는 연산자)

 

pi에는 i의 주소값이 들어가게됨 근데 이제 *pi를 출력하게되면 i의 값인 10이 나오게됨.

위에있는 그림은 pi를 출력했기에 i의 주소가 출력됨.

 

형이 다른것에 저장해버리면 warning뜸

 

메모리는 바이트 단위를 기준으로 주소가 매겨짐

char: 4 short: 2 int: 4 long: 4 float: 4 double: 8

포인터도 메모리에 할당되는 변수임변수의 주소를 추출하는데 사용되는 연산자는 &이며, 변수 x의 주소를 추출하여 변수 p에 대입하는 논리는p = &x라고 할수있고,정수형 포인터 p가 가리키는 위치에 25를 저장하는 논리는int *p = NULL;*p = 25;라고 할수있음.

 

포인터 연산에서는 증가, 감소, 덧셈, 뺄셈 연산이 가능하며,증가 연산의 경우 증가되는 값은 포인터가 가리키는 객체의 크기만큼 증가됨.

 

증가 연산을 진행하게되면 이렇게 포인터 변수에 주소값을 각각 정해주고 ++연산을 진행해주면 형의 크기 단위로 증가 감소가 일어나게됨.

 

*p++와 (*p)++의 차이점은 전자는 p만 증가시키는 것이고 후자는 p가 가르키는 것을 증가시키는 것이다

p가 포인터라고 하면 *(p+3)의 의미는 p로부터 객체 단위의 3을 더한 후에 그 값을 가져오는것

'C' 카테고리의 다른 글

c programing  (0) 2023.10.17

+ Recent posts