2016. 2. 25. 16:49

c언어에서의 파일입출력은

1. 파일을 연다

2. 파일에 입출력 작업을 한다.

3. 파일을 닫는다.

로 요약됩니다.


입출력 할 때 파일포인터가 필요하기 때문에

FILE *f; 와 같은식으로 파일 포인터를 선언해준다.


1. 파일 열기

파일을 열기 위해서는 fopen() 함수를 사용하게 됩니다.

따로 경로가 없다면 현재 소스파일이있는 그 경로를 나타냅니다.

파일명은 확장자 까지 포함한 풀 네임이며, 모드가 있는데,

w : 쓰기 모드, 만약 파일이 존재 하지 않는다면 파일을 새로 만든다.

r : 읽기 모드, 만약 파일이 존재 하지 않는다면 NULL을 리턴한다.

a : 추가 모드. 파일이 존재 하지 않는다면 파일을 새로 만든다.

w+ : 읽기/쓰기

r+ : 읽기/쓰기

a+ : 읽기/추가

b : binary


2. 입출력 작업

입출력을 하기 위해서는 파일입출력 함수들이 이용됩니다.

문자 : fgetc(), fputc();

문자열 : fgets(), fputs();

fscanf(), fprintf(); 


간단한 입출력 예제

1
2
3
4
5
6
7
8
#include <stdio.h>
int main()
{
    FILE *f;
    f=fopen("a.txt","w");
    fprintf(f,"Hello World");
    fclose(f);
}
cs

 

이렇게 입력후 실행하면!?

도스창엔 아무것도 안뜨게 됩니다.

왜냐하면 파일에 작업을 하고있기 때문이죠


소스가 있는 a.txt파일이 보일 것입니다.

텍스트 파일을 열어보면 이와같이 Hello World라는 문구가 출력되어 있습니다.



3. 파일 닫기

위 소스들을 잘 살펴보면 항상 

fclose(f); 라는 함수가 소스의 끝부분에 쓰여 있습니다.

바로 이 함수가 파일을 닫겠다는 의미입니다

Posted by 시리시안
2016. 2. 25. 14:20

코루틴의 특성


1. 특정 작업을 단계적으로 발생하게 한다.

2. 시간이 흐름에 따라 발생하는 루틴을 작성할 수 있다.

3. 다른 연산이 완료될때까지 기다리는 루틴을 작성할 수 있다.

예를 들어, 컷 씬 시퀀스를 조직하거나 또는 단순히 적이 죽는 애니메이션을 기다린 다음, 재생성을 할 수도 있다.


만약 단지 몇 초 동안 특정 연산을 지연시키기를 원한다면, Invoke를 사용하는 것을 고려해보자..


우선, 확실하게 알고 넘어가자, 코루틴은 쓰레드가 아니다. 코루틴은 비동기가 아니다


헷갈릴수 있는데, 쓰레드는 프로그램에서 다른 쓰레드와 함께 비동기로 실행된다.

멀티프로세서 기계에서, 쓰레드는 실제 다른 쓰레드와 함께 동시에 코드가 실행될 수 있다.


그래서 코루틴이 무엇인가?


코루틴은 쓰레드가 아니다. ,  코루틴은 한번에 하나의 코루틴이 싱행되고, 게임의 메인쓰레드에 맞춰서 실행된다. 사실 함수를 호출하는것처럼 게임의 핵심 기능을 호출하는것과 같다,


따라서 코루틴은 쓰레드가 아니며 비동기도 아니기 때문에 사용할때 절대 동기화나 특정 값을 잠금하는것에 대한 걱정이 필요없다.

당신이 yield를 실행하여 완전히 통제 할 수 있기 때문이다.


따라서 코루틴이란, 부분적으로, 그리고 특정한 상황이 맞아 떨어졌을 때 실행되는 함수로써, 그 작업이 완료가 되기전까지, 미래의 어느시점에 재개 될 수 있는 함수이다.




위에 표를 보면 yield로 끈어준 코루틴들이 각각의 알맞는 상황에 호출된다.


밑에 간단한 코루틴을 보자


1
2
3
4
5
6
7
8
IEnumerator TestCoroutine()
{
      while(true)
      {
           Debug.Log(Time.time);
           yield return null;
      }
}
cs



이 코루틴은 영원히 작동하는 루프를 가지고 있다. 이 코루틴은 현재 시간으로 로그를 남기고, 그 다음 yield한다. 다시 재개될 때, 다시 루프를 돌아서 로그를 남기고, 한번 더 yield될 것이다.


루프안에 있는 코드는 정확하게 Update 함수와 같다. 단지 순서에따라 업데이트 다음에 실행될 뿐이다.


StartCoroutine(TestCoroutine())를 호출할 때, 코드는 첫번째 yield를 만날때까지 즉시 실행되며,

유니티 프로세스가 이 객체에 대해서 코루틴을 처리할 때, 다시 재개된다.


위에 있는 도표에 맞춰 코루틴안에서 호울하는 yield에 따라 다음 호출되는 지점이 달라진단느 것을 알 수 있다.


null - Update구문의 수행이 완료될 때까지 대기한다.


WaitForEndOfFrame - 현재 프레임의 렌더링 작업이 끝날 때까지 대기한다.


WaitForFixedUpdate - FixedUpdate구문의 수행이 완료될 때까지 대기한다.


WaitForSeconds - 지정한 초만큼 대기한다.


WWW - 웹에서 데이터의 전송이 완료될 때까지 대기한다.

(WaitForSeconds or null처럼 재시작한다.)


Another coroutine - 새로운 코루틴은 yielder가 재시작되기 전에 완료 될 것이다.



또한 yield break; 명령어를 실행할 수 있다. 이는 즉시 코루틴을 멈춘다.



요약


코루틴은 시간에 따라 혹은 외부의 처리가 완료되었을 때, 연산이 일어나는 순서를 정하는 정말 멋진 방법이다.


코루틴은 쓰레드가 아니다. 그리고 비동기가 아니다(동시에 일어나지 않는다.)


코루틴이 실행되고 있을 때, 다른 어떤 것들도 실행되지 않는다.


코루틴은 yield 문의 조건을 만족시켰을 때, 재개될 것이다.



Posted by 시리시안
2016. 2. 25. 13:10

기본적으로 많이 사용하는 형식의 데이터형만 기본 데이터로써 정의되고있고, 나머지는 프로그래머가 필요에따라 정의해서 사용할 수 있도록 만들수있습니다.


그래서 기본 데이터 형인 char, int, float, double등을 제공하고 나머지는 프로그래머가 배열, 포인터,구조체, 공용체, 혹은 typedef 같은 문법을 사용해 직접 정의 해서 사용합니다.


배열과 같은 무법으로 사용자 정의 데이터형을 만들게 되면 동일한 데이터 형만을 그룹지을 수 있습니다. 단순한 형태만 정의 가능하다는 단점이 있지만, 이를 보완하기 위해 만들어 진것이 구조체 입니다.


구조체는 서로 다른 데이터형을 하나의 데이터로 군집화 하여 사용 할 수 있도록 하는 사용자 정의 데이터 형입니다.


1. 구조체 정의하기

 

구조체를 정의 할 때에는 다음과 같이 사용합니다.


struct 구조체명 {

데이터형 변수명;

...

};

이렇게 선언된 구조체명이 새로 쓸수있는 데이터형의 이름입니다. 즉, int char처럼 사용가능합니다.


구조체는 struct 키워드를 앞에 명시한 다음에 사용할 구조체명을 적어서 정의합니다. 이 때 구조체

     내부를 구성하는 데이터는 미리 정의되어 있어야 하며, 각 데이터는 세미콜론(" ; ") 키워드로 

분리해야합니다. 

예를 들어 이름, 나이, 키, 몸무게와 같은 인적사항을 저장하는 People 이라는 구조체를 정의하면 다음과 같습니다.


1
2
3
4
5
6
7
 
    struct People {
        char name[20];
        int age;
        double height;
        double weight;
    };
cs

    위에서도 말했듯이 구조체 내부의 데이터는 미리 정의된 데이터형이여야하기 때문에 char, int, double

    등과 같은 기본 데이터형 외에 다른 구조체나 사용자가 따로 정의한 데이터형을 사용하고 싶으면

    이 구조체보다 앞서 정의되어 있어야합니다.

 

    일반적이지는 않지만 한번만 선언해서 사용하는 경우 아래와 같이 구조체명을 생략해서 사용하는 경우도 있습니다.

즉, 구조체를 정의함과 동시에 data 라는 변수를 선언하여 사용하기 때문에 이 데이터형을 다시 사용할 필요가 없다면 구조체명도 필요없기 때문에 생략 가능합니다.

 

 

1
2
3
4
5
6
    struct {
        char name[20];
        int age;
        double height;
        double weight;
    } data;
cs

 

 

2. 구조체 사용하기

 

    정의된 구조체는 구조체명을 이용하여 변수를 선언하듯이 선언해주면 되는데, C 언어에서는 구조체를

    이용하여 변수를 선언할 때에는 반드시 구조체명 앞에 "struct" 키워드를 붙여주어야합니다.

 

    // People 구조체를 사용하여 data 변수를 선언한다.

    struct People data;

 

    C++ 언어로 구조체를 선언할 때에는 struct 키워드를 생략하여 선언할 수 있지만 C 언어의 경우에는

    구조체를 선언할 때마다 struct 키워드를 명시해야 하기때문에 (굉장히 불편합니다.)

해결법으로는 아래와 같이 typedef 명령어를 이용하여 구조체를 또다른 데이터 타입으로 정의하여 사용하기도 합니다.

 

    typedef struct People PEOPLE;

 

    이렇게 선언하면 C 언어에서도  People 구조체를 사용하여 변수를 선언할때 struct 키워드를

    적지 않게되어 좀더 편리하게 사용할수 있습니다.

 

    PEOPLE data;  // struct People data;

 

    하지만, 구조체 정의 따로 typedef 정의 따로 사용하는 방식이 프로그램을 이해하기에 불편할수도

    있기 때문에 이것을 아래와 같이 한번에 사용하여 구조체를 정의하기도 합니다.

 

1
2
3
4
5
6
7
8
 
 // struct people { ... } 구조체를 PEOPLE 이라는 데이터형으로 정의한다.
    typedef struct People{
        char name[20];
        int age;
        double height;
        double weight;
    } PEOPLE;
cs

 

최근에는 C++ 를 사용하는 사람들이 많아짐에따라 struct 가 생략가능해지면서 typedef 을 사용하는 형식도 점차 줄어들고 있습니다.    (Class가 있으니..)

   

구조체로 선언한 데이터형도 기본데이터형(char, int, ...)과 동일한 데이터 형이기 때문에 변수 선언 배열, 포인터 문법을 아래와 같이 그대로 사용할수 있습니다.

 

PEOPLE data;      // 일반 변수

PEOPLE list[20];  // 배열

PEOPLE *p;         // 포인터

 

이렇게 선언된 구조체 변수는 아래의 코드처럼 구조체의 요소에 접근할 때에는 " . " 를 이용하여 구조체의 "변수명.요소명" 과 같은 형식을 사용합니다.

     

1
2
3
4
5
6
PEOPLE one;
 
    strcpy(one.name, "운영진");
    one.age = 23;
    one.height = 179.9;
    one.weight = 70;
cs

 

3. 구조체의 크기와 메모리 배열 ( struct member alignment )

 

구조체의 크기는 일반적으로 구조체를 구성하는 데이터형의 크기를 합산한 값입니다.


    Windows 32비트 운영체제는 레지스터의 크기, 데이터 버스의 크기, 데이터 처리 단위, 포인터의 크기

    등이 모두 4 바이트로 되어 있어서 해당 크기로 연산하고, 처리하는 것이 속도가 더 빠릅니다. 그래서 

    컴파일 옵션에 따라서 구조체를 해석할때 그 배열을 짝수 또는 4의 배수로 재구성하는 작업

    수행하기 때문에 실제로 구조체가 구성되는 것이 조금 다를 수 있습니다.

 

1
2
3
4
5
6
7
typedef struct Test {
        char a;       // 1 바이트의 데이터 크기를 가짐
        int b;          // 4 바이트의 데이터 크기를 가짐
        short int c;  // 2 바이트의 데이터 크기를 가짐
        int d;          // 4 바이트의 데이터 크기를 가짐
        char e;       // 1 바이트의 데이터 크기를 가짐
    } TEST;
cs

 

위와 같이 정의된 TEST 구조체를 선언하면 12 바이트의 메모리가 할당된다고 생각할 수도 있지만

구조체를 구성하고 저장하는 단위가 4 바이트이기 때문에 b 나 d 같은 4 바이트 이상의 크기를 가지는

변수가 4의 배수 주소 값에 위치하도록 아래와 같은 형태로 메모리가 사용되어 20 바이트의 메모리가 할당됩니다.

 

그렇기때문에 쓸데없는 메모리의 낭비를 막기 위해서는 아래와 같이 데이터를 구성하는 순서를변경하여 구조체를 정의하는 것이 좋습니다.

 

   

1
2
3
4
5
6
7
typedef struct Test {
        int b;          // 4 바이트의 데이터 크기를 가짐
        int d;          // 4 바이트의 데이터 크기를 가짐
        short int c;  // 2 바이트의 데이터 크기를 가짐
        char a;       // 1 바이트의 데이터 크기를 가짐
        char e;       // 1 바이트의 데이터 크기를 가짐
    } TEST;
cs

 

   

 

이처럼 구조체를 정의할 때 어떻게 배치하는가에 따라 구조체의 크기가 변경될 수 있기때문에 메모리를 낭비하지 않도록 주의해야합니다,

프로그래밍을 하다가 구조체의 크기를 명시해야하는 경우에는 수치 값이 아닌 sizeof 매크로를 사용하여 구조체의 크기를 반환받아 사용해야합니다. 

Posted by 시리시안
2016. 2. 25. 11:49

함수 

Component GetComponent<type Stirng>

설명

만약 게임 오브젝트에 type에 맞는 컴포넌트가 있다면 그 컴포넌트를 찾아서 돌려주고,, 없으면 null 를 돌려준다


 

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using System.Collections;
 
public class mGetcomponentExample: MonoBehaviour {
    void Start() {
        Transform mTransform;
        mTransform = gameObject.GetComponent<Transform>();
        mTransform = gameObject.transform;
    }
}
cs


Posted by 시리시안
2016. 2. 25. 11:38

함수 

Component GetComponentInChildren<type>() 

설명

게임오브젝트에서 type 타입의 컴포넌트나 depth-frist 찾기를 해서 얻은 그것의 자녀들 중 가장 빠른 자식을 돌려준다.


오직 활성 컴포넌트만 검사한다. 


1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using System.Collections;
 
public class example : MonoBehaviour {
    public ScriptName mScript;
    void Start() {
        mScript = gameObject.GetComponentInChildren<ScriptName>();
        mScript.DoSomething();
    }
}
cs


Posted by 시리시안
2016. 2. 23. 20:35


try, throw, catch 를 이용한 예외 처리 방법



예외 ( Exception ) 란? : 프로그램의 정상적인 실행을 방해는 조건이나 상태를 말합니다. ( 에러가 아닙니다.)


1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
 
 
void main()
{
    int a = 0;    
    int b =20;
    
    int c = b/a;
 
}
cs

위 상황을 봅시다. a의 값이 0이 할당된 상태에서 b를 a로 나누려고 합니다. 아직까진 오류가 아니지만, ( 물론 컴파일러에 따라서 오류로 판단할수 있습니다.) 

하지만, 오류가 아니라 판단되면, 다음식을 쓰레기값을 뱉을수 밖에없습니다. 그럴때 try catch문을 사용합니다.

try
1. 예외가 발생할만한 코드 블록을 지정합니다.
2. try{} 괄호 안에 예외 처리 대상 코드를 작성합니다. 
3. 이블록안에서 예외가 발생했을 때 throw 명령으로 예외를 던집니다.

throw
1. 프로그램이 정상적으로 실행될 수 없는 상활일 때 이 명령으로 예외를 던집니다.
2. thorw다음에 던지고자하는 예외객체를 넘김. 예외 던지면 catch문으로 이동(단: 예외 객체 타입이 맞을때만 적용됨 ), (오버로딩 가능)

catch
1. if-else문처럼 try-catch문으로 한쌍으로 쓰입니다.
2. try안에서 throw한 예외 객체에 대한 예외처리를 합니다
3. catch블록을 예외 핸들러라고 부릅니다..

사용법
try{
    if(예외조건)
       throw 예외객체;
} catch ( 타입 예외객체 ){
      예외처리;
}

사용 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
 
 
void main()
{
    int a = 0;    
    int b =20;
    
    try{
    if(a == 0)
        throw a;
    int c = b/a;
    }
    catch(int ex){
        std::cout<<"예외 처리 발생, a가 0입니다."<<std::endl;
    }
}
cs

그냥 if문으로 바로 처리하면 되는데, 왜 굳이 catch까지 쓰나요?

라고 물어볼수 있는데, 이는 Stack Unwinding(스택 풀기) 를 이해하면 쉽다.

다음 예제를 보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<iostream>
 
 
int AddValue_abs (int a, int b)
{
    if(a<0)
        throw a;
    if(b<0)
        throw b;
 
 
    return a+b;
}
void main()
{
    int a,b;
    
    std::cin>>a>>b;
    
    try{
        //바로 보이는 이곳엔 throw가 없다.
        std::cout<<AddValue_abs(a,b)<<std::endl;
    }
    catch(int ex){
        //예외처리가 생기면 실행
        std::cout<<"예외 처리 발생"<<std::endl;
    }
}
cs


AddValue_abs라는 함수에서 try도 없는데, 그렇다고 catch도 없는데 뜬금없이 조건 검사후 throw를 던집니다.
여기서 Stack Winding(스택풀기)를 볼수있는데, throw가 던져진 시점에서 바로 뒤에 코드는 읽지 않습니다.
그리고 뒤로 스택을 풀어 나가며, throw로 던져진 변수 타입과 같은 인자를 지닌, catch를 찾습니다.

만약 예외 처리를 if문으로 검사하게되면, 여러 조건에 따라 함수 자체의 반환값에 예외를 하거나, 예외후 처음부터 다시해야하는 번거로움이 있는데, 이걸 사용할 경우 간편하게 할수가 있습니다.

또한 Throw로 클래스 자체를 넘겨버리면, Catch에서 calss를 인자로 받아올수 있다는점..!








 




'프로그래밍 > Language' 카테고리의 다른 글

파일 입출력에 대하여  (0) 2016.02.25
구조체에 대하여  (0) 2016.02.25
포인터에 대하여  (0) 2016.02.23
C언어 연산자에 대하여 - 비트연산  (0) 2016.02.23
C언어 연산자에 대하여  (0) 2016.02.23
Posted by 시리시안
2016. 2. 23. 15:10

절대좌표 연산을 사용하면서 부드러운 회전을 구현하려면 정말 귀찮다.

시작 Quaternion, 목표 Quaternion, Deltatime을 고려해 속도를 구해서 업데이트마다 카운트하면서 더해준다..

하지만 메서드 한방으로 이걸 전부할수있다면?

Quaternion.Slerp(현재 각도, 도착 각도, 속도);


위 메서드의 반환값을 transform.rotation에 대입해주면 된다.

각도에는 모두 Quaternion을 사용한다.



또한 Mathf.Slerp 로 같은 원래로 float도 가능하다.

Vector3도있으니 참고하자.

Posted by 시리시안
2016. 2. 23. 13:18

망하는 제품의 흔한 개발 과정

※ 리더 : 요즘 유행하는 대세를 들고 온다. 이것이 대세다!

 리더 : 속으로는 이런 것들을 쓰는 사람들은 사회부적응자라 생각하고 본인은 정작 써 본 적이 없다.

 기획 : 써 본적은 없지만 들어는 봤다. 이런 것을 쓰는 사람은 격이 떨어지는 사람이라 생각하고, 내가 우아하고도 유럽 명품에 견줄 수 있는 것을 보여주어야 겠다 생각한다.

 기획 : 해당 제품군을 모조리 조사한다. 그래서 해당 제품군의 모든 특징을 합한 고질라 같은 것을 그려 낸다.

 리더 : 그것만으로는 뛰어 넘을 수 없다고 한다.

 기획 : 아이디어를 동원한다. 이제 그 고질라에 스타워즈, 반지의 제왕, 해리포터를 더하기 시작한다. 자신의 상상력의 끝은 어디인가 하면서 스스로 놀라워 한다.

 리더 : 고질라에서 빠진 게 없나 살핀다. 다소 억지 스럽지만, 비슷한류의 제품을 가져와 하나 더 붙인다. 이런게 바로 리더의 통찰력이라 으쓱거린다. 기획자의 아이디어를 보고는 기획자가 미쳐 생각하지 못한 경우의 수를 생각해서, 더 복잡하게 만든다. 아직 가르칠게 많다고 생각한다.

 개발 : 그런건 못만들어요 불평을 늘어놓는다.

 리더 : 내앞에서 안된다는 말은 하지 말라고 하며, 할 수 없다는 것부터 이야기하는 태도가 문제라고 한다. 그리고는, 자신의 인생 역정기를 늘어 놓는다.

 개발 : 기획에 대한 조언을 해 줘야 겠다고 생각한다. (사실 해당 제품군을 사용해 본 유일한 사람이다.)

 리더 : 넌 아직 인지과학, 심리학을 모른다고 일축한다.

 기획 : 파워포인트로 찍어 내는 노가다를 시작한다.

 리더 : 문서에서 오타를 찾아 낸다.

 개발 : 이 프로젝트는 어짜피 산으로 갈 것이라고 떠들어 대기 시작한다.

 리더 : 최근 세미나에서 본 솔루션들을 쓰면 금방 할 것이라고 말한다. 그리고 비싼 돈을 들여 도입을 추진한다.

 개발 : 그게 뭔지 모른다. 다만, 대충 들어보니, 그것 보다는 자기간 만들어 놓은 자작 솔루션이 훨씬 더 좋은거라고 속으로 생각한다.(사실 지금 이 상황에 그걸 배워서 만드는 것은 엄두가 나지 않는다) 그리고, 쓰는 척 시늉만 하기로 결심한다. 타인이 만든 것을 사용하는 것은 하수들이나 하는 짓이라 생각한다.

 리더 : 개발기간은 3개월이라 한다.

 개발 : 불가능한 일정이라 하고, 기획안을 조정하라고 주장한다.

 리더 : 나는 어찌 저런 무능하고 게으른 개발자만 옆에 있는지 탄식한다. 나에게 해외 유수기업의 개발자를 붙여주면 단박에 성공 할 수 있으리라 생각한다.

 개발 : 투덜거리며 밤 샌다. 불행하게도 고질라를 만들어 내는 과정과 SF 가 붙여 지는 과정은 개발 과정 진행중에 병행해서 발행하는 일이다. 스타워즈를 다 붙여놓으면, 어느덧 스토리는 해리포터로 바뀌어 있다. 다시 밤을 샌다.

 리더 : 3개월 후면 다 되어 있겠지 생각을 한다. 개발 과정에는 관심이 없다. 개발이 진행되는 중간 중간, 어제밤 자다가 생각난 환타스틱한 장면을 기획자에게 넣으라고 말한다. 이 장면을 놓쳤으면 이번 제품에 핵심이 빠졌을 거라고 생각하고, 이제라도 넣게 되어 다행이다 생각한다. 그리고, 자신이 얼마나 디테일에 강한가 다시 한번 생각해 본다.

 개발 : 코드는 개떡이 되어 간다. 어짜피 이건 내탓이 아니다. 정말 제대로 된 환경에서 했다면, 난 정말 멋지게 해 낼 수 있었을 텐데, 운없이 이런 놈들이랑 팀을 해서 이렇게 된거라 생각한다. 이 제품은 내 손에서 나왔지만, 내가 만든건 아니라 생각한다.

 리더 : 3개월후, 생각했던게 안나오자 개발자에게 책임 추궁을 해야겠다 생각한다. 처음부터 태도도 안좋았고, 자기가 말한 것을 구현해 낼 실력도 없었던 사람이었다 생각한다. 후회한다. 이 모든 것은 개발의 문제다. 하지만, 일단 출하한다.

 기획 : 자신의 유럽 명품적 감각의 파워포인트를 어떻게 이런 제3세계 제품으로 만들어 냈는지 의아해 한다.

 리더 : 다시 시작하자 으쌰 으쌰 해 본다. 그리고, 그 사이 대세가 바뀌지 않았다 살펴 본다.

 개발 : 어짜피 이렇게 된거, 처음부터 다시 시작하자고 한다. 나는 다시 내가 만든 것을 들여다 보고 싶지 않다.

 리더 : 역시 중요한 것은 사람이다 라고 생각한다.


흥하는 제품의 흔한 개발 과정

 리더 : 자신에게 꼭 필요했던 핵심가치(기능)을 발견한다. 현존하는 타 제품에서는 발견할 수 없기에, 만들어야 겠다고 결심한다.

 기획,개발 : 자신도 꼭 필요했던 것이라 생각하고, 만들면 정작 자신이 가장 큰 혜택을 받을 것이라 생각한다.

 리더,기획,개발 : 다 같이 모여서 기존 제품들을 맹렬히 비판해 낸다. 왜 다 이렇게 될 수 밖에 없었는지 생각해 본다.

 개발 : 관련된 기술을 조사한다. 그리고, 조사한 결과를 공유한다.

 기획 : 수없이 많은 기술을 가지고, 두개의 연결(조합)을 시도한다. 전혀 상관없을 것이라 생각했던 두가지 기술을 합하니, 매우 멋진 모습이 되었다.

 리더 : 이 멋진 조합이 핵심가치를 구현하는 결정적 요소가 아니면, 버리자고 한다. 핵심가치에만 촛점을 맞춘다.

 개발 : 핵심가치를 구현할 가장 단순한 방법을 찾는다. 구현이 단순할 수록 생각치 못한 일이 발생할 가능성이 줄어 든다.

 리더 : 개발된 시제품을 써 본다. 하루고 이틀이고 계속 써 본다. 불편한 점을 찾거나, 그 보다 더 단순하게 할 방법을 생각해 낸다.

 개발 : 반복적으로 만들어 낸다. 구현 방법이 단순하였기에, 이 반복과정이 고통스럽지 않다. 이 반복과정을 더 쉽게 할 수 있는 방법을 계속 추가해 낸다.

 기획 : 이 단순한 핵심가치를 제공하는 이 제품이 생각보다 많은 곳에서 응용될 수 있다는 것을 찾아 낸다.

 리더 : 기쁘지만, 처음 생각한 것에 집중하기로 한다.

 리더 : 충분히 만족스러운 상태가 되면 제품으로 출하한다. 충분히 고민한 것이기 때문에, 아주 오랫동안 다시 이 문제를 생각할 필요가 없을 거라 생각한다. 누군가 흉내내면서 새로운 것을 덧붙여 내거나 변형을 시켜내도 크게 신경 쓰지 않는다.

 기획 : 현재까지 이룩한 것에서 최소한의 노력으로 추가할 수 있는 핵심가치를 다시 찾기 시작한다.

 리더 : 역시 중요한 것은 사람이다 라고 생각한다.




Posted by 시리시안
2016. 2. 23. 08:41


포인터 *


포인터란 어떠한 값을 저장하는게 아닌 어떠한 값의 주소(Address)를 저장하는 것 입니다


어떠한 값의 주소는 컴퓨터 메모리의 주소를 의미합니다.

1
2
3
4
5
6
7
8
#include<stdio.h>
 
void main()
{
    int num = 0;
    int *pnum = &num;
    //pnum은 num의 주소값을 가지고 있다.
}
cs


포인터 변수를 선언하기 위해서는 변수이름 앞에 *을 붙여서 선언하면 그 변수는 포인터 변수가 됩니다.


대체로 포인터인걸 알리기위해 변수병 앞에 소문자로 p를 붙이기도 합니다. 하지만 이건 코드 스타일이니.. 각자 알아서..


아직 이해가 잘 안되실텐데, 포인터 변수를 이용하여, 주소값과 포인터의 관계를 나타낸 예제입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>
 
int main(void)
{
    int *pnum;         // 포인터 변수 선언
    int num = 10;     // 일반변수 선언 & 초기화
    pnum = &num;     // num의 주소값을 pnum에 저장
 
    printf("num의 값: %d\n", num);
    printf("num변수의 주소:%p\n", &num);
    printf("pnum변수의 값 :%p\n", pnum);
    printf("pnum이 가리키는 값: %d\n"*pnum);
 
/*
결과값
num의 값: 10
num변수의 주소:001919fC
pnum변수의 값 :001919fC
pnum이 가리키는 값: 10
*/
}

cs


밑에 주석처리한 결과값을 보면 pnum은 num의 주소값을 가리킨다는것을 알수있습니다.

즉 pnum이 가진 주소값의 위치로 찾아가면 num의 값을 얻을수있다는 것입니다.

주소값의 위치를 찾기위해선, 포인터 선언때 사용했던 * 연산자를 변수 명 앞에 적어주면 됩니다.


그럼 여기서 num의 값이 변화한다면 어떻게 될까요?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<stdio.h>
 
int main(void)
{
    int *pnum;         // 포인터 변수 선언
    int num = 10;     // 일반변수 선언 & 초기화
    pnum = &num;     // num의 주소값을 pnum에 저장
 
    printf("num의 값: %d\n", num);
    printf("num변수의 주소:%p\n", &num);
    printf("pnum변수의 값 :%p\n", pnum);
    printf("pnum이 가리키는 값: %d\n"*pnum);
    printf("num값 증가 +10 \n");
    num+=10;
    printf("pnum이 가리키는 값: %d\n"*pnum);
 
/*
결과값
num의 값: 10
num변수의 주소:001919fC
pnum변수의 값 :001919fC
pnum이 가리키는 값: 10
num값 증가 +10
pnum이 가리키는 값: 20
*/
}

cs


주석 처리된 출력값을 보면 마지막에 num의 값이 증가했는데 pnum이 가리키는 값 또한 증가한걸 볼수있습니다.

당연하겠죠?

이해가 안된다면, pnum이 가지고있는 값은 주소값이며, *pnum으로 가리키는 주소로 갔더니 값이 있어서 그걸 출력한것뿐입니다.


집주소를 알고있지만, 같은 주소에 다른사람이 살고있는것과 같겠죠?




◎ 포인터의 초기화


포인터의 초기값은 무엇으로 해야할까요? 

1
2
3
4
5
6
7
#include<stdio.h>
 
int main(void)
{
    int *pnum;         // 포인터 변수 선언
    *pnum = 100;
}

cs

위와 같이 코딩해 실행한다면 어떻게 될까요?

물론 컴파일러의 도움을 받아서 컴파일을 해주지 않겠지만, 이론대로라면 초기화 되지않은 포인터 변수는 쓰레기값.. 어딘지 모르는 주소값을 가지고있어서, 컴퓨터가 사용하는 어느 일정 부분의 메모리를 갑자기 100으로 덮어씌울수도있고, 그에따라 치명적인 오류를 뱉어 낼수도있습니다.


따라서 포인터 변수의 초기화는 NULL 또는 0 으로 하면 됩니다.

1
2
3
4
5
6
7
#include<stdio.h>
 
int main(void)
{
    int *pnum01 = null;         // 포인터 변수 선언
    int *pnum02 = 0;         // 포인터 변수 선언
}
cs

NULL은 아스키 코드값으로 0을 나타내며, 포인터 변수에 들어간 0은 0번째 주소를 가리키는게 아닌, "아무것도 없다." 라는 뜻을 지니게 됩니다.






Posted by 시리시안
2016. 2. 23. 07:58

저번 포스트에 앞내용을 그대로 가져오겠습니다.


C언어 연산자. 

C연어 연산자는 C언어의 아주 기초적인 내용입니다.

아주 기초적이기 때문에 소홀히 하기 쉬운 부분이기도 합니다.

C언어에서 연산자가 상당히 많은 중요한 역할을 하고 있다 할 수 있지만, 주목받지 못하는 이유는 이해 하기가 너무 쉽고, 소프트웨어 관련 프로그램에서는 문법보다 많은 비중을 차지하지 못하기 때문입니다.

그럼에도 연산자를 알아야 하는 이유는, 당연히 C언어 코딩 과정이 게임을 만드든, 뭘 만드든 연산자와 함수를 통해서 이루어지기 때문이죠.

특히, 비트연산자는 하드웨어와 관련된 신호를 처리할 때 도움이 되는 개념으로 꼭 알아야 합니다.

 

 

※ 연산자 종류


분류 

연산자 

 대입 연산자 

 

 산술 연산자 

 +, -, *, /, %

 복합 대입 연산자

 +=, -=, *=, /=, %=

 증감 연산자 

  ++, --

 관계 연산자 

  >, <, ==, !=, >=, <=

 논리 연산자 

  &&, ||, !

 조건 연산자

  ? :

 비트 논리 연산자 

  &, |, ^, ~

 비트 이동 연산자 

 >>, <<


대입 연산자 부터 조건 연산자 까지는 이전 포스팅에서 작성 되었습니다.


http://silisian.tistory.com/35


◎ 비트 논리 연산자


비트 논리 연산자는 총 &, |, ^, ~ 4개를 가지고있습니다.


비트 논리 연산자 

구분 

& 

 비트 곱 (AND)

 비트 합 (OR)

^ 

 베타적 비트 합 (XOR)

 비트 부정 (NOT)


◎ 비트 곱은 비트 라는 것을 제외하고 생각하면 곱셈연산과 같습니다. 단, 비트별로 곱합니다.

◎ 비트 합은 비트 라는 것을 제외하고 생각하면 덧셈연산과 같습니다. 단, 비트별로 덧셈하며 결과값은 참/거짓(1/0)으로만 표현합니다.

◎ 베타적 비트 합은 비트별로 연산하며 같으면 거짓을 다르면 참을 반환합니다. 비트별 != 연산이라 생각하면 간단합니다.

◎ 비트 부정은 Not연산이며, 비트 보수 연산자라고 불리기도 합니다. 자기자신의 비트별 !연산이라 생각하면 간단합니다.


◎ 비트 곱 (AND)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<stdio.h>
 
void main()
{
 
    /*
    비트 곱은 비트별로 연산 한다느넛만 빼면 논리 곱과 똑같습니다.
    A(1100) 과 B(1010)을 비트 곱 한다 하면
    차례대로 
    1&1 = 1            (1*1 = 1)
    1&0 = 0            (1*0 = 0)
    0&1 = 0            (0*1 = 0)
    0&0 = 0            (0*0 = 0)
    이 나오게 됩니다. 오른쪽 처럼 생각하면 간단하죠?
    */    
 
    //예제 소스
    
    char Data = 0x86;         //16진수인 0x86은 2진법으로 표현하면 1000 0110 입니다.
    char BitMask = 0xf2;    //16진수인 0xf2는 2진법으로 표현하면 1111 0010 입니다.
    char Result = Data & Bitmask;    //이 2개의 수를 비트곱 연산을 하게되면, 값은 1000 0010이 나오게됩니다.
    // 즉 0x82가 나오게 됩니다.
 
    /*
    연산
    1 & 1 = 1
    0 & 1 = 0
    0 & 1 = 0
    0 & 1 = 0
    0 & 0 = 0
    1 & 0 = 0
    1 & 1 = 1
    0 & 0 = 0
    */
}
cs



◎ 비트 합 (OR)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<stdio.h>
 
void main()
{
 
    /*
    비트 합은 비트별로 연산 한다는것만 빼면 논리 합과 똑같습니다.
    A(1100) 과 B(1010)을 비트 합 한다 하면
    차례대로 
    1|1 = 1            (1||1 = 1)
    1|0 = 1            (1||0 = 1)
    0|1 = 1            (0||1 = 1)
    0|0 = 0            (0||0 = 0)
    둘중 하나라고 비트값이 1 이면 1을 반환합니다.
    논리연산자중 ||을 생각해서 둘중 하나라고 참이면 참을 반환한다. 라고 생각하면 간단합니다.
    */    
 
    //예제 소스
    
    char Data = 0x86;         //16진수인 0x86은 2진법으로 표현하면 1000 0110 입니다.
    char BitMask = 0xf2;    //16진수인 0xf2는 2진법으로 표현하면 1111 0010 입니다.
    char Result = Data | Bitmask;    //이 2개의 수를 비트곱 연산을 하게되면, 값은 1111 0110이 나오게됩니다.
    // 즉 0xf6가 나오게 됩니다.
 
    /*
    연산
    1 | 1 = 1
    0 | 1 = 1
    0 | 1 = 1
    0 | 1 = 1
    0 | 0 = 0
    1 | 0 = 1
    1 | 1 = 1
    0 | 0 = 0
    */
}
cs


◎ 베타적 비트 합 (XOR)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<stdio.h>
 
void main()
{
 
    /*
    베타적 비트 합은 비트별로 연산 한다는것만 빼면 논리연산자중 != 과 똑같습니다.
    A(1100) 과 B(1010)을 베타적 비트 합 한다 하면
    차례대로 
    1^1 = 1            (1!=1 = 0)
    1^0 = 1            (1!=0 = 1)
    0^1 = 1            (0!=1 = 1)
    0^0 = 0            (0!=0 = 0)
    두개의 값이 서로 다르다면 1 같으면 0을 반환합니다.
    논리연산자중 !=을 생각해서 둘이 다르다면 참을 반환 한다고 생각하면 간단합니다.
    */    
 
    //예제 소스
    
    char Data = 0x86;         //16진수인 0x86은 2진법으로 표현하면 1000 0110 입니다.
    char BitMask = 0xf2;    //16진수인 0xf2는 2진법으로 표현하면 1111 0010 입니다.
    char Result = Data ^ Bitmask;    //이 2개의 수를 비트곱 연산을 하게되면, 값은 0111 0100이 나오게됩니다.
    // 즉 0x74가 나오게 됩니다.
 
    /*
    연산
    1 ^ 1 = 0
    0 ^ 1 = 1
    0 ^ 1 = 1
    0 ^ 1 = 1
    0 ^ 0 = 0
    1 ^ 0 = 1
    1 ^ 1 = 0
    0 ^ 0 = 0
    */
 
    //여기서 특이한점은, 베타적 비트합을 2번 연산 하게되면 원래 값으로 돌아가게 됩니다.
 
    Result = Result ^ Bitmask;    // (0111 0100) ^ (1111 0010) = 1000 0110
 
    //이처럼 XOR 즉 베타적 비트 합은 암호화의 자주 사용 됩니다.
}
cs

◎ 비트 부정 (NOT)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<stdio.h>
 
void main()
{
 
    /*
    비트 부정은 자기자신의 비트별로 연산 한다는것만 빼면 논리연산자중 ! 과 똑같습니다.
    논리연산자중 앞에 !을 붙이면 참과 거짓이 바뀌듯 비트값앞에 !을 붙이게되면 비트의 0과 1이 바뀌게 됩니다.
    따라서 A(1100)를 ~A 한다 하면
    차례대로 
    ~1 = 0            
    ~1 = 0            
    ~0 = 1            
    ~0 = 1            
    1은 0으로 0은 1로 연산하게 됩니다.
    */    
 
    //예제 소스
    
    char Data = 0x86;         //16진수인 0x86은 2진법으로 표현하면 1000 0110 입니다.
    char Result = ~Data;    //Data를 부정(~) 하게되면 0111 1001이 됩니다.
    // 즉 0x79가 나오게 됩니다.
 
    /*
    연산
    ~1 = 0
    ~0 = 1
    ~0 = 1
    ~0 = 1
    ~0 = 1
    ~1 = 0
    ~1 = 0
    ~0 = 1
    */
}
cs



◎ 비트 이동 연산자


비트 이동 연산자는 총 << 과 >> 가지고있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
 
void main()
{
 
    /*
    비트 이동 연산자는 비트의 위치를 왼쪽 또는 오른쪽으로 온긴다고 생각하면 됩니다.
    A(1101)를 B = A<<2; 라 하면 B의 값은 0100이 됩니다.
    A를 왼쪽으로 2번 쉬프트 하면 11 0100 이 됩니다. 하지만 범위를 초과한 비트는 버려지게 됩니다.
    따라서 0100만 남아서 B의 값이 됩니다. ( A값은 변하지 않습니다. )
    */    
 
    //예제 소스    
    char Data = 0x76;         //16진수인 0x86은 2진법으로 표현하면 0111 0110 입니다.
    char Result01 = Data<<2;    //Data를 <<2 하게되면 1101 1000이 됩니다.
    char Result02 = Data>>2;    //Data를 >>2 하게되면 0001 1101이 됩니다.
    char Result03 = Data<<8;    //Data를 <<8 하게되면 0000 0000이 됩니다.
}
cs



참고 자료 1 : https://ko.wikipedia.org/wiki/%EB%85%BC%EB%A6%AC_%EC%97%B0%EC%82%B0

참고 자료 2 : http://www.tipssoft.com/bulletin/board.php?bo_table=FAQ&wr_id=598

참고 자료 3 : https://msdn.microsoft.com/ko-kr/library/336xbhcz.aspx





 



'프로그래밍 > Language' 카테고리의 다른 글

try, throw, catch 를 이용한 예외 처리 방법  (0) 2016.02.23
포인터에 대하여  (0) 2016.02.23
C언어 연산자에 대하여  (0) 2016.02.23
상수란 무엇인가?  (0) 2016.02.22
변수란 무엇인가?  (0) 2016.02.22
Posted by 시리시안