728x90

정리

C는 두가지 접근 방법으로 비트들에 접근한다.

1. 비트 단위 연산자들을 사용

2. 구조체 비트 필드 생성

 

컴퓨팅 하드웨어는 2진수 체계와 일접한 관련이 있다.

메모리와 레지스터에 있는 비트들의 on/off 상태를 2진수인 1과 0으로 나타낼 수 있기 때문이다.

 

2진수와 관련된 8진수 표기와 16진수 표기를 인식한다. 2진수 숫자 하나가 1비트를 나타내듯이, 8진수 숫자 하나는 3비트를 나타내고, 16진수 숫자 하나는 4비트를 나타낸다. 이러한 관계로 인해 2진수를 8진수나 16진수로 쉽게 변환할 수 있다.

 

C는 몇가지 비트 단위 연산자를 제공한다. 비트단위 연산자는 어떤 값에 있는 개별적인 비트들을 독립적으로 처리할 수 있기 때문이다.

1. 비트 단위 부정 연산자(negation : ~) : 피연산자에 있는 각 비트들을 반전시킨다. 즉 1->0, 0->1

2. 비트 단위 논리곱 연산자(AND : &) : 두 피연산자로 부터 하나의 값을 만든다. 서로 대응하는 위치에 있는 각 비트들이 둘다 1이면 결과 비트가 1이고 둘중에 하나 이상이 0이라면 해당 결과 비트도 0이다.

3. 비트 단위 논리합 연산자 (OR : |) : 두 피연산자로 부터 하나의 값을 만든다. 이때 두 피연산자의 서로 대응하는 비트중 하나라도 1이면 결과 비트가 0이 된다.

4. 비트 단위 배타적 논리합 연산자(Exclusive OR : ^) : 두 피연산자로 부터 하나의 값을 만든다. 대응하는 비트가 다를때 1이고 대응하는 비트가 0이면 결과 비트도 0이다.

 

C는 왼쪽 시프트 연산자(<<)와 오른쪽 시프트 연산자(>>)를 제공한다.

이 두 연산자는 주어진 비트 수만큼 비트 패턴을 왼쪽이나 오른쪽으로 이동시켜 새로운 값을 만든다. 왼쪽 시프트 연산의 경우 이동시키면서 비워지는 비트에 0을 채운다. 오른쪽 시프트 연산의 경우 값이 unsigned 형이라면 비워지는 비트에 0을 채우지만, signed 데이터형의 경우 컴파일러에 따라 다르다.

 

728x90

'Programming > C' 카테고리의 다른 글

C #define에 전달인자 사용하기  (0) 2019.07.10
C 전처리기와 C 라이브러리  (0) 2019.07.10
C 비트 필드  (0) 2019.07.10
C 비트 연산  (0) 2019.07.10
C언어 공부 37  (0) 2019.07.08
728x90

비트를 조작하는 두번째 방법은, 비트필드(bit field)를 사용하는 것이다. 비트 필드는 하나의 signed int 형 또는 하나의 unsigned int형 안에 있는 이웃하는 비트들의 집합이다.

 

비트 필드는 각 필드에 레이블을 붙이고, 그들의 크기를 지정하는 구조체 선언을 통해 설정한다. 비트 필드들로 이루어진 구조체는 하나의 단위 안에 여러개의 설정을 저장할 수 있다.

ex)

struct Flags {

unsigned int a : 1;          //1비트 크기

unsigned int b : 4;            //4비트 크기

unsigned int c : 7;          //7비트 크기

}bits;

 

위 코드는 1비트짜리 필드 1개와 4비트짜리 필드 1개, 7비트짜리 필드 1개를 만든다.

각 필드에 값을 대입할 수 있다.

bits.a = 0;

bits.b = 3;

bits.c = 102;

대입하는 값이 필드의 수용능력을 벗어나지 않도록 주의하라.

 

선언한 비트들의 총 개수가 unsigned int의 크기를 초과하면 그 다음 위치의 unsigned int 기억 장소가 사용된다.

 

하나의 필드가 두 unsigned int의 경계에 양다리를 걸칠 수 없다. 경계에 양다리를 걸치는 필드 정의는 컴파일러가 unsigned int 경계에 맞게 자동으로 정렬한다.

 

테이블이 없는 필드 크기를 지정하여, 레이블이 없는 구멍이 있는 필드 구조체를 메울 수 있다. 레이블이 없는 필드 크기를 0으로 지정하면, 그 다음에 오는 필드가 그 다음 정수에 맞게 정렬된다.

ex)

struct {

unsigned int field1 : 1;

unsigned int         : 2;

unsigned int field2 : 1;

unsigned int         : 0;

unsigned int field3 : 3;

}stuff;

 

여기서 stuff.field1과 stuff.field2 사이에는 2비트짜리 갭이있다. 그리고 stufffield3은 그 다음 위치의 int에 저장된다.

 

 

 

비트 필드 예제

흔히 비트필드는 데이터를 간결하게 저장하는 하나의 방법으로 사용된다. 예를들어, 화면 위에 표시되는 사각형의 속성을 나타내기로 결정했다고 가정하자. 아래는 사각형의 속성이다.

- 사각형은 불투명하거나 투명하다

- 채우기 색상은 black, red, green, yellow, blue, magenta, cyan, white 로 구성된 색상 팔레트에서 선택한다.

- 테두리는 보이게 하거나 숨길 수 있다.

- 테두리 색상은 채우기 색상과 마찬가지로 동일한 팔레트에서 선택한다.

- 테두리 선의 스타일은 실선(solid), 점선(dotted), 파선(dashed) 중에서 하나를 선택한다.

 

사각형이 불투명인지 투명인지를 나타내는 데에는 한 비트만 있으면 된다. 또한 테두리를 보이게 할것인지 숨길 것인지를 나타내는 데에도 한 비트만 있으면 된다. 8 가지의 색상 값들을 비트 3개짜리 한단위로 나타낼 수 있다. 3가지의 테두리선 스타일은 비트 2개짜리 한단위로 나타낼 수 있다. 그러므로 총 10비트만 있으면 된다.

 

위와 같은 정보를 표현하는 한가지 방법은, 채우기 관련 정보를 한 바이트에 넣고, 테두리 관련 정보를 다른 한 바이트에 넣기 위해 패딩(padding)을 사용하는 것이다.

ex)

struct box{

        bool opaque              :1;

        unsigned int fill-color   :3;

        unsigned int               :4;

        bool show_border       :1;

        unsigned int border_color  :3;

       unsigned int border_style   :2;

       unsigned int                   :2;

};

 

패딩이 위 구조체를 16비트 크기로 만든다. 패딩이 없으면 이 구조체는 10비트 크기가 될 것이다. 일반적으로 C는, 비트 필드로 이루어진 구조체의 기본 구성 단위로 unsigned int형을 사용한다. 그러므로 1비트짜리 필드 하나가 구조체를 구성하는 유일한 멤버인 경우에도, 그 구조체는 unsigned int형과 같은 크기(32bit)를 가진다.

 

opaque 멤버에 대해, 사각형이 불투명한 경우에 값 1, 투명한 경우에 값 0을 사용할 수 있다. show_border 멤버에도 테두리를 보일 경우에 값 1, 숨길경우에 값 0을 사용할 수 있다. 색상의 경우에는 간단한 RGB(red-green-blue)방식을 사용할 수 있다.

 

RGB는 빛 혼합의 3원색이다. 모니터는 red, green, blue 픽셀들을 혼합하여 다양한 색상을 구현한다. 컴퓨터 컬러의 초창기에, 각 픽셀은 on 또는 off가 될 수 있었다. 그래서 세2진수로 색상의 강도를 나타내는데 각각 1비트씩 사용할 수 있다.

일반적인 순서는 왼쪽 비트로 blue 강도, 가운데 비트로 green강도, 오른쪽 비트로 red 강도를 나타낸다. 이것들을 통해 8가지 조합을 나타낼 수 있다. 이들을 fill_color 와 border_color 멤버의 값으로 사용할 수 있다. 마지막으로 실선, 점선, 파선 스타일을 값 0,1,2 로 나타낼 수 있다.

 

간단한 색상 표현

     비트표현                                 10진수 표기                            색상

       000                                          0                                      Black

       001                                          1                                      Red

       010                                          2                                      Green

       011                                          3                                      Yellow

       100                                          4                                       Blue

       101                                          5                                       Magenta

       110                                          6                                       Cyan

       111                                          7                                       White

 

 

 

 

 

 

 

 

 

 

 

728x90

'Programming > C' 카테고리의 다른 글

C 전처리기와 C 라이브러리  (0) 2019.07.10
C 비트 연산 - 정리  (0) 2019.07.10
C 비트 연산  (0) 2019.07.10
C언어 공부 37  (0) 2019.07.08
C언어 공부 36  (0) 2019.07.08
728x90

비트단위 시프트 연산자

비트단위 시프트 연산자는 비트들을 왼쪽으로 또는 오른쪽으로 이동시킨다.

 

왼쪽으로 시프트 : <<

왼쪽으로 시프트 연산자(<<)는, 왼쪽 피연산자의 비트들을 오른쪽 피연산자가 지정하는 자릿수만큼 왼쪽으로 이동시킨다. 비워지는 위치들은 0으로 채워진다. 왼쪽 끝을 지나 밖으로 밀려나는 비트들은 사라진다.

(10001010) << 2

(00101000)

이 연산은 새로운 비트값을 만들지만 피연산자를 변경시키지 않는다.

변수값을 실제로 변경시키려면 왼쪽으로 시프트-대입 결합 연산자(<<=)를 사용할 수 있다. 이 연산자는  왼쪽 피연산자로 주어진 변수의 비트들을 오른쪽 피연산자가 지정하는 자릿수만큼 왼쪽으로 이동시킨다.

ex)

int stonk = 1;

int onkoo;

onkoo = stonk << 2;                   //onkoo에 4를 대입한다.

 

stonk <<= 2;                            //stonk를 4로 변경한다.

 

오른쪽으로 시프트 : >>

오른쪽으로 시프트 연산자(>>)는, 왼쪽 피연산자의 비트들을 오른쪽 피연산자가 지정하는 자릿수 만큼 오른쪽으로 이동시킨다. 오른쪽 끝을 지나 밖으로 밀려나는 비트들은 사라진다. 부호없는(unsigned) 데이터 형인 경우에, 왼쪽에 비워지는 위치들은 0 으로 채워진다. 결과값은 시스템에 따라 다르다. 왼쪽에 비워지는 위치들이 0으로 채워질 수도 있고, 부호 비트(제일 왼쪽 비트)가 복사되어 채워질 수 있다.

 

ex)

(10001010) >> 2                                //부호 있는값

(00100010)                                        //결과값 (일부 시스템에서)

(10001010)>>2                                 //부호 있는값

(11100010)                                       //결과 값(다른 시스템에서)

 

부호 없는 값의 경우에는 다음과 같다.

(10001010)>>2                                 //부호 없는값

(00100010)                                      //결과값(모든 시스템에서)

 

오른쪽 시프트-대입 결합 연산자 (>>=)는 왼쪽 피연산자로 주어진 변수의 비트들을 오른쪽 피연산자가 지정하는 자릿수만큼 오른쪽으로 이동시킨다.

int sweet = 16;

int ooosw;

 

ooosw = sweet >> 3;                             //ooosw = 2, sweet은 16

sweet >>= 3;                                       //sweet이 2

 

용도 : 비트단위 시프트 연산

비트 단위 시프트 연산자들은, 어떤 수를 2의 거듭제곱으로 곱하거나 나눌 때 편리하게 사용할 수 있습니다.

number << n  : number에 2의 n승씩 곱한다.

number >> n : number가 음수가 아니라면, number를 2의 n승씩 나눈다.

 

시프트 연산은, 10진수 체계에서 10을 곱하거나 10으로 나눌 때 소수점을 오른쪽 또는 왼쪽으로 이동시키는 것과 비슷하다.

 

CHAR_BIT 은 limit.h에 있는 매크로 이다. 이 매크로는 char에 있는 비트 수를 나타낸다. char 은 1byte 즉 8bit

CHAR_BIT은 8이다.

 

변수 n과 1개의 비트를 비교하려면 (0x1 & n)을 사용한다.

 

 

 

 

728x90

'Programming > C' 카테고리의 다른 글

C 비트 연산 - 정리  (0) 2019.07.10
C 비트 필드  (0) 2019.07.10
C언어 공부 37  (0) 2019.07.08
C언어 공부 36  (0) 2019.07.08
C언어 공부 35  (0) 2019.06.22
728x90

C의 비트 단위 연산자

C는 비트단위 논리연산자와 시프트 연산자를 제공한다.

 

비트단위 논리 연산자.

네개의 비트 단위 논리 연산자(char형을 포함한) 정수형 데이터에 적용된다. 이들을 비트 단위 연산자라고 부르는 이유는, 각 비트에 연산이 적용될 때 왼쪽이나 오른쪽에 있는 비트들에 영향을 받지 않기 때문이다.

 

1의 보수, 비트단위 부정(negation) 연산자 : ~

단항 연산자 ~는, 다음과 같이, 각 비트에 대해 1은 0으로, 0은 1로 바꾼다.

~(10011010) -> (01100101)

 

unsigned char 형인 val에 값 2가 대입되어 있다고 가정하자. 2진수 표기로 2는 00000010이다. 따라서 ~val은 값 11111101, 즉 253이다.

3 * val 이 val의 값을 변경시키지 않듯이, 이 연산자는 val의 값을 변경시키지 않는다. 그러므로 val은 여전히 2이다. val의 값을 ~val로 바꾸고 싶다면 대입을 사용하라.

val = ~val;

 

비트 단위 논리곱(AND)연산자 : &

이항 연산자 &는 두 피연산자를 대응하는 비트끼리 서로 비교함으로써 새로운 값을 만든다. 대응하는 두 비트가 모두 1일 때에만 결과 비트가 1이다.

(10010011) & (00111101) 의 값은 (00010001)이다.

 

C는 비트단위 논리곱 - 대입 결합 연산자 &= 를 제공한다.

val &=0377;

다음과 같은 문장과 동일하다.

val = val & 0377;

 

 

비트 단위 논리합(OR) 연산자 : |

이항 연산자  |는 두 피연산자를 대응하는 비트끼리 서로 비교함으로써 새로운 값을 만든다. 각각의 비트 위치에 대해, 피연산자들의 대응하는 두 비트중 어느하나라도 1이면 결과 비트가 1이다.

(10010011)|(00111101)의 결과는 (10111111)이다.

 

C는 비트 단위 논리합(OR) - 대입 연산자 |=를 제공한다. 

val |= 0377;

위 문장은 다음과 같다.

val = val | 0377;

 

비트단위 배타적 논리합(Exclusive OR)연산자 : ^

이항 연산자 ^는 두 피연산자를 대응하는 비트끼리 서로 비교함으로 써 새로운 값을 만든다. 피연산자들의 대응하는 두 비트중 어느 하나가(둘다가 아니라) 1이면, 결과 비트가 1이다.

(10010011) ^ (00111101)의 결과는 (10101110)이다.

 

C는 비트단위 배타적 논리합(Exclusive OR)- 대입 결합 연산자 ^=를 제공한다.

val ^= 0377;

위 문장은 다음과 같다.

val = val ^ 0377;

 

용도 : 마스크

비트 단위 논리곱 연산자는 흔히 마스크(mask)와 함께 사용된다. 

일부 비트들은 1, 일부 비트들은 0으로 설정된 비트 패턴을 마스크라 한다.

 

예를 들어 #define을 사용하여 기호 상수 MASK를 2로 정의 했다고 가정하자. 그럼 2는 2진수로 00000010이다.

그러면 다음과 같은 문장은

flags = flags & MASK;

flags의 하나의 비트를 제외한 모든 비트들을 0으로 설정한다. 그 이유는 & 연산자를 사용하여 0과 결합되는 비트는 결과 비트가 0이 되기 때문이다. 그러나 MASK의 1이 있는 위치의 flags 비트는 변하지 않는다.

 

위 논리를 확장하면, 마스크에 있는 0인 비트들을 불투명막으로, 1인 비트들은 투명막으로 생각할 수 있다. 표현식 flags & MASK는 flags의 비트패턴을 마스크로 가리는 것과 같다. 즉, MASK에 있는 1인 비트들에 대응하는 비트들만 볼 수 있다.

 

C에서 마스크의 가장 일반적인 용도는 다음과 같은 문장이다. ch & 0xff;

값 0xff는 2진수로 11111111이기 때문에, 이 마스크는 ch의 마지막 8비트만 남기고 모두 0으로 설정한다. 따라서 ch가 8비트, 16비트 또는 그 이상이라도 상관없이, 최종값을 1바이트에 넣을 수 있는 값으로 만들어 준다.

 

 용도 : 비트 켜기(On)(비트 설정)

어떤 경우 특정 비트들만 켜고, 나머지 비트들을 변경시키지 말아야할 경우가 있다. 이러한 경우에 비트 단위 논리합 | 연산자를 사용하면 된다.

 

예를 들어, 비트 1번이 1로 설정된 (00000010) MASK를 가지고 있다고 가정하자.

flags = flags | MASK;

flags의 비트 1번이 1로 설정되고, 나머지는 변경되지 않는다.

 

ex) flags는 00001111이고, MASK는 10110110이다.

flags |= MASK;

(00001111) | (10110110)

위 값은

(10111111)이다.

MASK에서 1로 정해진 모든 비트들은 결과적으로 1로 정해진다. MASK에서 0비트에 상응하는 flags에 있는 모든 비트들은 변경되지 않는다.

 

용도 : 비트 끄기(OFF)(비트 삭제)

어떤 경우 특정 비트들만 끄고, 나머지 비트들을 변경시키지 않는 것이 유용한 경우도 있다.

flags의 비트 1번만 끄기를 원한다고 가정하자. MASK는 비트 1번만 1로 설정되어 있다. 그러면 다음과 같은 문장을 사용할 수 있다.

flags = flags &~MASK;

~MASK는 비트 1번을 제외한 나머지 비트들이 모두 1이다. 11111101 이다. 또한 &를 사용하여 ~MASK의 1인 비트에 결합하는 비트는 변경되지 않고 0인 비트와 결합하는 비트는 0이 된다. 그러므로 원래의 값과 무관하게 0으로 설정된다.

 

flags가 00001111이고 MASK가 10110110이라고 가정하면 flags &= ~MASK;는

(00001111) &~(10110110)-> (00001111) &(01001001) -> (00001001)이 된다.

결과적으로 MASK에서 1로 설정된 모든 비트들은 0으로 설정된다. MASK에 0비트에 상응하는 flag에서의 모든 비트들은 변경되지 않고 남는다. flags&= ~MASK;

 

용도 : 비트 토글

비트를 토글(toggle)한다는 것은, on을 off로, off를 on으로 전환하는 것을 의미한다. 비트를 토글하기 위해서는 배타적 논리합(Exclusive OR) 연산자 ^를 사용할 수 있다. b가 비트세팅(1 또는 0)일때, b가 1이면 1^b는 0이다. b가 0이면 1^b 는 1이다. 또한 0 ^ b의 값이 무엇이든 간에 그대로 b가 된다. 그러므로 ^연산자를 사용하여 어떤 값을 마스크와 결합하면, 마스크에 있는 1인 비트에 대응하는 값들은 토글되고, 0인 비트에 대응하는 값들은 변하지 않는다.

 

예를 들면 flags가 00001111이고 MASK가 10110110이라고 가정하면 flags ^ MASK는 (00001111) ^ (10110110)이 된다. 이값은 (10111001)이 된다.

 

MASK에서 1로 설정된 모든 비트들이 토글되고 있는 flags의 비트에 결과적으로 상응하게 된다. MASK에 있는 0비트에 상응하는 flags 비트는 변경되지 않는다.

 

용도 : 비트 값 검사

특정 비트 하나의 값만 검사하고 싶다. flag에 있는 비트 1번이 1인지 검사하는 방법은 다음과 같이 할 수 있다.

if((flag & MASK) == MASK)

             puts("wow!");

위와 같은 방법을 사용한다면 이것은 특정 위치에 있는 비트를 검사하는 것이 아니라 모든 비트들의 세팅을 비교하는 것이다. 그로므로 비트 1번만 비교하려면 MASK를 사용하여 flag의 비트 1번을 제외한 모든 비트들을 마스크 처리 해야한다.

MASK = (00000010)

if((flag & MASK) == MASK)와 같은 과정을 통해 특정 비트의 값을 알 수 있다.

비트 연산자는 == 보다 우선순위가 낮다. 그러므로 flag&MASK를 괄호로 둘러싸야 한다.

마스크(MASK)의 비트 수가 마스크하려는 값(flag)의 비트수와 같거나 커야한다.

 

 

728x90

'Programming > C' 카테고리의 다른 글

C 비트 필드  (0) 2019.07.10
C 비트 연산  (0) 2019.07.10
C언어 공부 36  (0) 2019.07.08
C언어 공부 35  (0) 2019.06.22
C언어 공부 34  (0) 2019.06.22
728x90

비트조작

 

C에서는 시스템이 사용하는 문자 집합을 저장하는데 필요한 크기를 바이트(byte)라고 부른다. 그러므로 C에서는 1바이트가 8비트, 9비트, 16비트 또는 그밖의 어떤 비트 수를 가질 수 있다. 그러나 메모리 칩과 데이터 전송률을 표시하는 데에는 8비트 바이트가 사용된다. 보통 1바이트는 8비트이다.

 

   

7번 비트를 최상위 비트라고 하고 0번 비트를 최하위 비트라고 한다. 각각의 비트 번호는 하나씩 증가되는 2의 거듭제곱에 대응한다. 위의 예는 64 + 8 + 1, 73이다.

1바이트가 가질 수 있는 가장 큰 2진수는 11111111 이다. 이 값은 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255 이다.

1바이트는 0부터 255까지 256개의 수를 저장할 수 있다. 또는 비트 패턴을 다르게 해석함으로써, 프로그램은 1바이트에 -128 ~ + 127 까지, 전체 256개의 수를 저장 할 수 있다.

 

unsigned char 형은 0 ~ 255 까지의 범위를 나타낸다. signed char 형은 -128 부터 + 127 까지 범위를 나타낸다.

 

부호 있는 정수

부호 있는 수들의 표현은 C가 아니라 하드웨어에 의해 결정된다. 부호 있는 수듥을 표현하는 가장 간단한 방법은 1비트를, 최상위 비트를 부호를 나타내는데 사용하는 것이다. 이것은 수 자체를 위해 7비트를 사용한다는 것을 의미한다. 따라서 전체 범위는 -127 부터 +127 까지이다.

 

위와 같은 접근에는 하나의 문제점이 있다. +0과 -0 두개의 0이 생기는 것이다. 하나의 값을 나타내는데 비트 패턴을 두개를 써버린다. 이 문제점을 해결하는 방법이 "2의 보수" 이다.

 

2의 보수를 1바이트값에 적용하여 설명하겠다. 이 방법은 0부터 127까지의 값들을, 0으로 설정된 최상위 비트를 뺀 나머지 7비트로 표현한다. 또한, 최상위 비트가 1이면 그값은  음수이다. 음수의 값을 결정하는 방법은 9비트 패턴 100000000(256)에서 그 음수의 패턴을 뺀 결과가 음수 값의 크기이다. 9비트 패턴 100000000(256의 2진수 표현)으로 부터 음수용 비트 패턴을 뺀 결과는 값의 크기이다. 

예를 들어, 패턴이 10000000이라고 하면 부호 없는 값은 128이다. 부호 있는 값은 100000000 - 10000000, 즉 10000000(128)이 된다. 그러므로 수는 -128이다.

마찬가지로, 10000001 은 -127 이고, 11111111 은 -1 이다. 이 방법은 -128 부터 + 127 까지의 범위에 있는 수들을 나타낸다.

 

2의 보수 방법으로 표현된 2진수의 부호를 뒤집는 가장 간단한 방법은, 각 비트를 반전시키고(0은 1로, 1은 0으로) 거기에 1을 더하는 것이다. 1은 00000001이기 때문에, 2의 보수로 -1은 11111110 + 1 즉 11111111이 된다.

 

2진 부동 소수점 수

2진 소수에서는 증가되는 2의 거듭제곱을 분모로 사용한다. 2진 소수 .101은 1/2 + 0/4 + 1/8 로 나타낸다. 이값의 10진 표기는 0.50 + 0.00 + 0.125 즉 0.625이다.

 

컴퓨터는 하나의 부동 소수점 수를 표현하기 위해, (시스템 마다 다른) 특정 개수의 비트들을 2진 소수를 저장하는데 사용한다. 그리고 추가적인 비트들에 지수를 저장한다. 부동 소수점 수의 실제 값은 2진 소수와 2진 지수의 곱으로 이루어진다.

 

8진수, 16진수

 

8진수 체계는 8을 기수로 사용하며, 숫자 0에서 7까지 사용한다. 8진수 451은 4 x (8 x 8) + 5 x 8+ 1 x 1 = 297(10진수) 이다.

 

8진수는 2진수의 세 숫자에 대응한다.

                      8진수                                                    2진수

                        0                                                        000

                        1                                                        001

                        2                                                        010

                        3                                                        011

                        4                                                        100

                        5                                                        101

                        6                                                        110

                        7                                                        111

 

 

 

 16진수 체계는 16을 기수로 사용한다.

숫자 0에서 15까지를 사용하며 10 ~ 15는 A~F로 나타낸다.

16진수 A3F는 10 x (16 x 16) + 3 x (16) + 15 x 1 = 2623(10진수)이다.

2623 을 0xa3f로 나타낸다.

 

16진수는 2진수의 4개의 숫자에 대응한다. 그러므로 16진수 숫자 2개는 정확히 8비트 즉 1바이트에 대응한다. 첫번째 숫자는 상위 4비트를 나타내고, 두번째 숫자는 하위 4비트를 나타낸다. 이것은 바이트 값들을 16진수로 나타내는 것을 자연스럽게 만든다.

 

C는 비트들을 다루는 두가지 기능을 제공한다. 첫번째 기능은, 비트들에 적용하는 여섯개의 비트단위 연산자들로 이루어진 집합이다. 두번째 기능은, 필드(field) 데이터 형식이다. 이것을 사용하여 하나의 int형 안에 있는 비트들에 접근할 수 있다.

 

 

728x90

'Programming > C' 카테고리의 다른 글

C 비트 연산  (0) 2019.07.10
C언어 공부 37  (0) 2019.07.08
C언어 공부 35  (0) 2019.06.22
C언어 공부 34  (0) 2019.06.22
C언어 공부 33  (0) 2019.06.22
728x90

------------------------------------------------------------정리-------------------------------------------------------

구조체를 사용하면, 개별적으로 흩어진 여러 변수들의 집합으로 정보를 저장하지 않고, 관련된 모든 정보를 한 장소에 저장할 수 있다.

 

구조체를 설계할 때, 구조체와 함께 사용할 함수들의 패키지를 함께 개발하는 것이 일반적으로 유용하다. 예를 들어 구조체의 내용을 출력하고 싶을 때마다 printf를 사용하는 것이 아닌 구조체를 전달인자로 사용하는 하나의 디스플레이 함수를 작성하는 것이 좋다.

 

공용체 선언은 구조체와 비슷하다. 그러나 공용체 멤버들은 같은 메모리 공간을 공유한다. 그러나 공용체에는 한번에 한 멤버만 들어갈 수 있다. 공용체를 사용하면, 데이터형은 다양하지만 한번에 하나의 값만 넣을 수 있는 변수를 생성할 수 있다.

 

enum은 정수 기호 상수들을 정의하는 수단을 제공한다. typedef는 기본 데이터형이나 유도 데이터형에 새로운 식별자를 부여한다.

함수를 가리키는 포인터는, 어떤 함수에게 그것이 어떤 함수를 사용해야 하는지 알리는 수단을 제공한다.

 

-----------------------------------------------------------요약-----------------------------------------------------------------

C구조체는, 일반적으로 데이터형이 서로 다른 여러개의 데이터 항목들을 하나의 데이터 객체(object)에 저장하는 수단을 제공한다. 태그(tag)를 사용하여, 특정 구조체 템플릿을 식별하고 그 구조체형의 변수들을 선언할 수 있다. 도트연산자(.)를 사용하여 구조체 템플릿에 있는 레이블을 사용하여 구조체의 개별 멤버들에 접근 할 수 있다.

 

구조체를 가리키는 포인터를 선언하면, 이름과 도트연산자를 사용하는 대신에 포인터와 간접 멤버 연산자(->)를 사용하여 구조체의 개별 멤버에 접근할 수 있다. 구조체의 주소를 얻으려면 &연산자를 사용해야 한다. 배열과 달리, 구조체의 이름은 구조체의 주소 역할을 하지 않는다.

 

구조체 자체를 함수의 전달인자와 리턴값으로 사용할 수 있고, 같은 유형의 다른 구조체에 대입할 수 있다.

 

공용체는 구조체와 동일한 신택스를 사용한다. 그러나 공용체에서 멤버들은 동일한 기억공간을 공유한다. 공용체는 선택 리스트에 들어있는 한가지 데이터형만 저장한다.

 

enum을 통해 한무리의 정수 기호 상수들을 설정하고, 관련 열거형을 정의할 수 있다.

 

typedef기능을 사용하면, C의 표준 데이터형들에 다른 이름이나 약식 표기를 부여할 수 있다.

 

함수의 이름은 함수의 주소역할을 한다. 이러한 함수 주소는 다른 함수에 전달인자로 사용할 수 있다.

 

 

 

 

 

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 37  (0) 2019.07.08
C언어 공부 36  (0) 2019.07.08
C언어 공부 34  (0) 2019.06.22
C언어 공부 33  (0) 2019.06.22
C언어 공부 32  (0) 2019.06.22
728x90

함수를 가리키는 포인터는 함수 코드가 시작되는 위치의 주소를 가질 수 있다. 예를들어 int형을 가리키는 포인터는, 하나의 int형이 저장된 메모ㅓ리 공간의 주소를 가진다. 어떤 함수의 기계어 버전은 메모리에 적재된 코드들로 이루어지기 때문에, 함수들도 마찬가지로 주소를 가진다.

 

ex)

void ToUpper(char *)

ToUpper()함수의 함수형은 "하나의 char * 전달인자를 사용하고 void형을 리턴하는 함수"이다.

이 함수형을 가리키는 pf라는 이름의 포인터는 다음과 같이 선언한다.

void (*pf)(char *);

 

이 선언에서 첫번째 괄호가 * 연산자를 pf와 결합시키므로, pf는 어떤 함수를 가리키는 포인터가 된다. 이것은 (*pf)를 함수로 만들고, (char *)를 그 함수의 전달인자 리스트로 만들고, void를 그 함수의 리턴형으로 만든다. 연산자 우선순위 규칙 때문에 첫번째 괄호가 반드시 필요하다.

 

괄호가 없을때

void * pf(char *);        // pf는 포인터를 리턴하는 함수이다.

 

함수 이름이 그 함수의 주소이다.

ex)

void ToUpper(char *);

void ToLower(char *);

int round(double);

void (*pf)(char *);

pf = ToUpper;

pf = ToLower;

pf = round;                                 //불가능하다. 함수형이 다르다. round는 int형함수 pf는 void형

pf = ToLower();                            //불가능하다. ToLower()은 함수의 주소가 아니다.

 

pf는 전달인자로 char*를 사용하고 리턴형이 void인 함수만 가리킬 수 있다.

 

함수 포인터를 사용하여 함수에 접근할 수 있다.

ex)

void ToUpper(char *);

void ToLower(char *)

void (*pf)(char *);

char mis[] = "Nina Metier";

pf = ToUpper;

(*pf)(mis);                      //1.ToUpper에 mis를 전달함

pf = ToLower;

pf(mis);                          //2.ToLower에 mis를 전달함

 

1.의 경우 *pf는 ToUpper 함수이다. 따라서 (*pf)(mis)는 ToUpper(mis)와 같다.

2.의 경우 함수의 이름은 주소이기 때문에 사용자는 포인터와 함수 이름을 교환하여 사용할 수 있다. 그러므로 pf(mis)와 ToLower(mis)는 같다.

 

함수 포인터를 사용하여 함수에 접근하는 방법은(*pf)(mis), (pf(mis)) 두가지 방법 모두 가능하다.

 

함수를 함수의 전달인자로 사용

void show(void (*fp)(char *), char * str);

fp는 함수 포인터, str는 데이터 포인터이다.

fp는 char *형 전달인자를 사용하고 리턴형이 void인 어떤 함수를 가리킨다. str은 char형을 가리킨다.

위에 해당하는 함수 호툴로는

show(ToLower, mis);     //show()에서 ToLower함수를 사용

show(pf, mis);            //show()는 pf가 가리키는 함수를 사용

 

show() 함수를 사용하는법

void show(void (*fp)(char *), char * str)

{

         (*fp)(str);

         puts(str);

}

fp 전달인자로 함수를 받아오고 str에 저장된 문자열을 fp의 전달인자로 넣어준다.

 

리턴값이 있는 함수들은 다른함수들에 두가지 방식으로 전달 될 수 있다.

ex) 

function1(sqrt);                       //함수 주소를 전달한다.

function2(sqrt(4.0));                  //함수의 리턴값을 전달한다.

 

function1은 함수 내부에서 sqrt함수를 사용할 것이다.

function2는 sqrt()함수를 호출하여 실행한 리턴값을 function2에 넘겨준다.

 

 

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 36  (0) 2019.07.08
C언어 공부 35  (0) 2019.06.22
C언어 공부 33  (0) 2019.06.22
C언어 공부 32  (0) 2019.06.22
C언어 공부 31  (0) 2019.06.16
728x90

열거형(enumerated type)

열거형을 사용하여 정수 상수를 나타내는 기호 이름을 선언할 수 있다.

enum 키워드를 사용하여 새로운 데이텋형을 만들고, 그 데이터형이 가질 수 있는 값들을 지정할 수 있다.

 

열거형은 프로그램의 가독성을 높이는데 사용한다. 신택스는 구조체와 비슷하다. 

ex)

enum spectrum {red, orange, yellow, green, blue, violet};

enum spectrum color;

 

red ~ violet 처럼 여러된 상수들은 int형이지만 다른 정수형을 사용해도 상관없다.

red ~ violet은 각각 0~5까지의 값을 가진다.

 

열거된 리스트에 있는 상수들은 기본 설정으로 정수값 0,1,2,3 등으로 대입된다.

 

열거된 상수들을 사용자가 원하는 값으로 초기화 시킬 수 있다.

enum level {low = 100, medium = 500, high = 2000};

한 상수에만 값을 지정하고 그 다음 상수들에게 지정하지 않으면 그 다음 상수들에는 연속적으로 수가 매겨진다.

 

ex)

enum feline { cat, lynx = 10, puma, tiger};

일러한 선언에는 cat은 0, lynx = 10, puma = 11, tiger = 12로 초기화된다.

 

열거형의 목적은 프로그램의 가독성을 높이고 유지하기 쉽게 하는것이다.

 

 

typedef

typedef는 어떤 데이터형에 원하는 이름을 부여할 수 있는 데이터관련 고급 기능이다. #define과 비슷하지만, 세가지 차이점이 있다.

1.#define과 달리, typedef는 값이 아니라 데이터형에만 기호 이름을 부여할 수 있도록 제한된다.

2.typedef 해석은 전처리기가 아니라 컴파일러가 수행한다.

3.위 두개의 범위 내에서, typedef가 #define보다 융통성 있다.

 

typedef를 사용하여 BYTE를 char형 변수인 것 처럼 사용하기.

ex)

typedef char BYTE;

BYTE x,y[10], *z;

 

typedef 명령문이 어디에 있느냐에 따라 달라진다. typedef문이 함수 안에 들어 있으면, 범위가 지역적으로 그 함수에만 한정된다. 정의가 함수 바깥에 있으면, 범위는 전역적으로 모든 함수에 적용된다.

 

typedef char * STRING;

char형 포인터를 가리키는 포인터형을 STRING으로 사용할 수 있다. 즉 typedef를 통해 STRING은 char형을 가리키는 포인터를 의미하는 식별자가 된다.

이후 다음과 같은 선언이 가능하다.

STRING name, sign;

 

위 선언은 다음과 같다.

ex)

char *name, *sign;

 

typedef가 아닌 #define을 사용할 때

#define STRING char *

STRING name, sign;

위 선언은 char *name, sign;이 된다.

이경우에 name만 포인터가 된다.

 

 typedef를 사용하는 한가지 이유는, 자주 쓰이는 데이터형을 위한 편리하고 쉽게 인식할 수 있는 이름을 만드는 것이다.

typedef를 사용하는 두번째 이유는 복잡한 데이터형을 typedef이름으로 간단하게 표현할 수 있기 때문이다.

ex)

typedef char (*FRPTC())[5];

FRPTC를 char형 5개짜리 배열을 가리키는 포인터를 리턴하는 함수의 데이터형으로 만든다.

 

복잡한 선언        //어렵다

int board[8][8]              //int형 배열의 배열

int ** ptr;                     //int형을 가리키는 포인터를 가리키는 포인터

int *risks[10];                //int형을 가리키는 포인터 10개짜리 배열

int (*rusks)[10];            //int 10개짜리 배열을 가리키는 포인터

int * oof[3][4];            //int형을 가리키는 포인터들의 3x4 배열

int (*uuf)[3][4];           //int형 3x4배열을 가리키는 포인터

int (*uof[3])[4];           //int형 4개짜리 배열을 가리키는 포인터 3개짜리 배열

 

1.배열을 나타내는 [ ]과, 함수를 나타내는 ( )는 우선순위가 같다.

이들은 간접 연산자 *보다 우선순위가 높다.

2.[ ]과 ( )는 왼쪽에서 오른쪽으로 결합한다. 

ex) int goods[12][50];

int형 50개짜리 배열을 12개 가지는 배열을 만든다.

3.[ ] 와 ( )는 우선순위가 같다.

ex) int (*rusks)[10];

위는 rusksㄹ를 int형 10개짜리 배열을 가리키는 포인터로 만든다.

 

 

728x90

'Programming > C' 카테고리의 다른 글

C언어 공부 35  (0) 2019.06.22
C언어 공부 34  (0) 2019.06.22
C언어 공부 32  (0) 2019.06.22
C언어 공부 31  (0) 2019.06.16
C언어 공부 30  (0) 2019.06.16

+ Recent posts