- 수식과 연산자
- 수식(expression) : 피연산자와 연산자의 조합
- 연산자(operator) : 어떤 연산을 나타내는 기호
- 피연산자(operand) : 연산의 대상이 되는 것
ex) 수식 (5 * 8)에서 5와 8은 피연산자이고 *는 연산자이다.
또한 모든 수식은 값을 가짐. 수식 (5*8)의 값은 40.
수식은 항상 계산값을 반환한다고 생각하기.
- 연산자의 분류
- 증감 연산자 : 단항, ++ --
- 대입 연산자 : 이항, =, +=, -=
- 산술 연산자 : 이항, + - * / %
- 관계 연산자 : 이항, > < == != >= <=
- 논리 연산자 : 이항, && || !
- 비트 연산자 : 이항, & | ^ ~ << >>
- 조건 연산자 : 삼항, ?:
* 단항, 이항, 삼항 연산자로 나뉘는 것은 피연산자의 수에 따른 것이다.
- 산술 연산자(+, -, *, /, %) : 기본적인 산술 연산인 덧셈, 뺄셈, 곱셈, 나눗셈, 나머지 연산을 실행하는 연산자.
* 산술 연산자의 종류
- 덧셈 : +
- 뺄셈 : -
- 곱셈 : *
- 나눗셈 : /
- 나머지 : %
참고) %를 출력하려면 "%%"하여야 한다.
참고) 정수 7을 정수 4로 나누면(/) 3이고 실수 7.00을 실수 4.00으로 나누면(/) 1.75(소수점 둘째자리까지만 표기)이다.
- 나머지 연산자(%)
- x%y는 x를 y로 나누어서 남은 나머지를 반환한다.
ex) 7 % 2 = 1
- 어떤수가 홀수인지 짝수인지 판별할 때 사용 가능. 해당 수를 2로 나눈 나머지가 0이라면 해당 수는 짝수, 1이라면 해당 수는 홀수.
- 숫자의 자릿수를 추출하는 데에도 사용 가능.
ex) 123에서 일의 자릿수, 십의 자릿수, 백의 자릿수를 추출하려면 각각 (123 % 10), ((123/10)%10), ((123/100)%10)을 하면 된다.
- 증감 연산자(++, --) : ++ 기호나 -- 기호를 사용하여 변수의 값을 1만큼 증가시키거나 감소시키는 연산자.
- 증감 연산자는 피연산자의 앞이나 뒤에 올 수 있는데, 이것에 따라 수식의 값이 달라진다.
ex) ++x : 수식의 값은 증가된 x값. 문장에서 x의 값을 증가시킨 후에 수식의 값을 계산.
x++ : 수식의 값은 증가되지 않은 x값. 수식의 값을 계산한 후에 x의 값을 증가시킴. 현재의 증가되기 전의 x값을 반환하고 x 값을 1 증가시킴.
--x : 수식의 값은 감소된 x값.
x-- : 수식의 값은 감소되지 않은 x값.

- 대입 연산자(=(등호), assignment operator, 할당 연산자, 배정 연산자) : 변수에 값을 저장하기 위하여 사용하는 연산자.
- 왼쪽에 있는 변수에 오른쪽에 있는 수식의 값을 계산하여 저장함.
- 대입 연산자는 연속해서 사용될 수 있다.
ex) x = y = z = 0;
- 복합 대입 연산자 : C언어에서 대입 연산자는 다른 연산자와 함께 사용 가능. 예를 들어, 산술 연산자와 함께 사용하여 변수에 값을 더하거나 빼거나 곱할 수 있다. 복합 대입 연산자는 기존 변수의 값에 새로운 값을 연산하여 다시 할당하는 연산자. 수식을 간략화하여 작성 가능하다.
ex) x += y (의미 : x = x + y)
- +=, -=, *=, /=, %= 가능.
- 관계 연산자(relational operator, >, <, ==, !=, >=, <=) : 주어진 두 값의 관계를 비교하여 참 또는 거짓을 반환하는 연산자.
- 조건문과 반복문 등의 제어 구조에서 자주 사용됨.
- 결과는 참(true,1) 아니면 거짓(false, 0)으로 계산됨.
- 순서를 바꿔쓰거나(<=를 =<로) 중간에 공백을 집어넣지 않아야 한다.(<=를 < =라고 오표기 또는 <<를 < <라고 표기하여 비트 이동 연산자로 만들기)
- 6가지 관계 연산자
- x ==y : x와 y의 값이 같으면 참을 반환.
- x!=y : x와 y의 값이 다르면 참을 반환.
- x>y : x가 y보다 크면 참을 반환.
- x<y : x가 y보다 작으면 참을 반환.
- x>=y : x가 y보다 크거나 같으면 참을 반환.
- x<=y : x가 y보다 작거나 같으면 참을 반환.
* C에서 참과 거짓은 1과 0으로 표시됨.
- 실수를 비교할 때
- 실수를 비교할 때 == 연산자로 비교하는 경우에는 부동소수점 정밀도 오차로 인하여 예상치 못한 결과가 발생할 수 있다.
- 실숫값을 올바르게 비교하려면 fabs(a-b) < c와 같이 비교를 해야한다. 이 함수는 실숫값의 절댓값을 구하는 함수로, a와 b의 차이를 계산하고, 차이가 c보다 작다면 같다고 간주하는 것이다. c는 0.0001, 1e-9 등의 숫자를 사용한다.
- 논리 연산자(&&, ||, !) : 여러 개의 조건을 조합하여 참인지 거짓인지를 따질 때 사용.
- 3가지 논리 연산자
- && : AND 연산, x와 y가 모두 참이면 참, 그렇지 않으면 거짓.
- || : OR 연산, x나 y 중에서 하나만 참이면 참, 모두 거짓이면 거짓.
- !x : NOT 연산, x가 참이면 거짓, x가 거짓이면 참.
ex) result = !(2==2) 실행시 result값은 최종적으로 0이 됨. ((2==2) 실행시 1.)
ex) !100, !-3 실행시 0이 됨.
- NOT 연산자는 논리적인 NOT과 차이가 있다. 논리적인 NOT은 두 번 적용시키면 원래의 상태가 되지만, 연산자 NOT은 두 번 적용시켜도 원래의 상태가 되지 않는 경우가 있다.
ex) !!3 실행시 수식의 값은 1이 된다. !!3 = !(!3)이고, !3은 0. 따라서 !0이므로 1.
- 수식의 결과로는 항상 0 아니면 1만 생성되지만, 피연산자로서 참과 거짓을 분류할 때는 0이면 거짓이고 나머지는 모두 참으로 간주한다. (!100에서 100의 값을 참으로 간주해 여기에 NOT을 적용하여 0 값이 생성되는 것임.)
- 단축 계산(short circuit evaluation) : 모든 피연산자들을 계산하지 않고 전체 수식의 값을 얻는 기법.
- && 연산자의 경우, 여러 개의 피연산자 중에서 처음 피연산자의 값이 거짓(0)이면 다른 피연산자들은 계산되지 않는다. 왜냐하면 첫 번째 피연산자의 값이 거짓(0)이면 나머지 피연산자들을 계산하지 않아도 전체 수식의 값은 거짓(0)이기 때문.
ex) ( 2 > 3 ) && ( ++x < 5 ) 에서 ( ++x < 5 )는 실행되지 않는다. 앞의 값이 거짓이기 때문이다.
- 수식의 계산을 빠르게 하기 위하여 컴파일러에서 사용하는 기법.
- || 연산자의 경우, 첫 번째 피연산자의 값이 참(1)이면 나머지 피연산자들을 계산하지 않는다. 왜냐하면 전체 수식은 이미 값이 참(1)이기 때문이다.
ex) ( 3 > 2 ) || ( ++x < 5 ) 에서 ( 3 > 2 )가 이미 참이기 때문에 뒤의 수식은 실행되지 않는다.
- 조건 연산자(?:)
- 유일하게 3개의 피연산자를 가지는 삼항 연산자이다.
ex) max_value = ( x > y ) ? x : y 식에서 조건 ( x > y )가 참이면 x가 수식의 결과값, 따라서 x가 max_value에 대입됨. 조건이 거짓이면 y가 수식의 결과값, 따라서 y가 max_value에 대입됨.
- 대표적인 이용 사례
ex1) absolute_value = (x > 0) ? x: -x; // 절댓값 계산
ex2) max_value = (x > y) ? x: y; // 최댓값 계산
ex3) min_value = (x < y) ? x: y; // 최솟값 계산
- 조건 연산자에는 printf()와 같은 문장도 넣을 수 있다.
ex) (age >= 20) ? printf("성인\n"): printf("청소년\n");
- 비트 연산자(&, |, ^, < <, > >, ~) : 비트별로 AND 연산이나 OR 연산을 하는 연산자.
- 비트(bit)는 컴퓨터에서 정보를 저장하는 가장 작은 단위. 비트는 이진수의 한 자리에 해당하므로 0 또는 1의 값을 가질 수 있다.
- 6가지 비트 연산자
- & : 연산자의 의미는 비트 AND로, 두 개의 피연산자의 해당 비트가 모두 1이면 1, 아니면 0.
- | : 연산자의 의미는 비트 OR로, 두 개의 피연산자의 해당 비트 중 하나만 1이면 1, 아니면 0.
- ^ : 연산자의 의미는 비트 XOR로, 두 개의 피연산자의 해당 비트의 값이 같으면 0, 아니면 1.
- < < : 연산자의 의미는 왼쪽으로 이동으로, 지정된 개수만큼 모든 비트를 왼쪽으로 이동.
- > > : 연산자의 의미는 오른쪽으로 이동으로, 지정된 개수만큼 모든 비트를 오른쪽으로 이동.
- ~ : 연산자의 의미는 비트 NOT으로, 0은 1로 만들고 1은 0으로 만듦.
- 비트 연산자는 정수 타입의 피연산자에만 적용할 수 있다(char, short, int, long 등). 정수 타입이면 signed나 unsigned에 상관없이 모두 적용 가능. 실수에는 적용할 수 없다.
참고) %08X은 8자리의 16진수로 표시하라는 의미이다.
- 비트 이동(shift) 연산자는 지정된 숫자만큼 전체 비트를 이동한다. 이동 가능 방향으로는 왼쪽과 오른쪽이 있다.
- x << y : x의 비트들을 y칸만큼 왼쪽으로 이동. 비트들을 왼쪽으로 한번 이동할 때마다 값은 두배가 된다.
- x >> y : x의 비트들을 y칸만큼 오른쪽으로 이동. 비트들을 오른쪽으로 한번 이동할 때마다 값은 1/2배가 된다. 왼쪽의 발생하는 빈 공간은 부호비트로 채워야 한다. 양수면 부호 비트는 0, 음수면 부호비트는 1.
- 정수 32(int a = 32)를 비트 연산만으로 2의 보수로 만들어서, 음수 -32로 만드는 방법 : 모든 비트를 반전 시킨 후(~(NOT 연산), a = ~a) 1을 더하여(a = a + 1) 2의 보수로 만들기.
참고) 10진수를 2진수로 출력하기
#include <stdio.h>
int main(void)
{
int decimal;
printf("10진수를 입력하시오(0에서 255 사이): ");
scanf("%d", &decimal);
//이진수 형태로 출력
printf("%d의 2진수: ", decimal);
printf("%d", (decimal >> 7) & 1); //해당 비트가 1인지 0인지 검사
printf("%d", (decimal >> 6) & 1); //해당 비트가 1인지 0인지 검사
printf("%d", (decimal >> 5) & 1); //해당 비트가 1인지 0인지 검사
printf("%d", (decimal >> 4) & 1); //해당 비트가 1인지 0인지 검사
printf("%d", (decimal >> 3) & 1); //해당 비트가 1인지 0인지 검사
printf("%d", (decimal >> 2) & 1); //해당 비트가 1인지 0인지 검사
printf("%d", (decimal >> 1) & 1); //해당 비트가 1인지 0인지 검사
printf("%d", (decimal >> 0) & 1); //해당 비트가 1인지 0인지 검사
printf("\n");
return 0;
}
참고) 어떤 수가 2의 거듭제곱인지 비트 연산을 활용하여 알아보기
* x가 2의 거듭제곱이면 (x & (x-1))이 0이다. (&은 비트 AND 연산자임)
ex) 2의 3제곱 = 8은 2진수로 1000이고, 1을 빼면 0111이 되고, 1000 & 0111하면 0이 됨. 2의 거듭제곱보다 1이 작은 수를 메르센 수라고 함.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
int num;
printf("숫자를 입력하세요: ");
scanf("%d", &num);
printf("거듭제곱 여부=%d \n", num > 0 && (num & (num-1)) == 0 );
return 0;
}
- 연산자의 우선순위와 결합 규칙
- 연산자의 우선순위(precedence)
- 많은 연산들 중에서 어떤 연산을 머저 수행할지를 결정하는 규칙.
- 각 연산자들은 서열이 매겨져 있음.
- 우선순위 높은 것부터 나열 :
++, --
%, *, /
+, -
- 우선순위대로 연산 하지 않고 다른 순서로 하고 싶은 경우 괄호 사용.
- C 언어에서 사용되는 모든 연산자의 우선순위 정리
1. () [] -> . ++(후위) --(후위)
- 결합 규칙 : ->(좌에서 우)
2. sizeof &(주소) ++(전위) --(전위) ~! *(역참조) +(부호) -(부호), 형변환
- 결합 규칙 : <-(우에서 좌)
3. *(곱셈) / %
- 결합 규칙 : ->(좌에서 우)
4. +(덧셉) -(뺄셈)
- 결합 규칙 : ->(좌에서 우)
5. << >>
- 결합 규칙 : ->(좌에서 우)
6. < <= >= >
- 결합 규칙 : ->(좌에서 우)
7. == !=
- 결합 규칙 : ->(좌에서 우)
8. &(비트연산)
- 결합 규칙 : ->(좌에서 우)
9. ^
- 결합 규칙 : ->(좌에서 우)
10. |
- 결합 규칙 : ->(좌에서 우)
11. &&
- 결합 규칙 : ->(좌에서 우)
12. ||
- 결합 규칙 : ->(좌에서 우)
13. ?(삼항)
- 결합 규칙 : <-(우에서 좌)
14. = += *= /= %= &= ^= |= <<= >>=
- 결합 규칙 : <-(우에서 좌)
15. ,(콤마)
- 결합 규칙 : ->(좌에서 우)
- 연산자의 결합 규칙(association) : 동일한 우선순위의 연산이 있는 경우에 무엇을 먼저 수행하느냐에 대한 규칙.
- 산술 연산자를 비롯한 대부분의 이항 연산자들은 왼쪽에 있는 연산을 먼저 수행.
- 다항 연산자와 대입 연산자는 오른쪽 연산을 먼저 수행.
참고) 연산자들의 우선순위가 생각나지 않으면 괄호 이용.
- 관계 연산자는 산술 연산자보다 우선순위가 낮다.
ex) x + 2 == y + 3 에서 +는 ==보다 우선순위가 높다.
- 일반적으로 단항 연산자는 이항 연산자보다 우선순위가 높다.
ex) ( ++x <= 10 ) 에서 ++는 <=보다 우선순위가 높다.
- 형변환(type casting) : 데이터의 타입을 변환시키는 연산.
1. 묵시적 형변환 : 컴파일러에 의해 자동 수행되는 것.
- 대입 연산 시에 형변환이 자동 발생 가능. 대입 연산자의 오른쪽에 있는 값은 왼쪽에 있는 변수의 자료형으로 자동적으로 변환됨.
ex) double f; f = 10; 이라는 수식이 있다고 하자. 이 경우 정수 10이 double형으로 변환(10.0)된 후에 변수 f로 대입된다.
- 위와 같은 변환을 올림 변환(promotion)이라고 한다. 올림 변환에서는 문제가 발생되지 않는다.
ex) int x; x = 3.14; 라는 수식이 있다고 하자. 이 경우 소수점 이하는 버려지게 된다(정수부인 3만 x에 저장됨).
- 위와 같은 변환을 내림 변환(demotion)이라고 한다. 데이터의 손실이 발생할 수 있으므로 내림 변환은 문제가 된다.
- 하나의 수식에서 서로 다른 자료형이 사용되면 모든 자료형은 그 중에서 가장 높은 등급의 자료형으로 자동 변환된다. 이유는 데이터 손실을 막기 위해서이다.
ex) 10+1.2345라는 수식 계산 과정에서 int형인 10은 double형 10.0으로 변환(승급)되어서 1.2345에 더해진다. 따라서 전체 수식 값은 double형이 된다.
2. 명시적인 형변환 : 개발자가 직접 형변환을 지정하는 것.
- 형변환 연산자를 사용하여 어떤 데이터의 자료형을 바꿀 수 있다.
ex) int형 변수 5를 double형(5.0)으로 변환하기 : (double) 5
- 변수나 수식 앞에도 사용 가능.
참고) (double)5 / 4에서 형변환 연산자가 우선순위가 높기 때문에 먼저 실행되어 정수 5가 부동소수점수 5.0으로 변환됨.
- 형변환을 하였다고 해서 변수의 형이 변경되는 것은 아님. 변수가 갖고 있는 값을 꺼내서 값의 자료형을 변경해서 수식에서 임시로 사용하는 것임.
- 형변환을 남발하면 프로그램의 가독성이 떨어지고, 자료형 불일치로인한 오류가 발생할 가능성이 높아짐. 따라서 형변환은 필요한 경우에만 사용하는 것이 좋음.
참고) 7.9를 반올림하기
1. double a = 7.9; int b; b = (int)(a + 0.5);
2. math.h에 있는 round() 함수 사용. b = (int) round(a);
* round() 함수는 주어진 부동소수점수를 가장 가까운 정수로 반올림한다. 소수점 둘째 자리에서 반올림하기 때문에, 소수점 자리수를 조절하려면 해당 부동소수점수에 10의 거듭제곱을 곱한 후 round() 함수를 사용한 다음 다시 10의 거듭제곱으로 나누면 됨.
ex) double num = 3.14159; //반올림할 부동소수점수
double rounded = round(num * 100.0) / 100.0; //소수점 둘째 자리에서 반올림
'프로그래밍 언어 > C언어_기초 프로그래밍' 카테고리의 다른 글
[ C언어 기초 ] 6. 반복문 (2) | 2024.09.16 |
---|---|
[ C언어 기초 ] 5. 조건문 (7) | 2024.09.16 |
[ C언어 기초 ] 3. 자료형 (1) | 2024.08.28 |
[ C언어 기초 ] 2. 변수와 입출력 (1) | 2024.08.07 |
[C언어 기초] 1. 기초사항 (0) | 2024.07.09 |