C, 연산자, sizeof, 비트마스크


이번 챕터의 목표


1. 초를 입력받아 시분초 단위로 바꾸는 프로그램

c_operator_goal1


2. 국영수 점수 입력받아 합격여부 출력하는 프로그램

c_operator_goal2_1 c_operator_goal2_2 c_operator_goal2_3




연산자란?


우리가 일상생활에서 많이 쓰는 연산이라면

사칙연산이 있습니다.

이처럼 연산이라는 것은 무언가를 계산한다는 것입니다.

간단한 덧셈 연산의 예를 들면 다음과 같습니다.

1 + 2 = 3

여기서 +를 연산자, 숫자(1, 2)를 피연산자라고 합니다.


그리고 +의 경우에는 피연산자가 2개가 있습니다.

이런 연산자를 2항 연산자라고 부릅니다.

c에는 1항 연산자부터 3항 연산자까지 있습니다.




연산자의 우선순위와 결합성


간단한 연산자 예제를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 4 + 6 - 3;
    printf("x = %d\n\n", x);
    
    return 0;
}

/*
 x = 7
*/

일단 x의 결과값이 7이 나오는 건 쉽게 알 수 있습니다.

위의 코드에서는 연산이 총 3번 일어납니다.

  1. 4 + 6 이 계산되고, 임시메모리에 10이 저장된다.
  2. 10 - 3이 계산되고, 임시메모리에 7이 저장된다.
  3. x = 7이 실행된다.

+, - 뿐 아니라 =도 연산자입니다.

이 연산 순서를 이해하기 위해서는 연산자의 속성 두가지를 알아야합니다.

이 두가지는 우선순위결합성(연산방향)입니다.

아래의 표는 이를 정리한 것입니다.

우선순위 연산자 결합성
1 () [] . ->
2 * & ! ++ – sizeof (datatype)
3 * % /
4 + -
5 « »
6 < > <= >=
7 == !=
8 &
9 ^
10 |
11 &&
12 ||
13 ? :
14 = += -= *= %= /= &= |= ^= «= »=
15 ,


연산자가 쓰인 구문을 살펴보겠습니다.

int x = 4 + 6 - 3;

여기서 쓰인 연산자는 =, +, - 이렇게 3개입니다.

이 중 우선순위를 보면 =는 14위, +와 -는 4위입니다.

즉, +와 -를 먼저 연산하면 됩니다.

그런데 +와 -는 결합성 화살표가 왼쪽에서 오른쪽을 가리킵니다.

즉, 계산은 왼쪽에서 오른쪽 순서대로 하게 되고,

왼쪽에 있는 4+6이 먼저 연산됩니다.

계산결과인 10은 임시메모리에 저장되고, 이제 10-3이 계산되는 것입니다.

즉, 동일한 순위의 연산자가 있을 때는 결합성 방향에 맞게 연산해주면 됩니다.


+, -의 연산이 끝나면 이제 그 다음 순위였던 = 연산자를 수행합니다.

=의 결합성 화살표는 오른쪽에서 왼쪽을 가리킵니다.

즉, 계산은 오른쪽에서 왼쪽으로 진행되고,

오른쪽의 계산 결과인 10이 왼쪽의 x에 저장됩니다.


표를 보면 ‘이걸 어떻게 다 외워’라고 생각할 수 있지만 의외로 간단합니다.

일단 결합성은 ←인게 3분류밖에 없고 나머지는 모두 →입니다.

그리고, 뭔가 연산을 하고 싶은데 우선순위가 애매하다면 그냥 1순위의 괄호를 사용하면 됩니다.

괄호는 거의 만능이라고 보시면 됩니다.

괄호는 일반적인 수학식에 쓸 때처럼 사용하면 됩니다. 예를 들어

4 + 7 * 2

를 계산한다고 하면, 그냥 이대로 계산하면 곱하기(*)가 우선순위가 더 높으므로

7*2를 먼저 하고 4를 더하게 되므로, 결과는 18이 됩니다.

그런데 만약 4 + 7을 먼저 하고싶다면, 이렇게 연산하면 됩니다.

(4 + 7) * 2

간단한 예제코드 입니다.

#include <stdio.h>

int main(void)
{
    int x = 4 + 7 * 2;
    int y = (4 + 7) * 2;
    
    printf("x = %d, y = %d\n\n", x, y);
    
    return 0;
}

/*
 x = 18, y = 22
*/




이형자료 간의 연산 및 형승격


여기서 이형자료라고 함은 다른 형식의 자료형이라는 것입니다.

형자료의 다를 이(異)를 사용합니다. 예를 들어 다음과 같은 코드가 있습니다.

#include <stdio.h>

int main(void)
{
    char ch = 'A';
    
    // char + int는 int
    printf("ch + 1 = %c\n", ch+1);
    printf("ch + 1 = %d\n\n", ch+1);
    
    double du = 3.0;
    // double + int는 double
    printf("du + 4 = %d\n", du+4);
    printf("du + 4 = %f\n", du+4);
    
    return 0;
}

/*
 ch + 1 = B
 ch + 1 = 66
 
 du + 4 = 768
 du + 4 = 7.000000
*/

c에서 정수형의 기본형은 int입니다.

즉, ch+1을 하면 char + int 의 형태가 됩니다.

char + int 는 int가 됩니다.


du+4는 double + int의 형태이고 결과값은 double이 됩니다.

출력 결과를 보면 세번째 출력문에서는 엉뚱한 숫자가 나와있습니다.

double을 %d로 출력하였기 때문입니다.

저 결과값은 맥 기준이고, 윈도우(Visual Studio)에서는 이렇게 안맞는 출력을 시도하면

그냥 0이 나와버립니다.

여하튼 4번째 출력문같이 알맞은 출력형식이면 7.0 이라는 올바른 값이 나옵니다.


이렇듯, 임시결과의 자료형은 연산에 참여한 피연산자 중 정보 범위가 더 넓은 자료형이 됩니다.

char(1바이트)와 int(4바이트) 연산 결과 -> int(4바이트)
double(8바이트)과 int(4바이트) 연산 결과 -> double(8바이트)

이러한 현상을 자료이 상승(승격)했다고 해서

형승격(type promotion)이라고 합니다.




곱셈(*), 나눗셈(/), 나머지(%) 연산자


곱셈, 나눗셈, 나머지 연산 중 주의해야 할 연산자는

나눗셈과 나머지 연산입니다.

먼저 나눗셈 연산의 주의점입니다.

  1. int/int 는 몫만 나오고 int형식입니다.
  2. double/int 혹은 int/double은 나눈 값이 나오고 double형식입니다.
  3. 0으로 나눌 수 없습니다.

다음은 나머지 연산의 주의점입니다.

  1. 0으로 나눌 수 없습니다.
  2. 정수형%정수형 만 가능합니다.


그냥 외워야 하는건가 싶지만, 잘 생각해보면 당연한 것입니다.

먼저 나눗셈 주의사항을 살펴보면,

int/int는 같은 자료형이므로 결과값도 int가 나와야합니다.

예로 5/2의 값은 2.5이지만 int자료형에 들어가야하므로

소수점 이하는 버려지고 2만 저장됩니다. 결국 몫만 남게 되는 것이죠.

하지만, double/int나 int/double은 이형자료간 형승격에 의해

결과값이 double이므로 2.5가 그대로 보존됩니다.

그리고, 어떤 수든 0으로 나눌 수 없다는 것은 나눗셈의 기본이죠.


다음으로 나머지 연산의 주의점을 살펴보겠습니다.

나머지도 나눗셈의 결과로 나오는 결과이기 때문에

0으로 나눌 수 없는 건 동일합니다.

그리고 2번 사항은 실수%실수 가 불가능하다는 것입니다.

예로 2.4%3 을 계산한다 가정하겠습니다.

나머지라는 건 제수(divisor)보다 작은 0이상의 정수여야 하는데

구할 수가 없습니다.


간단한 예제 코드를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 7;
    
    // int * int => int
    printf("x * 11 = %d\n", x*11);
    // int * double => double
    printf("x * 11.0 = %d\n", x*11.0);
    printf("x * 11.0 = %f\n\n", x*11.0);
    
    // int / int => int
    printf("x / 2 = %d\n", x / 2);
    printf("x / 2 = %f\n\n", x / 2);
    
    // double / int => double
    printf("14.0 / 2 = %f\n\n", 14.0/2);
    
    // int % int => int
    printf("x %% 2 = %d\n\n", x % 2);
    
    return 0;
}

/*
 x * 11 = 77
 x * 11.0 = 0
 x * 11.0 = 77.000000
 
 x / 2 = 3
 x / 2 = 0
 
 14.0 / 2 = 7.000000

 x % 2 = 1
*/

결과값이 이상하게 나온 것(0인 것)은 출력형식이 맞지 않기 때문입니다.

나머지 결과값은 위의 설명대로 잘 나옵니다.


마지막 출력문에 %%와 같이 %를 두번 쓴 이유는

%는 출력형식을 정해주기 위한 용도로 쓰기 때문에

% 자체를 출력하고 싶으면 %%로 써야합니다.




단순 대입 연산자


지금까지 계속 써왔던

int x = 7;

구문의 = 입니다.

간단히 주의해야할 점만 알아보면

대입연산자는 변수에만 대입할 수 있다는 것입니다.

예를 들어서 다음과 같은 코드는 에러가 납니다.

#include <stdio.h>

int main(void)
{
	char name[30] = {0};

	// 배열 이름은 배열 메모리의 주소(상수)입니다.
	// 상수는 변수가 아니므로 대입연산자 사용 불가합니다.
	name = 't';

	// 3은 상수입니다.
	// 상수는 변수가 아니므로 대입연산자 사용 불가합니다.
	3 = 4;
}


그리고 다음 코드를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 0, y = 0;
    x = y = 7;
    
    printf("x = %d, y = %d\n", x, y);
    
    return 0;
}

// x = 7, y = 7

간단한 코드긴 한데 여기서 주목할 부분은

x = y = 7;

입니다. 위에서 봤던 우선순위/결합성 표를 보면

= 는 연산 순위는 14위에 결합성은 오른쪽에서 왼쪽입니다.

즉, y = 7 이 먼저 수행되고나서 x = 7이 수행됩니다.




복합 대입 연산자


복합 대입 연산자의 종류에는 다음이 있습니다.

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

간단한 예제 코드를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 0;
    x += 10; // x = x + 10;
    printf("x = %d\n", x);
    
    x -= 3; // x = x - 3;
    printf("x = %d\n", x);
    
    x *= 11; // x = x * 11;
    printf("x = %d\n", x);
    
    x /= 3; // x = x / 3;
    printf("x = %d\n", x);
    
    x %= 7; // x = x % 7;
    printf("x = %d\n", x);
    
    return 0;
}

/*
 x = 10
 x = 7
 x = 77
 x = 25
 x = 4
*/

+=만 설명하자면,

x += 10; 이라는 코드는 옆에 주석에 써있듯이

x = x + 10; 과 완전 동일한 코드입니다.

코드가 조금 더 짧아서 더 간결해집니다.




형변환(type cast) 연산자


형변환은 위의 우선순위/결합성 표에서

2위에 있는 (datatype) 입니다.

말그래도 자료형을 변환시키는 연산자이고 간단한 예를 들자면

double tcEx = (double)2;

2는 원래 int이지만 앞에 (double) 연산자를 통해 2.0으로 바뀝니다.

간단한 예제 코드를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    printf("5 / 2 = %d\n", 5 / 2);
    printf("(double)5 / 2 = %f\n", (double)5 / 2);
    printf("(double)(5 / 2) = %f\n", (double)(5 / 2));
    
    return 0;
}

/*
 5 / 2 = 2
 (double)5 / 2 = 2.500000
 (double)(5 / 2) = 2.000000
*/

마지막 출력문에서 조심해야합니다.

(double)(5 / 2)는 먼저 5/2를 계산하여 2라는 값을 임시저장하고,

그 값에 (double)연산자를 붙이므로 2.0이 나옵니다.




단항 증감 연산자(++, - -)


x++ 이나 ++x 는 x += 1 과 같고,

x- - 이나 - -X 는 x -= 1 과 같습니다.

간단한 예제를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 0;
    x++;
    printf("x = %d\n", x);
    ++x;
    printf("x = %d\n", x);
    
    x--;
    printf("x = %d\n", x);
    --x;
    printf("x = %d\n", x);
    
    return 0;
}

/*
 x = 1
 x = 2
 x = 1
 x = 0
*/


그런데, 사실 연산자가 앞에 붙는 것과 뒤에 붙는 것은 차이가 있습니다.

다음 코드를 보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 0;
    printf("x++ = %d\n", x++);
    printf("x = %d\n", x);
    printf("++x = %d\n", ++x);
    printf("x = %d\n", x);
    
    return 0;
}

/*
 x++ = 0
 x = 1
 ++x = 2
 x = 2
*/

첫번째 출력문을 살펴보면 x값이 증가하지 않았습니다.

그 이유는 x++은 printf()가 먼저 실행되고 난 후, 실행되기 때문입니다.

그래서 두번째 출력문에서는 1증가한 x가 나옵니다.

세번째 출력문은 x가 증가한대로 나옵니다.

그 이유는 ++x는 printf()보다 먼저 실행되기 때문입니다.




비트 연산자(&, |, ^, ~, », «)


비트 연산자는 정보를 자료형으로 해석하는게 아니라

비트 단위로 해석하여 계산하는 연산자 입니다.

간단한 예제를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 0x11223344;
    
    printf("%08x\n", x & 0x00FFFF00);
    printf("%08x\n", x | 0x2211FFFF);
    printf("%08x\n", x ^ 0x2211FFFF);
    printf("%08x\n", ~x);
    printf("%08x\n", x >> 8);
    printf("%08x\n", x << 16);
    
    return 0;
}

/*
 00223300
 3333ffff
 3333ccbb
 eeddccbb
 00112233
 33440000
*/

일단 숫자에 붙어있는 0x 는 16진수를 의미합니다.

보통 비트 단위로 상수를 나타낼 때는 16진수를 사용합니다.

16진수니까 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F 까지 있습니다.


먼저 비트 연산자 표입니다.

연산자 비트1 비트2 연산결과
& 0 0 0
0 1 0
1 0 0
1 1 1
| 0 0 0
0 1 1
1 0 1
1 1 1
^ 0 0 0
0 1 1
1 0 1
1 1 0


먼저 AND(&)연산자를 살펴보겠습니다.

&는 피연산자가 모두 1이여야만 1이 나오는 연산자입니다.

위 코드에서는 11223344 & 00FFFF00 이 연산되었습니다.

이를 비트 연산으로 나타내면

    0001 0001 0010 0010 0011 0011 0100 0100
& 0000 0000 1111 1111 1111 1111 0000 0000
=========================================
    0000 0000 0010 0010 0011 0011 0000 0000

으로 00223300 이 나옵니다.


이번엔 OR(|)연산자를 살펴보겠습니다.

|는 피연산자 중 하나라도 1이면 1이 나오는 연산자입니다.

위 코드에서는 11223344 | 2211FFFF 가 연산되었습니다.

이를 비트 연산으로 나타내면

  0001 0001 0010 0010 0011 0011 0100 0100
| 0010 0010 0001 0001 1111 1111 1111 1111
========================================
  0011 0011 0011 0011 1111 1111 1111 1111

으로 3333FFFF 가 나옵니다.


이번엔 XOR(^)연산자를 살펴보겠습니다.

^는 피연산자가 서로 다르면 1이 나오는 연산자입니다.

위 코드에서는 11223344 ^ 2211FFFF 가 연산되었습니다.

이를 비트 연산으로 나타내면

  0001 0001 0010 0010 0011 0011 0100 0100
^0010 0010 0001 0001 1111 1111 1111 1111
========================================
  0011 0011 0011 0011 1100 1100 1011 1011

으로 3333CCBB 가 나옵니다.


이번엔 NOT(~)연산자를 살펴보겠습니다.

~은 0을 1로, 1을 0으로 바꾸는 연산자입니다.

위 코드에서는 ~11223344 가 연산되었습니다.

이를 비트 연산으로 나타내면

~0001 0001 0010 0010 0011 0011 0100 0100
=========================================
  1110 1110 1101 1101 1100 1100 1011 1011

으로 EEDDCCBB 가 나옵니다.


이번엔 Shift Right(») 연산자를 살펴보겠습니다.

>>는 비트를 오른쪽으로 미는 것입니다.

위 코드에서는 11223344 >> 8 이 연산되는데

이는 11223344를 오른쪽으로 8칸 밀라는 뜻입니다.

그럼 원래 11223344는

0001 0001 0010 0010 0011 0011 0100 0100

이고, 오른쪽 8개 비트는 밀려서 없어지고 왼쪽에는 새로는 8개 비트가 0으로 채워집니다.

즉, 0000 0000 0001 0001 0010 0010 0011 0011 이 되어

16진수로 나타내면 00112233이 됩니다.

주의해야할 점은 비트가 채워질 때 1로 채워질 때도 있습니다.

singed 자료형에서 맨 왼쪽의 부호비트가 1일때 발생하는데, 부호를 유지하기 위함입니다.

이 코드를 실행시켜보시기 바랍니다.

printf("%08x\n", -1000);
printf("%08x\n", -1000 >> 2);

// 오른쪽으로 두칸 밀었는데 1로 채워져
// 앞이 또 f로 채워집니다.


이번엔 Shift Left(«) 연산자를 살펴보겠습니다.

«는 비트를 왼쪽으로 미는 것입니다.

위 코드에서는 11223344 « 16 이 연산되는데

이는 11223344를 왼쪽으로 16칸 밀라는 뜻입니다.

그럼 원래 11223344는

0001 0001 0010 0010 0011 0011 0100 0100

이고, 왼쪽 16개 비트는 밀려서 없어지고 오른쪽에는 새로운 16개 비트가 0으로 채워집니다.

즉, 0011 0011 0100 0100 0000 0000 0000 0000 이 되어

16진수로 나타내면 33440000이 됩니다.

«는 채워짐이 무조건 0으로 채워집니다.




비트마스크


위에서 알아본 비트연산자 중에서

AND(&)연산자를 활용한 비트마스크 연산이란게 있습니다.

비트마스크 연산은 여러 정보중에 필요한 정보만 잘라내고자 할 때 쓰입니다.

간단한 예제를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 0x11223344;
    
    printf("%08x\n", x & 0xFFFF0000);
    printf("%08x\n", x & 0x00FFFF00);
    
    return 0;
}

/*
 11220000
 00223300
*/

첫번째 출력문에선 원래의 x값인 11223344에서

앞의 16비트 정보만 추려내고 있습니다.

두번째 출력문에선 원래의 x값인 11223344에서

가운데 16비트 정보만 추려내고 있습니다.


저도 비트마스크를 실제로 활용해본적은 많이 없지만

종종 유용하게 쓸 수 있는 것 같습니다.

비트마스크를 활용한 알고리즘 문제 풀기는 여기에서 살펴보실 수 있습니다.

코드 내용은 아마 나중에 살펴볼 제어문을 알아야 이해할 수 있을 것 같습니다.




sizeof()


sizeof 연산자는 피연산자의 자료형의 크기를 계산합니다.

예로 sizeof(7)이라 하면 7은 int이고 int는 4바이트 이므로 4가 계산됩니다.

간단한 예제를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 7;
    
    printf("sizeof(x) = %d, sizeof(5) = %d, sizeof(int) = %d\n", sizeof(x), sizeof(5), sizeof(int));
    printf("sizeof('A') = %d, sizeof(char) = %d\n", sizeof('A'), sizeof(char));
    printf("sizeof(7.77f) = %d, sizeof(7.77) = %d\n", sizeof(7.77f), sizeof(7.77));
    
    printf("sizeof(x*11) = %d, sizeof(++x) = %d\n", sizeof(x*11), sizeof(++x));
    printf("x = %d\n\n", x);
    
    char name[30];
    printf("sizeof(name) = %d\n\n", sizeof(name));
    
    return 0;
}

/*
 sizeof(x) = 4, sizeof(5) = 4, sizeof(int) = 4
 sizeof('A') = 4, sizeof(char) = 1
 sizeof(7.77f) = 4, sizeof(7.77) = 8
 sizeof(x*11) = 4, sizeof(++x) = 4
 x = 7
 
 sizeof(name) = 30
*/

주의해야할 점은 sizeof(++x)의 경우

안에 ++x가 연산되지 않는다는 점입니다.

그래서 마지막 x값 출력에서 그대로 7이 출력됩니다.


그리고 sizeof 연산자는 프로그램 빌드 후 CPU가 실행하는 런타임 연산자가 아니라

컴파일러가 컴파일타임에 수행하는 연산자입니다.

즉, 아무리 많이 사용해도 프로그램이 느려지거나 하는일은 없습니다.




관계 연산자


관계 연산자에는 다음과 같은 종류가 있습니다.

연산식 분류 의미
A == B 상등연산 A와 B가 같다.
A != B 부등연산 A와 B가 다르다.
A > B 비교연산 A는 B보다 크다.
A < B A는 B보다 작다.
A >= B A는 B보다 크거나 같다.
A <= B A는 B보다 작거나 같다.

의미가 참이면 참(True, 1), 거짓이면 거짓(False, 0)의 결과를 내는 연산자입니다.

참고로 C에서 ‘참’의 정확한 의미는 ‘0이 아닌 모든 값’입니다.

간단한 예제를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 7, y = 77;
    
    printf("x == y ? %d\n", x == y);
    printf("x != y ? %d\n", x != y);
    printf("x > 8 ? %d\n", x > 8);
    
    return 0;
}

/*
 x == y ? 0
 x != y ? 1
 x > 8 ? 0
*/


이 연산자들은 피연산자의 자료형이 서로 다를때는 쓸 때 주의해야합니다.

다음 코드를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    printf("300 == 299.99999f ? %d\n", 300 == 299.99999f);
    printf("300 == 299.99999  ? %d\n", 300 == 299.99999);
    
    return 0;
}

/*
 300 == 299.99999f ? 1
 300 == 299.99999  ? 0
*/

299.99999가 float일때는 300과 같다고 나오고

double일때는 다르다고 나옵니다.

전에도 언급했었던 c에서의 소수처리 방법(부동소수점)에 따른 오차 때문입니다.




논리 연산자(&&, ||)


논리 연산자도 관계 연산자처럼 참이면 1, 거짓이면 0을 반환합니다.


먼저 논리곱(&&)을 살펴보겠습니다.

예를 들어 이런 프로그램을 짠다고 가정해보겠습니다.

어떤 놀이기구를 타는데 적정 키는 140cm ~ 190cm 입니다.

키를 입력했을 때, 탑승 가능하면 1, 탑승 불가능하면 0을 출력합니다.

#include <stdio.h>

int main(void)
{
    int height = 0, result = 0;
    printf("키를 입력하세요: ");
    scanf("%d", &height);
    
    result = height >= 140 && height <= 190;
    printf("result: %d\n\n", result);
    
    return 0;
}

/*
 키를 입력하세요: 191
 result: 0
*/

입력받은 키를 체크하는 코드를 살펴보면 && 연산자가 쓰인걸 볼 수 있습니다.

코드를 보고 사용법을 알면됩니다.


이번엔 논리합(||)을 살펴보겠습니다.

예를 들어 어떤 미생물이 있는데 번식을 억제하고 현상태를 유지하려 합니다.

번식은 온도와 관련있는데 20도 미만이거나 70도 초과면 번식하지 않습니다.

현재 온도를 입력했을 때, 번식억제 온도면 1, 번식 온도면 0을 출력합니다.

#include <stdio.h>

int main(void)
{
    int temperature = 0, result = 0;
    printf("온도를 입력하세요: ");
    scanf("%d", &temperature);
    
    result = temperature < 20 || temperature > 70;
    printf("result: %d\n\n", result);
    
    return 0;
}

/*
 온도를 입력하세요: 80
 result: 1
*/

온도를 체크하는 코드를 보면 || 연산자가 쓰인걸 볼 수 있습니다.

코드를 보고 사용법을 알면 됩니다.




부정(NOT, !)


부정연산자는 거짓(0)이면 1로, 참(0이 아닌 모든 수)이면 0으로 바꾸는 연산자입니다.

코드를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int x = 123;
    int y = 0;
    
    printf("!x = %d\n", !x);
    printf("!y = %d\n", !y);
    
    return 0;
}

/*
 !x = 0
 !y = 1
*/




조건 연산자(삼항 연산자)


조건 연산자는 C언어의 유일한 삼항 연산자 입니다.

다음과 같이 사용합니다.

조건식 ? A : B

조건식이 참이면 A를, 거짓이면 B를 선택합니다.

간단한 코드를 살펴보겠습니다.

이 코드는 입력된 점수가 70점 이상이면 ‘P’를,

70점 미만이면 ‘F’를 출력하는 프로그램입니다.

#include <stdio.h>

int main(void)
{
    int score = 0;
    // P: Pass, F: Fail
    char result = 'P';
    
    printf("점수를 입력하세요: ");
    scanf("%d", &score);
    
    result = score >= 70 ? 'P' : 'F';
    printf("result: %c\n\n", result);
    
    return 0;
}

/*
 점수를 입력하세요: 50
 result: F

*/

점수를 입력받고, result값을 재조정해주는 코드를 보면 됩니다.

이렇게도 사용할 수 있습니다.

#include <stdio.h>

int main(void)
{
    int score = 0;
    
    printf("점수를 입력하세요: ");
    scanf("%d", &score);

    printf("결과: %s\n\n", score >= 70 ? "합격":"불합격");
    
    return 0;
}

/*
 점수를 입력하세요: 88
 결과: 합격

*/




Back to the Goal


1. 초를 입력받아 시분초 단위로 바꾸는 프로그램

c_operator_goal1

#include <stdio.h>

int main(void)
{
    int time = 0, hour = 0, min = 0, sec = 0;
    printf("초를 '시:분:초'로 변환하는 프로그램\n\n");
    printf("초를 입력하세요: ");
    scanf("%d", &time);
    
    hour = time / 3600;
    min = (time % 3600) / 60;
    sec = time % 60;
    
    printf("%d초는 %02d시간 %02d분 %02d초 입니다.\n\n", time, hour, min, sec);
    
    return 0;
}

나눗셈 연산과 나머지 연산을 적절히 사용하면 쉽게 작성할 수 있습니다.


2. 국영수 점수 입력받아 합격여부 출력하는 프로그램

c_operator_goal2_1 c_operator_goal2_2 c_operator_goal2_3

#include <stdio.h>

int main(void)
{
    int kor = 0, eng = 0, math = 0, sum = 0, min = 100;
    double avg = 0.0;
    printf("국영수 점수를 받아 합격 여부 출력하는 프로그램\n");
    printf("과락 기준: 50점 / 평균 기준: 70점\n\n");
    
    printf("점수 입력\n");
    printf("국어: ");
    scanf("%d", &kor);
    printf("영어: ");
    scanf("%d", &eng);
    printf("수학: ");
    scanf("%d", &math);
    
    sum = kor + eng + math;
    avg = sum / 3;
    min = min < kor ? min:kor;
    min = min < eng ? min:eng;
    min = min < math ? min:math;
    
    printf("결과 - 총점: %d, 최저점: %d, 평균: %.2f, 합/불: %s\n\n", sum, min, avg, (min >= 50 && avg >= 70) ? "합격":"불합격");
    
    return 0;
}