이미 생성된 인스턴스에 다른 인스턴스를 대입할 때 대입 연산자(=)가 이용되며 실제로는 복사가 수행된다. 다음과 같은 간단한 클래스를 고려해 보자.


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

class Led {

    public :

        int pin;

        char *name;

};

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


이 클래스는 복사 생성자도 없고 =연산자도 오버로딩되지 않았다. 이 경우 디폴트=연산자 함수는 얕은 복사를 수해하게 된다. 이제 다음과 같은 두 예를 보자


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

//ex1

Led led1;

led1.pin=13;

Led led2 = led1; // 복사생성자 호출


//ex2

Led led1, led2;

led1.pin=13;

led2 = led1; // 대입 함수 호출

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


<ex1>에서 led2가 생성될 때는 복사생성자가 실행되고  <ex2>에서는 대입 연산 함수가 호출된다.


하지만 멤버변수에 포인터가 있으므로 여기에 문자열이 저장된 경우 얕은 복사는 문제가 일어나게 된다. 


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

Led led1, led2;

led1.name = new char[3];

strcpy(led1.name, "hi");


led2 = led1; // 얕은 복사가 일어나므로 문제를 야기함.

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


심지어 깊은 복사를 수행하는 복사생성자가 정의되어 있어도 대입 연산자는 얕은 복사만을 수행하므로 주의해야 한다. 즉, 대입연산은 자동으로 사용자가 정의한 복사 생성자를 호출하지 않는다. 아래와 같이 깊은 복사를 수행하는 복사 생성자를 작성했다고 하자.


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

class Led {

    public :

        int pin;

        char *namer;

        Led(const Led& src) {

            pin = src.pin;

            name = new char[strlen(src.name)+1];

            strcpy(name, src.name);

        }

};

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


복사 생성자는 생성자이므로 인스턴스가 새로 생성될 때 수행된다. 따라서 name필드에 이전에 메모리를 할당한 적이 없을 것이니 해제할 필요도 없다.


  이렇게 작성한 뒤에서 대입연산은 여전히 얕은 복사를 수행하게 된다. 즉, 복사생성자와 대입연산자는 별개이다.  대입 시에도 깊은 복사를 수행하려면 대입연산자를 오버로딩하여 사용자가 작성해주어야 한다. 한 가지 주의할 점은 대입연산자는 이미 생성된 인스턴스에다 복사해 넣는 것이므로 기존의 문자열을 해제하는 코드가 추가로 필요하다는 것이다. 이것이 대입연산자와 복사생성자의 차이이다.


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

Led& Led::operator= (const Led& src) {

    if (this == &src) return *this; // 자기 대입 방지


    pin = src.pin;

    i(name != NULL) delete[] name; // (주의)

        name = new char[strlen(src.name)+1];

        strcpy(str, src.name);


    return *this;

}

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


이제는 대입연사자를 사용하면 깊은 복사가 일어나게 된다. 만약 복사 생성자와 코드가 많이 중복된다면 중복되는 부분은 private 멤버 함수로 따로 작성하는 것이 더 효율적일 것이다.

[#00063]


Posted by 살레시오
,

_ (1) 정수형 상수 


 표에서 분류한 것과 같이 정수형은 크게 다섯 가지(char, short, int, long, long long)의 자료형이 있으며 바이트 수가 클수록 숫자 데이터의 범위도 커지게 된다. 정수형 변수에는 정수형 상수를 저장할 수 있으며 소수점(혹은 e/E)가 없는 숫자는 정수형 상수이다.


  예를 들어 부호가 있는 int 형 변수 ia 를 숫자 0으로 초기화하려면 다음과 같이 하면 된다.


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

int ia = 0;

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


이후에는 ia라는 int형 변수를 '사용'하여 정수 값을 저장하거나 산술 연산을 수행할 수 있다. 


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

int ia = 0;

ia = 34*567; // *는 곱셈 연산자임

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


  정수형 상수는 기본적으로 int형으로 간주되므로 저장하는데 내부적으로 4 byte(혹은 2 byte)가 소요가 된다. 만약 숫자 상수를 특정한 자료형으로 지정해 주고 싶다면 접미어를 붙여서 그 데이터형을 지정할 수 있는데 U(혹은 u)를 붙이면 unsigned int형이고 l(혹은 L)은 long형, ul(혹은 UL)은 unsigned long형임을 나타낸다.



접미어

자료형

예제

u/U

unsigned int

37u, 123U

l/L

long

12l, 12L

ul/UL

unsigned long

89ul, 89UL


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

unsigned int uia = 0u;

unsigned long ula = 123ul;

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


하지만 보통은 숫자 상수는 대입하려는 변수의 자료형에 자동으로 변환되어 저장되므로 굳이 이렇게 일일이 자료형에 맞추어 접미어를 붙여줄 필요는 없다. 즉,


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

unsigned long ula = 123; 

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


이라고 초기화하여도 123이라는 int형 상수는 자동으로 unsigned long 형으로 변환된 후 변수 ula에 저장된다.


  만약 숫자상수를 short 형이나 unsigned char 형으로 지정하고 싶다면 다음과 같이 해야 한다.


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

short sa = (short)1; 

unsigned char uca = (unsigned char)123;

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


이와 같이 숫자 상수 앞에 (자료형) 을 붙여기 자료형을 강제로 변환하는 것을 형변환(casting)이라고 한다. 하지만 이 경우도 아래와 같이 할 수 있다. 자동으로 형변환이 일어나기 때문이다.


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

short sa = 1; 

unsigned char uca = 123;

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


  한 가지 알아두어야 할 사실은 C/C++언어에서 정수를 표현할 때 0으로 시작되는숫자는 8진수이고, 0x로 시작하는 숫자는 16진수라는 것이다. 예를 들어서 123은 그냥 10진수 123이지만 0123 이라고 입력하면 8진수 123이기 때문에 10진수로 변환하면 83이다. 그리고 0x123은 16진수 123이기 때문에 10진수로 변환하면 291이 된다. 이것을 다음 표에 정리하였다.



숫자 상수

십진수값

비고

123

123

10진수

0123

83

8진수

0x123

291

16진수

0b1110

14

2진수(gcc에서 지원, C++14)


(2) 문자 상수


  C/C++ 언어에서는 정수형 변수에 정수뿐만 아니라 문자를 저장할 수 있으며 특별히 char형과 int형은 문자를 저장하는 목적으로도 자주 사용된다. 문자상수는 작은 따옴표(‘’)를 이용하여 표현되며 예를 들면 ‘a’, ‘+’, ‘0’ 등이 있다. 문자상수는 아스키(ASCII)코드라는 고유의 상수값이 정해져 있는데 다음 표에 몇몇 문자상수와 아스키코드값을 수록하였다.



종류

문자와 ASCII 코드

숫자 (문자)

ASCII 코드

‘0’

48

‘1’

49

‘2’

50

‘9’

57

알파벳 대문자

ASCII 코드

‘A’

65

‘B’

66

‘C’

67

...

‘Z’

90

알파벳 소문자

ASCII 코드

‘a’

97

‘b’

98

‘c’

99

...

‘z’

112

특수문자

ASCII 코드

‘&’

38

‘*’

42

‘+’

43




  char형은 크기가 1바이트이므로 부호 없는 unsigned char 변수인 경우 0부터 255까지의 작은 숫자를 저장할 수 있으며 short형은 2바이트로 부호 없는 숫자의 경우 0부터 65,535까지 저장할 수 있다. 만약 char형 변에 문자상수, 예를 들어서 ‘a’가 저장되면 내부적으로는 97이라는 숫자가 저장된다는 것을 알아야 한다.




bit7

bit6

bit5

bit4

bit3

bit2

bit1

bit0

‘a’ (97)

0

1

1

0

0

0

0

1


  부호가 있는 정수형 데이터는 음수를 2의 보수로 저장하기 때문에 저장범위가 char형은 –128~+127이 되고 short형은 –32,768~+32,767이 된다.


Posted by 살레시오
,

2.3 기본 자료형    [DOC]    [SMTS]

 자료형(data type)이란 정수, 실수, 문자 등과 같이 프로그램에서 사용되는 자료(data)의 종류를 의미한다. 사용하고자 하는 데이터의 종류가 정해지면 그것을 저장할 변수는 그것에 맞추어서 만들어져야 한다.  C 언어는 기본 자료형을 제공하고 사용자들이 필요에 따라 새로운 자료형을 정의할 수 있는 방법을 제공하고 있다.


 표준 C언어에서 제공하는 기본적인 자료형을 다음 표에 정리하였다.


[표 2.1.1] C언어의 기본 자료형

분류

데이터형

byte

범위

비고

정수형

(signed) char

unsigned char

1

-128~ 127

0~255


(signed) short

unsigned short

2

-32,768 ~ 32,767

0 ~ 65,535


(signed) int


unsigned int

4/2

-2,147,483,648 ~ +2,147,483,647

0~4,294,967,295


(signed) long

unsigned long

4

-2,147,483,648 ~  +2,147,483,647

0~4,294,967,295


(signed) long long

unsigned long long

8

-2^63 ~ 2^63-1

0 ~ 2^64-1

C99

C99

실수형

float

4



double

8



long double

12


C99

논리형

bool

boolean

1

1

true, false

true, false

C99(1)

C++

  1. stdbool.h 를 인클루드 한 후에 사용가능하다.


 자료형은 크게 정수형과 실수형, 논리형으로 나뉜다. 정수형은 부호가 있는 signed 형과 부호가 없는 unsigned 형이 별개로 존재하지만 실수형은 그렇지 않다. 또한 각각의 자료형은 정해진 크기가 있으나 PC환경에 따라 그 크기가 변화하는 데이터 형이 있음에 주의해야 한다. 대표적인 것이 int형과 long double형이다.


 논리형은 true, false 두 가지 값만을 가지는 자료형이다. C 언어에서는 C99이후에 stdbool.h 라는 표준 헤더 파일을 인클루드하면 bool형을 사용할 수 있다. (C++은 boolean 자료형이 기본형으로 제공된다.)


 변수(variable)는 자료를 담는 그릇이다. 변수를 사용하려면 반드시 먼저 선언을 해야 하며 문법은 다음과 같다.


자료형 변수명;

예를 들어서 int 형 변수 ia를 사용하려면 다음과 같이 선언을 해야 한다.


int ia;

이렇게 선언을 한 이후에는 이 변수에 값을 대입한다든지 계산을 하는데 사용할 수 있다.


int ia; // 선언
ia = 100; //사용(초기화) 정수값을 대입
ia = 12 * 34; //사용 :곱셈 계산

변수의 '초기화'는 선언된 이후에 맨 처음으로 어떤 값을 대입하는 것을 의미한다. 위와 같이 변수를 선언한 후 초기화를 별도로 할 수도 있고 선언과 동시에 초기화를 수행할 수 도 있다.


long la = 0; // 선언과 동시에 초기화
double da = 1.0; // 선언과 동시에 초기화

이렇게 한 이후에는 자유롭게 변수를 사용할 수 있다.


ex02-01.c

#include <stdio.h>

int main() {
short sa = 11;
int ib = sa + 12; // sa값과 12를 더한다.
int ic = sa * ib; // sa값과 ib값을 곱한다.

printf("%d, %d, %d", sa, ib, ic);
}

실행 결과

11, 23, 253


이 예는 변수 sa, ib, ic를 생성하여 그 값을 printf()함수를 이용하여 화면에 출력하는 것이다. 컴파일한 후 실행해서 결과를 직접 확인해 보자.



Posted by 살레시오
,