ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C] 연산자② - 관계와 논리, 조건과 비트연산자
    C 2023. 4. 1. 18:57

     

     

    관계와 논리, 조건과 비트연산자


     

     

    관계 연산자(ralational operator)

    • 두 피연산자의 크기를 비교하기 위한 연산자
      • 비교 결과가 참이면 1, 거짓이면 0

     

    • 관계연산자의 종류와 사용
    연산자 연산식 의미 예제 연산(결과)값
    > x > y x가 y 보다 큰가? 3 > 5 0 (거짓)
    >= x >= y x가 y 보다 크거나 같은가? 5 - 4 >= 0 1(참)
    < x < y x가 y 보다 작은가? 'a' < 'b' 1(참)
    <= x <= y x가 y 보다 작거나 같은가? 3.43 <= 5.862 1(참)
    != x != y x와 y가 다른가? 5 - 4 != 3 / 2 0 (거짓)
    == x == y x와 y 가 같은가? '%' == 'A' 0 (거짓)

     

    #include <stdio.h>
    
    int main(void) {
    
    	int speed = 80;
    
    	printf("60 <= speed :   %d\n", (60 <= speed));
    	printf("60 > spee :   %d\n\n", (60 > speed));
    
    	printf("'a' > 'b' :   %d\n", ('a' > 'b'));
    	printf("'Z' <= 'a' :   %d\n\n", ('Z' <= 'b'));
    
    	printf("4 == 4.0 :   %d\n", (4 == 4.0));
    	printf("4.0F != 4.0 :   %d", (4.0F != 4.0));
    
    	return 0;
    }
    60 <= speed   : 1
    60 > spee   : 0
    
    'a' > 'b'   : 0
    'Z' <= 'a'   : 1
    
    4 == 4.0   : 1
    4.0F != 4.0   : 0

     

     


     

    논리 연산자(logical operator)

    • 세 가지의 논리연산자 &&, ||, !을 제공 and, or, not의 논리연산
    • 결과가 참이면 1, 거짓이면 0을 반환
    • C 언어에서 참과 거짓의 논리형은 따로 없음
      • 0, 0.0, 'w0'은 거짓을 의미
      • 0이 아닌 모든 정수와 실수, 그리고 널(null) 문자 '\0'가 아닌 모든 문자와 문자열은 모두 참을 의미

     

    • &&
      • 두 피연산자가 모두 참(0이 아니여야)인면 결과가 1(참)이며 나머지 경우는 모두 0
    • ||
      • 두 피연산자 중에서 하나만 참(0이 아니여야)이면 1이고, 모두 0(거짓)이면 0
    • !
      • 단항연산자로 피연산자가 0이면 결과는 1이고 참(0이 아닌 값)이면 결과는 0

     

    논리연산자의 연산 결과

    x y x && y x || y !x
    0 (거짓) 0 (거짓) 0 0 1
    0 (거짓) Ø (0이 아닌 값) 0 1 1
    Ø (0이 아닌 값) 0 (거짓) 0 1 0
    Ø (0이 아닌 값) Ø (0이 아닌 값) 1 1 0
    21 && 3 : 1
    !2 && 'a' : 0
    3>4 && 4>=2 : 0
    1 || '\0' : 1
    2 >=1 || 3<=0 : 1
    0.0 || 2-2 : 0
    !0 : 1

     

     

     

    • 단축 평가(short-circuit evaluation)
      • 논리연산자 &&와 || 
        • 피연산자 두 개 중에서 왼쪽 피연산자만으로 논리연산 결과가 결정된다면 오른쪽 피연산자는 평가하지 않음.
      • 예를 들어 (x && y) 연산식 
        • x의 값이 0(거짓)이라면 y의 값을 평가하지 않고 연산(x&&y) 결과는 0
      • x || y 수식 : x가 0이 아니(참)라면 더 이상 y의 값을 평가하지 않고 연산식(x||y)는 1이라고 평가

    &&의 왼쪽 (a < b)가 0이므로 오른쪽 연산을 하지 않고 0으로 판별되며, m은 변하지 않음.
    ||의 왼쪽 (a < b)가 1이므로 오른쪽 연산을 하지 않고 1로 판별되며, m은 변하지 않음.
    
    #include <stdio.h>
    
    int main(void) {
    
    	int a = 10, b = 5, m = 1;
    	int result;
    
    	result = (a < b) && (m++ == 1);
    	printf("m=%d result=%d\n", m, result);
    
    	result = (a < b) || (--m == 0);
    	printf("m=%d result=%d\n", m, result);
    
    	return 0;
    }
    m=1 result=0
    m=0 result=1

     

     


     

     

    조건 연산자(conditional expression)

    • 연산자 ? : 
      • 조건에 따라 주어진 피연산자가 결과값이 되는 삼항연산자
    • 연산식 ( x ? a : b )에서 피연산자는 x, a, b 세 개
      • 피연산자인 x가 참이면(0이 아니면) 결과는 a이며, x가 0이면(거짓) 결과는 b
    	max = (a > b) ? a : b;		: 최대값 반환 조건연산
    	max = (a < b) ? b : a;		: 최대값 반환 조건연산
    	min = (a > b) ? b : a;		: 최소값 반환 조건연산
    	min = (a < b) ? a : b;		: 최소값 반환 조건연산
    	absolute = (a > 0) ? a : -a;	 : 절대값 반환 조건연산
    	absolute = (a < 0) ? -a : a;	 : 절대값 반환 조건연산
    조건 삼항연산자의 두 번째와 세 번째 피연산자는 문장도 가능
    조건 삼항연산자의 두 번째와 세 번째 피연산자는 문자열을 비롯하여 모든 자료형도 가능
    
    #include <stdio.h>
    #define _CRT_SECURE_NO_WARNINGS
    
    int main(void) {
    
    	int a = 0, b = 0;
    
    	printf("두 정수 입력>> ");
    	scanf("%d%d", &a, &b);
    
    	printf("최대값: %d ", (a > b) ? a : b);
    	printf("최소값: %d\n", (a < b) ? a : b);
    	printf("절대값: %d ", (a > 0) ? a : -a);
    	printf("절대값: %d\n", (b > 0) ? b : -b);
    
    	((a % 2) == 0) ? printf("짝수 ") : printf("홀수 ");
    	printf("%s", ((b % 2) == 0) ? "짝수" : "홀수");
    
    	return 0;
    }
    두 정수 입력>> 8 -9
    최대값: 8 최소값: -9
    절대값: 8 절대값: 9
    짝수 홀수

     

     


     

     

    비트 연산자

    • 정수의 비트 중심(bitwise) 연산자를 제공
      • 비트 논리연산자와 이동연산자가 제공
    • 비트 논리 연산자
      • 피연산자 정수값을 비트 단위로 논리 연산을 수행하는 연산자

     

    • &, |, ^, ~ 4가지
      • 연산자 ~
        • ~5와 같이 연산자 ~가 피연산자인 5 앞에 위치하는 전위인 단항 연산자
      • 나머지는 모두 (3 | 4)처럼 피연산자가 두 개인 이항 연산자
        • 피연산자의 자료형은 정수형에 해당하는 char, int, long, long long
        • 각 피연산자를 int 형으로 변환하여 연산하며 결과도 int 형
    연산자 연산자 이름 사용 의미
    &  비트 AND op1 & op2 비트가 모두 1이면 결과는 1, 아니면 0
    |  비트 OR op1 | op2 비트가 적어도 하나 1이면 결과는 1, 아니면 0
    ^  비트 배타적 OR(XOR) op1 ^ op2 비트가 서로 다르면 결과는 1, 같으면 0
    ~  비트 NOT(Negation) 또는 보수(complement) ~op1 비트가 0이면 결과는 1, 0이면 1

     

     

    • 보수 연산자(bitwise complement operator)~
      • 각 비트에서 0은 1, 1은 0이 결과
    피연산자 보수 연산
    비트표현(2진수) 보수 연산 결과 10진수
    1 00000000 00000000 00000000 00000001 11111111 11111111 11111111 11111110 ~1 = - 2
    4 00000000 00000000 00000000 00000100 11111111 11111111 11111111 11111011 ~4 = -5

     

     

    • 컴퓨터에서 정수의 음수 표현 방법
      • 보수를 이용하는 방법을 사용
      • 즉 양수 정수 a에서 음수인 -a의 비트 표현은 2의 보수 표현인 (~a + 1)
      • 즉 -1은 ((~1)+1)로, 정수 -1을 비트로 표현하면 32비트가 모두 1인 정수
    • 비트 논리 연산식 x & -1
      • 정수 x를 -1로 논리 and 연산을 수행하는 식으로 결과는 x
    • 비트 논리 연산식 x | 0
      • 정수 x를 0으로 논리 or 연산을 수행하는 식으로 결과는 x
    연산식 설명 연산값 연산식 설명 연산값
    1 & 2 0001 & 0010 0 1 & 3 0001 & 0011 1
    3 | 4 0011 | 0100 7 3 | 5 0011 | 0101 7
    3 ^ 4 0011 ^ 0100 7 3 ^ 5 0011 ^ 0101 6
    ~2 -2 == ~2 + 1 -3 ~3 -3 == ~3 + 1 -4

     

     

     

    비트 이동 연산자(shift operator) >>, <<

    • 연산자의 방향인 왼쪽이나 오른쪽으로, 비트 단위로 줄줄이 이동시키는 연산자
    • 실제 연산에서는 int 형의 크기인 32비트의 비트에서 연산을 수행
    연산자 이름 사용 연산 방법 새로 채워지는 비트
    >> right shift op1>>op2 op1 오른쪽으로 op2 비트만큼 이동 가장 왼쪽 비트인 부호 비트는 원래의 부호 bit로 채움
    << left shift op1<<op2 op1 왼쪽으로  op2 비트만큼 이동 가장 오른쪽 비트를 모두 0으로 채움
    #include <stdio.h>
    
    int main(void)
    {
    	int x = 16391;
    
    	printf("%6d  →  %08x\n", x, x);		# 정수 16391은 십육진수로 4007
    	
         # 정수 >> n은 정수를 n번 2로 나눈 효과
    	printf("x >> 1 → %d, %08x\n", x >> 1, x >> 1);
    	printf("x >> 2 → %d, %08x\n", x >> 2, x >> 2);	
    	printf("x >> 2 → %d, %08x\n", x >> 3, x >> 3);
        
         # 정수 << n은 정수를 n번 2로 나눈 효과
    	printf("x << 2 → %d, %08x\n", x >> 2, x >> 2);
    	printf("x << 2 → %d, %08x\n", x >> 3, x >> 3);
    
    	return 0;
    }
     16391  →  00004007
    x >> 1 → 8195, 00002003
    x >> 2 → 4097, 00001001
    x >> 2 → 2048, 00000800
    x << 2 → 4097, 00001001
    x << 2 → 2048, 00000800

     

    • 왼쪽 이동연산자에 의해 최상위 부호 비트가 0에서 1로, 1에서 0으로 바뀔 수 있으므로 왼쪽이 이동연산자에 의해 항상 2배씩 커지지는 않음.
    • 음수 홀수에 대한 오른쪽 이동연산자 >>도 -1을 한 짝수를 2로 나눈 결과
    연산식 설명 연산값 연산식 설명 연산값
    15 << 1 0000  1111  <<  1
    0001  1110
    30 15 << 2 0000  1111  <<  2
    0011  1100
    60
    0x30000000 <<  2 최상위 4비트 : 
    0011  0...  <<  2
    1100  0...
    - 1073741824
    0XC0000000
    -30 << 2 음수로 4배 커짐 -120
    -30 >> 1  짝수이면 2로 나누기 -15 -30 >> 2 한 비트 이동마다. 음수 홀수는
    -(a+1)한 수를 2로 나누기
    -8

     

     

    LAB 표준입력으로 받은 두 정수의 비트 연산 수행 출력 

    • 표준입력으로 받은 두 정수의 6개 비트 연산을 수행하여 결과를 출력하는 프로그램 
      • 비트 연산은 &, |, ^, ~, >>, << 연산을 수행
      • 단항 연산은 첫 번째 변수에 대한 연산 수행
    • 결과 
      • 비트 연산이 가능한 두 정수를 입력하세요
      • 40 3
      • 40 & 3 → 0
      • 40 | 3 → 43 
      • 40 ^ 3 → 43
      • ~ 40 → -41 
      • 40 >> 3 → 5 
      • 40 << 3 → 320  

     


     

    형변환 연산자와 연산자 우선순위

     

     

    내림변환과 올림변환

    • 자료형 변환은 크기 자료형의 범주 변화에 따른 구분 

     

    • 올림변환
      • 작은 범주의 자료형(int)에서 보다 큰 범주인 형(double)으로의 형변환
        • 올림변환은 형 넓히기라고도 부름
      • 올림변환은 정보의 손실이 없으므로 컴파일러에 의해 자동으로 수행 가능
        • 컴파일러가 자동으로 수행하는 형변환 : 묵시적 형변환(implicit type conversion)
    # 다양한 올림변환의 예
    
    'a' + 2  →  int + int	# 문자 'a'의 아스키 코드값의 int로 변환
    3 * 4.1F  →  float * float
    4.45F / 3.81  →  double / double
    9.34 - 2  →  double - double
    3 * 4.28  →  double * double

     

    • 내림변환
      • 큰 범주의 자료형(double)에서 보다 작은 범주인 형(int)으로의 형변환
      • 대입연산 int a = 3.4에서 내림변환이 필요
        • 컴파일러가 스스로 시행하는 무시적 내림변환의 경우 정보의 손실이 일어날 수 있으므로 경고를 발생
        • 프로그래머의 명시적 형변환이 필요

     

     

    형변환 연산자

    • 명시적 형변환(explicit type conversion)
      • 형변환 연산자 '(type) 피연산자'는 뒤에 나오는 피연산자의 값을 괄호에서 지정한 자료형으로 변환하는 연산자
      • 내림변환에서는 형변환 연산자(type cast)를 사용하여 내림변환을 직접 수행
    # 다양한 형변환연산 예
    
    (int) 'A' → 65
    (int) 3.14 → 3
    (double) 9 → 9.0
    (double) 3.4F → 3.4
    (double) 7 / 2 → 3.5
    • 올림변환
      • 상수나 변수의 정수값을 실수로 변환하려면 올림변환을 사용
      • 연산식 (double) 7의 결과는 7.0
    • 내림변환
      • 실수의 소수부분을 없애고 정수로 사용하려면 내림변환을 사용
      • (int) 3.8의 결과는 3
      • 단항연산자인 형변환 연산자는 모든 이항연산자보다 먼저 계산
    # 다양한 형변환 연산 예
    
    (int) 3.8 + 5.7  →  8.7
    3.8 + (int) 5.7  →  8.7
    (int) 3.8 + (int) 5.7  →  8
    (int) (3.8 + 5.7)  →  9
    7 / 2  →  3
    7.0 / 2  →  3.5
    7 / (double) 2  →  3.5
    (double) (7 / 2)  →  3.0

     

     

     

    연산자 sizeof

    • 연산값 또는 자료형의 저장장소의 크기를 구하는 연산자
      • 결과값은 바이트 단위의 정수
      • 피연산자가 int와 같은 자료형인 경우 반드시 괄호를 사용
      • 피연산자가 상수나 변수 또는 연산식이면 괄호는 생략 가능
    sizeof (int) → 결과는 4
    sizeof (3.14) → 결과는 8
    sizeof a → 결과는 2 (short a;)

     

     

     

    콤마연산자 ,

    • 콤마연산자 ,는 왼쪽과 오른쪽 연산식을 각각 순차적으로 계산
      • 결과값은 가장 오른쪽에서 수행한 연산의 결과
      • 연산식 2, 4의 결과값은 4
      • 또한 3 + 4, 2 * 5의 결과값은 10
      • 콤마연산자가 연속으로 나열된 식에서는 마지막에 수행된 가장 오른쪽 연산식의 결과가 전체 식의 결과값
    exp1 , exp2  → 결과값은 나중에 계산된 exp2의 결과값이다.
    
    3 + 4, 5 - 10  → 결과는 -5 
    3 + 4, 5 - 10, 2 * 3  → 결과는 6

     

     

    연산자 sizeof 와 콤마연산자 , 활용

    #include <stdio.h>
    
    int main(void) {
    
    	int a = 100, b = 50, c;
    
    	printf("%d ", sizeof(short));	# 연산자 sizeof (자료형)에서 괄호는 필수
    	printf("%d ", sizeof a);
    	printf("%d ", sizeof 3.5F);
    	printf("%d\n", sizeof 3.14);
    
    	c = ++a, b++;			 # (c=++a), (b++); 이므로 변수 a, b, c에는 각각 101, 51, 101	
    	printf("%d %d %d\n", a, b, c);
    	c = (3 + a, b * 2);		 # 변수 c에 저장되는 값은 b * 2 이므로 102
    	printf("%d %d %d\n", a, b, c);
    
    	return 0;
    }
    2 4 4 8
    101 51 101
    101 51 102

     

     

     

    복잡한 표현식의 계산

    • 연산자 우선순위와 결합성 
    • 규칙 3개를 적용 
      • 첫 번째 규칙은 괄호가 있으면 먼저 계산 
        • 괄호 우선 규칙: '괄호가 있으면 먼저 계산한다'라는 규칙 
      • 두 번째 규칙으로 연산의 우선순위(priority)  
        • 즉 '곱하기와 나누기는 더하기와 빼기보다 먼저 계산한다.'라는 규칙 
      • 세 번째 규칙은 동일한 우선순위인 경우  
        • 연산을 결합하는 방법인 결합성(또는 결합규칙)
          • 연산자 결합성 : '괄호가 없고 동일한 우선 순위라면, 덧셈과 뺄셈, 곱셈과 나눗셈과 같은 일반적인 연속된 연산은 왼쪽부터 오른쪽으로 차례로 계산
          • 다만 제곱승과 같은 정해진 연산은 오른쪽에서 왼쪽으로 차례로 계산한다'라는 규칙

     

     

     

    연산자의 우선순위(priority)

    • 우선순위가 1위
      • 함수호출과 괄호로 사용되는 ()
      • 후위 증감연산자 a++와 a-- 등의 단항연산자
      • 여러 개 있으면 결합성에 따라 왼쪽에서 오른쪽 순으로 계산
    • 우선순위가 2위
      • 전위 증감연산자 ++a와 -a
      • 주소연산자 &
      • 오른쪽에서 왼쪽으로 차례로 계산
      • 모든 단항연산자는 우선순위가 1, 2위
    • 우선순위가 16위
      • 콤마연산자
    우선
    순위
    연산자 설명 분류 결합성(계산방향)
    1 (   )
    [   ]
    .
    ->
    a++    a--
    함수 호출 및 우선 지정
    인덱스
    필드 (유니온) 멤버 지정
    필드 (유니온) 포인터 멤버 지정
    후위 증가, 후위 감소
      → (좌에서 우로)
    2 ++a   --a
    !      ~
    sizeof
    -      +
    &
    *
    전위 증가, 전위 감소
    논리 NOT, 비트 NOT(보수)
    변수, 자료형, 상수의 바이트 단위 크기
    음수 부호, 양수 부호
    주소
    간접, 역참조
    ← (우에서 좌로)
    3 (형변환) 형변환
    4 *   /   % 곱하기 나누기 나머지 산술 → (좌에서 우로)
    5 +      - 더하기 빼기 → (좌에서 우로)
    6 <<      >> 비트 이동 이동 → (좌에서 우로)
    7 <   >   <=   >= 대소 비교 관계 → (좌에서 우로)
    8 ==      != 동등 비교 → (좌에서 우로)
    9 & 비트 또는 논리 비트 → (좌에서 우로)
    10 ^ 비트 또는 논리 → (좌에서 우로)
    11 | 비트 또는 논리 → (좌에서 우로)
    12 && 논리 (단락 계산) 논리 → (좌에서 우로)
    13 || 논리 (단락 계산) → (좌에서 우로)
    14 ? : 조건 조건 ← (우에서 좌로)
    15 =   +=   -=   *=   /=   %=
    <<=   >>=   &=   |=   ^=
    대입 대입 ← (우에서 좌로)
    16 , 콤마 콤마 → (좌에서 우로)

     

     

     

    #include <stdio.h>
    
    int main(void)
    {
    	int a = 4, b = 6;
    	double x = 3.3, y = 4.7;
    
    	printf("%d ", a + b > y && x < y);
    	# 산수 > 관계 > 논리
        
    	printf("%d ", a++ - --b * 2);
    	# 단항 > 곱셈 > 뺄셈
        
    	printf("%f ", a > b ? x + 1 : y * 2);
    	# 산술 > 관계 > 조건
        
    	printf("%f ", x += 3 && y + 2);
    	# 산술 > 논리 > 대입
        
    	printf("%f\n", (x = x + 1, y = y + 1));
    	# 괄호 > 산술 > 대입 > 콤마
    
    	return 0;
    }
    1 -6 9.400000 4.300000 5.700000

     

     

    우선순위 요약

    • 콤마 < 대입 < 조건(삼항) < 논리 < 관계 < 산술 < 단항 < 괄호와 대괄호
      • 이항연산자 : 논리, 관계, 산술
      • 괄호와 대괄호는 무엇보다도 가장 먼저 계산한다.
      • 모든 단항연산자는 어느 이항연산자보다 먼저 계산한다.
      • 산술연산자 *, /, %는 +, -보다 먼저 계산한다.
      • 산술연산자는 이항연산자 중에서 가장 먼저 계산한다.
      • 관계연산자는 논리연산자보다 먼저 계산한다.
      • 조건 삼항연산자는 대입연산자보다 먼저 계산하나, 다른 대부분의 연산보다는 늦게 계산한다.
      • 조건 > 대입 > 콤마 연산자 순으로 나중에 계산한다.
    변수값 표현식 설명 해설 결과
    x = 3
    y = 3
    x  >>  1  +  1  >  1  &  y 산술 > 이동 > 관계 > 비트 ( (  x  >>  ( 1  +  1 ))  >  1 )  &  y 0
    x  -  3  | |  y  &  2 산술  >  비트  >  논리 ( x  -  3 )  | |  ( y  &  2 ) 1
    x  &  y  &&  y  >=  4 관계 > 비트 > 논리 ( x  &  y )  &&  ( y  >=  4 ) 0
    x  &&  x  |  y++ 증가 > 비트 > 논리 x  &&  ( x  |  ( y++ ) ) 1

     

     

    #include <stdio.h>
    
    int main(void) {
    
    	int m = 5, n = 10;
    
    	# 우측에서 좌측으로 결합
        printf("%d ", n += m /= 3);
    	m = 5; n = 10;
    	printf("%d\n", (n += (m /= 3)));
    
    	printf("%d ", 10 * 3 / 2);		# 좌측에서 우측으로 결합
    	printf("%d\n", 10 * (3 / 2));	# 우측에서 좌측으로 결합
    
    	# 우측에서 좌측으로 결합 (조건연산자는 결합성이 오른쪽에서 왼쪽으로)
        printf("%d ", 3 > 4 ? 3 - 4 : 3 > 4 ? 3 + 4 : 3 * 4);
    	printf("%d ", 3 > 4 ? 3 - 4 : (3 > 4 ? 3 + 4 : 3 * 4));
    
    	return 0;
    }
    11 11
    15 10
    12 12

     

     

     

    결합성

    • 결합법칙
      • 대부분 좌에서 우로 수행
      • 우에서 좌(d)로 수행
        • 우선순위가 2위인 전위의 단항 연산자, 우선 순위 14위인 조건연산자 그리고 우선순위 15위인 대입연산자
      • 산술연산식 10 * 3 / 2는 ((10 *3) / 2)
        • 결과는 12
      • 축약 대입연산자로 구성
        • 연산식 n += m /= 3
        • 우에서 좌(d)로 먼저 결합하여 식( n += (m /= 3))을 수행

     

     

    다양한 연산식

    • 수학이나 공학에서의 다양한 수식을 표현
      • 연산의 우선순위를 고려하여 괄호의 사용이 필요
      • 제곱근을 구하는 함수 sqrt()를 활용

     

     

     

     

     

     

    'C' 카테고리의 다른 글

    [C] 배열①  (0) 2023.04.05
    [C] 반복  (0) 2023.04.03
    [c] 조건  (0) 2023.03.29
    [C] 연산자①  (1) 2023.03.22
    [C] 전처리와 입출력  (1) 2023.03.17
Designed by Tistory.