티스토리 뷰

Computer/C

[C언어] 연산자와 상수

jamezc 2015. 2. 24. 11:01

연산자와 상수

 
1. 연산자 (Operator)
 
 
우선순위별로 보면 다음과 같다
1. 소괄호 () : 수학에서 괄호 속을 먼저 연산하는 것을 떠올리면 된다.
 
2. 단항연산자 : 1개의 항에 대하여 연산을 해준다.
 2-1) 논리연산자 ! : 피연산자 a의 값이 거짓(0)이면 참(1)을, 그 외의 수는 거짓(0)을 반환한다. 사용법 [ !a ]
 2-2) 비트연산자 ~ : 피연산자 a를 bitwise(비트 반전) 시킨다. 즉, 1100 -> 0011. 사용법 [ ~a ]
 2-3) 증감연산자 ++, -- : 피연산자의 앞에 오냐(전위증가), 뒤에 오냐(후위증가)에 따라 의미가 약간 달라진다.
    ++a (전위증가) 의 경우 a를 1 증가시키고 나머지 연산을 시행하는 반면
    a++ (후위증가) 의 경우 연산을 모두 마친 후, 최종적으로 a를 증가시킨다.
 
    ※ 후위증가가 전위증가보다는 우선순위가 높다. 사용법 [ ++a, --a, a++, a-- ]
 2-4) sizeof(변수, 자료형) : 입력받은 변수 혹은 자료형의 크기를 byte 단위로 반환한다. 사용법 [ sizeof(int), sizeof(a) ]
 2-5) casting연산자 (자료형) : 명시적으로 자료형을 변환해준다. 낮은 곳에서 높은 곳으로 형변환(casting)이 될 때는 데이터 손실이 일어나지 않지만, 높은 곳에서 낮은 곳으로 형변환이 될 때에는 데이터 손실이 일어난다.
   (double)3 : 3이라는 숫자는 정수형이지만, double로 형변환 하여 3.0과 동일한 가치를 지닌다. 상위형변환 이므로 데이터 손실이 일어나지 않는다.
   (int)3.5 : 3.5라는 숫자는 실수형이지만, integer로 형변환 하여 3이라는 가치를 지닌다. 하위형변환 이므로 데이터 손실이 일어난다.
 
3. 산술연산자 : 뺄셈, 곱셈, 나머지연산 - 덧셈, 뺄셈 순으로 수학과 동일하다.
 a+b, a-b, a*b, a/b : 각 연산에 대한 결과 반환. 단, 5/2의 경우 2.5가 아니라 2가 된다. (int / int = int가 되는데 2.5는 double형이므로 하위형변환이 묵시적으로 일어남)
 a%b : a를 b로 나눈 나머지 값을 반환한다. 즉, 123%10 = 3이다.
 
4. 비트이동 연산자 <<, >> : a의 비트를 <<(좌측), >>(우측)으로 b만큼 비트를 이동한다. 밀려나는 값들은 사라지고, 새로운 자리에는 0이 들어온다.
 a << 2 : a의 비트가 00001110 이면 00111000이 된다.
 
 ※ 비트를 왼쪽으로 한 번 이동하는 것은 2를 곱하는 효과, 오른쪽으로 한 번 이동하는 것은 2로 나누는 효과를 가져온다.
 
5. 관계연산자 : 다음 식이 참이면 1을 반환, 거짓이면 0을 반환한다.
 a<b, a>b, a<=b, a>=b, a==b (같다 라는 표현은 =을 두개 사용하여 나타낸다), a!=b (a와 b가 다르다)
 
6. 비트논리연산자 : and, or, xor의 논리값을 반환한다.
 a&b : a와 b가 둘 다 참(1)일 때만 1을 반환한다. 그 외에는 0
 a|b : a와 b가 둘 다 거짓(0)일 때만 0을 반환한다. 그 외에는 1
 a^b : a와 b가 서로 다를 때 1을 반환한다. 그 외에는 0
 
 n bit에 대하여 n 비트 전부를 개별로 검사한다. 그리고 그 값을 개별적으로 반환하여 합하여 계산한다. 
즉, 11000000 & 01000001 = 01000000으로 반환한다. 이는 특정 비트를 검사할 때 매우 유용하다. 특정 비트가 검사된다면 왼쪽 예와 같이 0이 아닌 값을 반환하기 때문이다. 가령 예를 들어 하위 두번 째 비트가 1인지 아닌지 여부를 검사한다고 가정하자. 그러면
& 00000010을 주면 하위 두번 째 비트가 1일 경우에만 0이 아닌 수가 반환된다.
 
11111101 & 00000010 = 00000000 (거짓값 : 하위 두번 째 비트가 1로 세팅되어 있지 않다)
11100011 & 00000010 = 00000010 (참 값 : 하위 두번 째 비트가 1로 세팅되어 있다)
11000000 & 00000010 = 00000000 (거짓값 : 하위 두번 째 비트가 1로 세팅되어 있지 않다)
 
7. 논리연산자 : and, or의 논리값을 반환한다. 이 때에는 피연산자 자체가 0이 아닌 경우 참, 0인 경우 거짓을 나타낸다.
 a&&b : a와 b가 둘다 참일 때만 1을 반환한다. 11111101 && 00000010의 경우 둘 다 0이 아닌 수라 참의 값을 지니므로 결과는 참이 된다. 주로 관계연산자와 같이 사용된다. (a>=b) && (a>100) 와 같이 사용한다.
 a||b : a와 b가 둘다 거짓일 때만 0을 반환한다.
 
8. 조건연산자 : a? b: c 의 꼴이며, a가 참이면 b, 거짓이면 c를 반환한다.
 int max = (a>b)? a: b; 의 형태로 사용된다 // a가 b보다 크면 a, 작으면 b가 반환되서 max라는 변수에 할당이 된다.
 
9. 할당 및 복합 할당연산자 (오른쪽에서 왼쪽으로)
 a = b :  위에서 a와 b가 같다라는 연산자는 ==를 사용했는지 알 수 있을 것이다. b의 값을 a에 할당한다.
 a += b : a = a+b 와 동일한 의미를 지닌다. 즉, 현재 a값에 b값을 더해 다시 a에 할당하라는 의미.
 a -= b : a = a-b 와 동일
 a *= b : a = a*b 와 동일
 a /= b : a = a/b 와 동일
 ...
 
10. 콤마 연산자 : 성격이 동일한 자료형을 나열할 때 쓰인다.
 int myInt, yourInt, hisInt, herInt, .... ;
 
 
 
이 모든 것을 어떻게 외우느냐? 하다보면 된다. 진심이다(...)
 
 
 
 
2. 상수(Constant)
 
 
리터럴(literal)이라고 한다. 자기 표현 자체가 값이 되는 것을 의미한다. 가령 예를 들면, int myInt = 1; 에서 1과 같은 것이다.
여기에는 여러 가지 종류가 있는데, 대표적으로는 다음과 같다.
 
접두어 : 특별한 선언이 없으면 10진수이다. 0x로 시작하면 16진수, 0으로 시작하면 8진수로 인식된다.
접미어 : 특별한 선언이 없으면 정수 / double형 실수로 인식한다. 즉, float myFloat = 1.234; 와 같은 경우, 우변의 1.234가 double형으로 인식되기에 하위형변환이 일어나 데이터가 손실 될 수 있다는 경고메세지가 뜰 것이다. (안뜨는 컴파일러도 있다.)
이럴 때, 명시적으로 float myFloat = 1.234f 혹은 1.234F 와 같이 float형을 명시해주면 경고메세지가 사라진다.
 
접미어로 L, l(이건 대문자 i와 상당히 유사하기에 보통 쓰이지 않는다)을 명시하면 long 타입의 정수가 된다. 
long int myLongInt = 1L;
 
 
혹은 const로 선언하는 변수는 심볼릭 상수(Symbolic Constant)가 된다. 이 경우, 이 변수는 더 이상 값을 수정할 수 없고, 초기값으로 지정되는 값을 상수로 가진다. 즉, 초기화 하지 않으면 쓰레기로 초기화가 되고, 값을 변경할 수 없다.
 
변수를 선언하는 형식 앞에 const를 추가한다. const double PI = 3.141592;
 
 
 
마지막 방법으로는 #define이라는 매크로를 이용하는 방법이 있다.
#define PI 3.1415 와 같이 선언한다. ( #include <stdio.h> 가 있는 위치에 선언한다 ). 컴파일러에 의해 타입은 자동으로 정해지므로 특별히 신경쓸 일은 없다. 보통 #define MAX 100 와 같은 용도로 선언하고, 프로그램을 수정할 일이 있으면 맨 윗줄의 #define문만 수정하면 되므로 매크로를 사용하지 않았을 때 보다 더욱 간결하다.
 
 
예를 들어, 다음과 같은 프로그램이 있다고 가정한다. (의미는 생각하지 않아도 된다)
 
#include <stdio.h> #include <stdio.h>
#define MAX 100
 
void main(){ void main(){
int i, j, arr[MAX][MAX]; int i, j, arr[100][100];
for(i=0; i<MAX; i++){ for(i=0; i<100; i++){
for(j=0; j<MAX; j++){ for(j=0; j<100; j++){
printf("%d", arr[i][j]); printf("%d", arr[i][j]);
} }
printf("\n"); printf("\n");
} }
} }
 
 
여기서 크기를 100에서 200으로 바꾸고 싶다고 할 때, 왼쪽의 코드는 #define MAX 200으로만 바꾸어 주면 되지만, 오른쪽의 코드는 100이 들어간 부분을 전부 다 찾아서 고쳐야 한다. 코드가 간결할 때는 별 문제가 없지만, 수 백, 수천줄의 코드를 모두 바꾸기란 여간 귀찮은 일이 아니며, 행여 하나라도 수정을 못하면 예상치 못한 결과가 나올 수 있다. 
 
순수하게 치환되는 방식을 취하므로 다음과 같이 임의로 자신만의 상태(statement)들을 만들 수 있다.
 
#define A_NONE 1000
#define A_KOR 1001
#define A_JPN 1002
#define A_CNA 1003
...
 
 
...
 
int myNation = A_KOR;
if( myNation == A_JPN ) myNation = A_KOR;
 
 
물론 이 경우에 한해서는 #define 대신에 int A_NONE = 1000; 으로 선언하고 똑같이 작성해도 상관없지만, 위에도 말했다시피 #define으로 선언된 것의 자료형은 컴파일러가 알아서 결정해주고, 변수선언으로는 불가능한 작업도 #define은 가능하게 해준다.

 

 

 

 

댓글

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음



Total
Today
Yesterday
최근에 달린 댓글