카테고리 없음

[C#프로그래밍 입문] 데이터를 옮겨 담는 방법 (형 변환)

Bordercolli 2023. 8. 4. 15:58
728x90

데이터형 변환, 오버플로우

 

형 변환(type conversion): 데이터 옮겨담기, 데이터의 형식을 변경해 데이터를 담는 과정

기본 값이 double인 이유는 최근 운영체제들이 64byte인 경우가 많아서

64bit운영체제라고 한다면 한 번에 값을 보낼때 64bit를 보내는 것을 의미한다.

만약 64bit운영체제에서 32bit의 값을 보낼때 나머지 32bit는 비어있는 채로 보내거나 의미없는 값을 보낸다.

그래서 강제적으로 float데이터형을 쓰려면 0.0F처럼 뒤에 F를 붙여 suffix를 추가해야 오류가 없이 데이터 형이 변환된 것을 볼 수 있다.

 

오버플로우(overflow): 데이터 형식을 변환하는 과정에서 데이터 손실이 발생하는 현상

오버플로우를 예방하기 위해서 데이터 형과 예방방법을 살펴본다. 

하나의 bit 안에 저장할 수 있는 값은 0 또는 1이다.

두 개의 bit안에 저장할 수 있는 값은 4개 00 01 10 11

이처럼 비트수가 증가할때마다 저장할 수 있는 값이 늘어난다. 

만약 두자리 비트인데, 100(4)를 저장하고 싶다면 00밖에 저장이 안된다. 

값을 더 저장하고 싶지만 2bit밖에 저장할 수 없기 때문에 값이 다시 처음부터 0을 출력하는 모습을 볼 수 있다.

using System;
using System.ComponentModel.Design.Serialization;

namespace TestProject
{
    class Program
    {
        static void Main(string[] args)
        {  
            sbyte value=128;    // sbytes는 -128~127까지 표현한다. 근데 128은 이 범위에 표현할 수 없음.
            Console.WriteLine(value);
           
        }
    }
}

128은 sbyte로 변환할 수 없어서 오버플로우 발생해 오류 발생

컴파일러 자체에서 오버플로우를 해결하는 모습을 볼 수 있다. 만약 컴파일러가 해당 기능을 지원하지 않으면 코드를 작성할 때는 문제가 없지만 나중에 동작할 때 문제가 발생한다. 

static void Main(string[] args)
        {  
            sbyte value=18;    // sbytes는 -128~127까지 표현한다. 근데 128은 이 범위에 표현할 수 없음.
            Console.WriteLine(value);
           
        }

오류발생없이 value값이 그대로 나오는 모습을 확인할 수 있다. 

static void Main(string[] args)
        {  
            sbyte value1=64;
            sbyte value2=64;
            sbyte value3=value1+value2;
            Console.WriteLine(value3);
        }

이러면 value3에 오류가 발생한다. 

 

명시적 형변환, 암시적 형변환

 

value1, 2는 범위 내에 있으니 오류가 없지만 value3는 범위 내에 없어서 오류가 발생한다. 

문제는 64+64=128로 연산하는 과정에서 overflow가 발생했다. 

이때 오류는 "int 형식을 sbyte형식으로 암시적으로 변환할 수 없다. 다만 명시적 변환은 가능하다"라는 대안을 주었다.  "캐스트가 있는지 확인하세요" 라고 제안을 하는데 cast는 type을 의미한다. 즉 형변환

오류내용들을 보면 value1과 value2의 값을 더했을때 각각은 sbyte인데, 더했을때 data를 저장할 수 있는 영역이 초과하면 자동으로 형 변환이 된 것이다. 이것을 암시적 형변환이라고 한다.

그런데 문제는 형변환이 되어서 value1+value2의 값은 int형이 되었다. 즉 정수형이 되었다.

이 값을 sbyte에 넣으니 문제가 된 것이다.

이를 해결하는 방법은

 static void Main(string[] args)
        {  
            sbyte value1=64;
            sbyte value2=64;
            int value3=value1+value2;    // sbyte가 아니라 int를 앞 자료형으로 넣어서 형변환을 한다. 
            Console.WriteLine(value3);
        }

이런 방식을 암시적 형변환 방식이라고 한다. 자동으로 형변환된 것을 그 형에 맞게끔 저장한 것을 의미한다. 

이는 컴파일러가 자동으로 알아서 데이터의 범위를 초과했을때, 형변환되는 것을 암시적 형변환이라고 한다.

 

반면 아래 처럼 프로그래머가 자체적으로 형변환을 하게 만들 수도 있다.

static void Main(string[] args)
        {  
            sbyte value1=64;
            sbyte value2=64;
            int value3=(int)value1+value2;   // 암시적 형변환과 명시적 형 변환이 모두 사용된 경우
            Console.WriteLine(value3);
        }

(int)처럼 자체적으로 형변환을 할 수 있도록 한것을 '명시적 형변환'이라고 한다.

value1은 원래 sbyte라는 자료형을 가지고 있었는데, 앞에다가 중괄호 안에 바꾸려고 하는 데이터 형을 넣으면 sbyte가 

int 로 변환된다.

value1부분은 명시적 형변환이 되고 그리고 이 두 값의 결과는 더 큰 데이터형으로 자동으로 형변환이 된다.

 

암시적 형변환: 맨 앞에 변환하고자하는 자료형를 입력하는 경우 ex) int value;

명시적 형변환: ()안에 변환하고자하는 자료형을 입력하는 경우 ex) (int)value;

 

static void Main(string[] args)
        {  
            sbyte value1=64;
            sbyte value2=64;
            sbyte value3=(int)value1+value2;    // 오류가 발생함 
            Console.WriteLine(value3);
        }

 

클래스와 인스턴스 

클래스 인스턴스
1. 설계도
2. 클래스는 그 클래스들이 가져야할 성질들을 다 정리하고 있음. 
1. 설계도를 실체화한 것 
2. 클래스를 메모리에 올려서 사용하는 것

클래스와 인스턴스 모두를 객체라고 하고, 객체안에는 정적인 성질들을 attribute/ property/ 멤버변라고 한다. 

그리고 동적인 성질(말을 하거나 동적인 행동을 취하거나)들은 method를 통해서 구현을 한다. 

즉 class마다 이런 attribute와 method가 있기 때문에 쉽게 쓸 수 있다. 

그러면 코드 상에서는 이런 클래스와 인스턴스가 만들어지고 활용할 수 있는지 살펴본다.

 

using System;    // System이라고 하는 namespace안에 consoles라는 클래스를 사용하겠다. 

// 클래스의 시작
class MainClass{
    public static void Main(string[] args)
    {
        // 코드영역
    }
} // 클래스의 끝

1. using system은 namespace였었다. class들의 집합인 시스템이라고 하는 namespace안에서 우리가 consoles라는 클래스를 가져와서 사용했다. 이는 직접 우리가 클래스를 만드는 것이 아니라 기존에 만들어져있는 클래스를 우리가 가져와서 쓰는 경우에 해당된다. 

// 클래스 형식

using System.IO.Compression;

class 클래스명{  // 클래스 시작

    // 내부에는 attribute 또는 method를 가지고 있다.
    public static void Main(string[] args)
    {
        /* 지금과 같은 경우에는 main method하나만 있다. 그 외에도 추가적으로 method를
        생성하거나 attribute를 선언할 수 있다.  */
    }


} // 클래스 끝

△ 클래스 작성 방법

△ 코드 설명

person 이라는 클래스를 하나 만들고, 사람의 정적인 특징(이름, 생년월일, 성별 등)들을 속성으로 정의를 한다.

그리고 매소드로는 Eat(), Walk(), Run() 등 먹는 행위, 걷는 행위, 뛰는 행위들을 할 수 있게 끔 만들었다.

class는 사람이 할 수 있는 모든 것을 구현하는 것이 아니라 목적에 맞게 끔 필요한 부분만 설게하는 것을 추상화하라고 합니다. 즉 원래 사람이 할 수 있는 영역들을 프로그램마다 원하는 바가 있기 때문에 그 안에서 필요한 것들만 구현하는 것

여기서는 임의로 먹는 행위를 Eat(), 걷는 행위를 Walk(), 뛰는 행위를 Run()으로 해당 행위들을 메소드로 정의하였다. 

 

구조: 시작은 class 키워드를 사용하고, class의 이름 보통 class의 이름은 앞자를 대문자로 쓴다. 'Person' 이런 식으로 

간혹 class의 바깥에 매소드를 구현하는 경우가 있는데, 그럴 경우에는 syntax 오류가 발생한다. 

 

★ 반드시 class내부에다가 메소드, 프로퍼티를 선언해야 한다.  

★ 그리고 메소드 안에다가 또 다른 메소드를 넣는 경우를 볼 수 있는데, 이건 무식한 행위는 하지말라. ★

★ 메소드는 반드시 class안에다가 선언을 해야 한다. ★ 

 

 

실제 어떻게 클래스를 인스턴스로 만드는지 과정

 

▽ 해석과정

 

class MainClass{
    public static void Main(string[] args)
    {
        Person p1;   // person이라는 class이름으로 p1이라는 인스턴스 변수 만듦
        // 이 인스턴스를 실제로 사용할 수 있으려면 일단은 메모리 영역이 할당되지 않음.
        p1=new Person();  
        /*new라는 키워드를 써서 class의 이름을 쓰고 중괄호를 열고 닫으면 p1이라는
        변수에 하나의 인스턴스를 쓸 수 있게끔 메모리 공간을 할당받음 */
        p1.Name="서준"; /*인스턴스의 변수로 특정한 프로퍼티나 매서드를 호출
        하기 위해서 . 이라는 연산자를 사용한다.
        p1.name이라고 하면 name이라는 프로퍼티 접근할 수 있다.
        그래서 "서준"이라는 값을 네임이라는 프로퍼티에다가 저장함.*/
        p1.Eat();
        /*eat라는 매서드를 호출하면 name에 해당하는 문장이 출력됨*/
    }
}
using System;

class Person{
    public string Name;

    public void Eat(){
        Console.WriteLine(Name + "(이)가 식사를 합니다.");
    }
}
/*하나의 프로퍼티와 하나의 매소드가 있는 perosn이라는 클래스를 생성함. */
class Program{
    public static void Main(string[] args) {
        Person p1; /* 이 상태는 변수만 만든 것 안에 값이 없듯이 인스턴스변수만 있고 아직 어떤
        내용을 참조할 수 있을지 결정하지 않은 상태*/
        /*이 person을 생성하기 위해서 mainclass에서는 클래스의 이름이 데이터의 이름이 되고
        데이터형 뒤에는 변수명을 작성하듯이 인스턴스 변수명을 하나 만들어준다.*/
    }
}
Person p1;    /*이 상태로 바로 초기화가 가능하다.*/
using System;

class Person{
    public string Name;

    public void Eat(){
        Console.WriteLine(Name + "(이)가 식사를 합니다.");
    }
}
/*하나의 프로퍼티와 하나의 매소드가 있는 perosn이라는 클래스를 생성함. */
class Program{
    public static void Main(string[] args) {
        Person p1=new Person();  /*초기화 방법: new라고 작성하고 ()를 하면 초기화까지 된다. */
        p1.Name="서준";
        /*p1이라고 하는 name의 인스턴스 안에는 name에 서준이라고 하는것이 들어간 것이고, method를 호출하는
        것은 p1.하고 매서드 이름을 쓰면 된다. 해당 매서드를 호출하면 실행했을때, 인스턴스 변수에 이름을 초기화하고
        eat이라고 하는 매서드가 호출되는 것 이러면 "서준(이)가 식사를 합니다."가 호출된다."*/
    }
}

정리: 클래스를 생성, 클래스에 프로퍼티 매소드를 만들어서 그걸 인스턴스로 만든 다음에 호출하는 과정

다음: 클래스 시작/ 종료