본문 바로가기
Programming/C

[C언어] 연산자 우선 순위, 컴파일, 진법 변환, 선언

by 고간디 2024. 3. 12.

[동아리] A반 1차 2일 과제 (2024.03.11)

2024.03.11 ~ 12

2일 과제

  • 연산자 순서
  • 컴파일 과정 - 간략하게라도 학습
  • printf 함수 숙달되게 연습
  • 2진수, 8진수, 16진수 변환 및 C언어에서의 선언

 


 

연산자 순서

더보기

컴퓨터 프로그램에서 산술식 또는 연산식을 표현하고 처리하기 위해서 사용하는 다양한 기호들을 연산자(Operator)라고 부른다.

C언어에서 사용되는 연산자는 크게 8가지 종류(대입, 산술, 관계, 논리, 할당, 삼향, 비트 연산자)로 나눌 수 있다.

 

1. 대입 연산자 (=)

변수에 어떠한 값을 대입할 때 사용되는 피연산자가 두 개인 이항 연산자이다.

등호 (=) 를 주로 나타내며 왼쪽의 피연산자에 오른쪽의 피연산자를 대입하는 기능을 수행한다.

int num = 1234;

 

이외에도 아래에 기술할 산술 연산자와 결합한 복합 대입 연산자도 존재한다.

 

2. 산술 연산자 (+, -, *, /, %)

대부분의 사람들이 알고 있는 사칙연산에 주로 사용되는 이항 연산자이다.

+, - 는 보편적으로 알고 있듯이 더하기와 빼기를 의미하는데 왼쪽의 피연산에 오른쪽의 피연산자를 더하고 뺄 때사용된다.

int num1 = 1234;
int num2 = 4321;

printf("%d", num1 + num2);	// 5555
printf("%d", num2 - num1);	// 3087

 

곱셈과 나눗셈은 실생활에서 ×, ÷ 이렇게 두 가지 기호를 많이 사용하지만 프로그래밍에서는 곱하기표 대신 별(*) 기호, 나눗셈표 대신 슬래시(/) 기호를 사용한다.

슬래시를 사용한 나눗셈의 경우 몫을 구하는 나눗셈을 나타낸다

나머지를 구하는 나눗셈의 경우에는 퍼센트(%) 기호를 사용한다.

int num1 = 48;
int num2 = 4;

printf("%d", num1 * num2);	// 192
printf("%d", num1 / num2);	// 12
printf("%d", num1 % num2);	// 0

 

 

3. 복합 대입 연산자 (+=, -=, *=, /=, %=, >>=, <<=, &=, |=, ^=)

산술 연산자와 대입 연산자가 결합된 형태의 복합 대입 연산자이다.

왼쪽의 피연산자에 오른쪽의 피연산자로 결합된 산술 연산을 수행한 후의 결과값을 왼쪽의 피연산자에 저장한다.

이때 오른쪽 피연산자의 값에는 변화가 없다.

int num1 = 3;
int num2 = 5;
int num3 = 7;

num1 += num2	// num1 = 3 -> num1 = 8
num3 -= num2	// num3 = 7 -> num3 = 2

 

 

4. 관계 연산자 (>, <,  >=, <=, ==, !=)

두 개의 피연산자를 비교하는 이항 연산자이다.

비교를 통하여 True(1) 또는 False(0) 둘 중 하나를 반환한다.

일반적으로 알고 있는 부등호의 기능과 매우 유사하지만, 등호의 경우 대입 연산자와 구별하기 위해 등호를 두 개 붙여서 사용한다.

!= 연산자는 NOT 게이트 연산으로 같지 않다는 의미를 가진다.

int num1 = 10;
int num2 = 100;

printf("%d", num1 > num2);	// 0
printf("%d", num1 < num2);	// 1
printf("%d", num1 == num2);	// 0
printf("%d", num1 != num2);	// 1

 

 

5. 논리 연산자 (&&, ||, !)

세 가지의 논리 연산자가 있다. 

순서대로 AND, OR, NOT 연산자라고 부른다.

AND, OR 연산자는 이항 연산자지만, NOT 연산자는 단항 연산자이다.

int num1 = 12331;
int num2 = 22133;

printf("%d", (num1 > 20000) && (num2 > 20000));	// 0
printf("%d", (num1 < 20000) && (num2 > 20000));	// 1

printf("%d", (num1 > 20000) || (num2 > 20000)));	// 1

printf("%d", !((num1 > 20000) || (num2 > 20000)));	// 0

 

6.  조건 연산자 (? :)

if ~ else 조건문을 간단하게 대체할 수 있는 연산자이다.

피연산자가 세 개이기 때문에 삼향 연산자라고도 불린다.

 

조건, 참일 때의 리턴, 거짓일 때의 리턴 세 가지의 피연산자를 가진다.

int num1 = 2;
int num2 = 3;
int result1, result2;

result1 = num1 < num2 ? 20 : 30 ;	// result = 20
result2 = num1 > num2 ? 20 : 30 ;	// result = 30

 

7. 증감 연산자 (++, --)

피연산자 하나를 1씩 증가 또는 감소하는 기능을 수행하는 단항 연산자이다.

피연산자가 오른쪽에 위치할 경우 피연산자의 값을 증가 또는 감소시킨 후 연산을 수행하고, 

피연산자가 왼쪽에 위치할 경우 연산을 수행한 후 피연산자의 값을 증가 또는 감소시킨다.

int num1 = 5;
int num2 = 10;

result1 = (num1++) * 10;	// result1 = 60, num1 = 6
result2 = (++num1) * 10;	// result2 = 50, num1 = 5

 

 

8. 비트 연산자 (&, |, ^, ~, <<, >>)

비트 단위로 논리 연산을 수행하는 연산자이다.

논리 연산자와 비슷한 구성으로, &는 비트 AND, |는 비트 OR 연산을 한다.

^는 비트 XOR, ~는 비트 NOT 연산을 수행하고, <<과 >>는 비트 시프트 연산자라고도 불리는데 각각 지정한 만큼의 비트들을 왼쪽, 오른쪽으로 전부 이동시킨다.

 

 

9. 형변환 연산자 (자료형)

서로 다른 자료형의 데이터를 이용해서 연산을 수행하거나 활용하는 경우 형변환의 과정이 필요할 수 있다.

일부 경우에는 컴파일러가 자동적으로 형변환을 해주기도 하지만 수동으로 해주어야만 하는 경우가 있기 때문에 형변환을 해주는 것이 좋다.

int ascii = 97;
float num = 3.14;

printf("%c", (char)ascii); // 'a'
printf("%d", (int)num);	// 3

 

 

10. 기타 연산자 (,, &, sizeof)

쉼표 연산자 또는 콤마 연산자라고 부르는 연산자가 있는데 두 가지 표현식을 동시에 표현하도록 도와주는 연산자이다.

첫 번째 표현식을 연산 후, 그 다음 순서대로 표현식을 연산한다.

int x = 1,2,3;	// x=3

 

 

&를 사용하는 연산자를 주소 연산자 또는 번지 연산자라고 한다.

변수 앞에 사용하는 연산자인데 변수의 주소값을 반환한다.

주소 연산자는 참조 연산자(*)와 함께 포인터의 선언과 참조에 주로 사용된다.

int num1 = 12331;		// 0x00000004
float num2 = 91.8;		// 0x00000008
int* num1_ptr = &um1;		// 12331
float* num2_ptr = &num2;	// 91.800000

 

 

sizeof 연산자는 피연산자의 자료형을 저장하는 데에 필요한 데이터의 크기를 반환하는 단항 연산자이다.

int size = sizeof(int);

printf("%d", size);	// 4

 


사칙연산이 다양하게 사용되는 식에서는 곱셈 및 나눗셈이 덧셈과 뺄셈보다 먼저 수행되는데 이는 C언어에서도 마찬가지이다.

이런 식으로 사칙연산 외에도 다양한 연산자들이 존재하는 C언어에서는 연산자들의 우선 순위가 정해져 있다.

 

  1. 후위 증가 및 감소 연산자, 함수 호출 연산자( ( ) ), 첨자 연산자 ( [ ] ), 구조체/공용체 접근 연산자 ( ., ->), 복합 리터럴 연산자
  2. 전위 증가 및 감소 연산자, 단항 덧셈/뺄셈 연산자, !, ~, 자료형 캐스팅 연산자, 역참조 연산자, 주소 연산자, sizeof 연산자
  3. *, /, %
  4. +, -
  5. <<, >>
  6. <, <=, >, >=
  7. ==, !=
  8. &
  9. ^
  10. |
  11. &&
  12. ||
  13. ? : (삼향 연산자)
  14. 대입 연산자, 산술 대입 연산자
  15. , (쉼표 연산자)

넘버링 순서대로 우선 순위가 정해져 있다.

같은 연산자가 여러 번 등장할 경우 가장 앞에 있는 연산자가 우선 순위가 높다

괄호를 통해서 우선 순위를 변경해줄 수도 있다.

printf("%d", 3 * 4 + 3 % 2);	// 13
printf("%d", 3 * (4 + 3) % 2);	// 1

 


컴파일

작성한 C 코드를 컴퓨터가 이해하고 실행할 수 있도록 하기 위해서는 컴파일이라는 과정이 필요하다.

컴파일을 마치면 작성한 소스파일과 함께 실행 파일이 생성된다.

 

컴파일러는 gcc, clang 등 다양한 컴파일러가 존재하지만 gcc를 기준으로 컴파일 과정을 공부해보자.

 

컴파일은 크게 4가지 단계로 이루어진다.

전처리, 컴파일, 어셈블, 링크 순서대로 구성된다.


1. Preprocess

.c 확장자를 가진 소스파일을 전처리기를 통하여 .i 확장자를 가진 파일을 만드는 과정이다.

gcc의 경우 전처리기로 cpp를 사용한다.

 

C언어를 사용하면 #include, #define과 같은 전처리기 구문을 사용할 수 밖에 없다.

외부에서 선언된 헤더 파일을 사용하고 코드의 효율성을 높이기 위해 매크로를 정의하고 조건부 컴파일을 하기도 한다.

 

전처리기를 통해서 만들어진 .i 파일을 읽어보면 #include <stdio.h> 같은 구문에서 뽑아온 stdio.h의 내용이 들어가있고, #define을 통해 선언된 함수 및 변수가 치환되어 있는 것을 알 수 있다. 

 

2. Compile

gcc는 cc1을 사용하여 어셈블리 소스 파일을 생성한다.

.s 확장자를 가진 파일을 생성하는데 해당 파일을 읽어보면 작성한 C 코드가 어셈블리어로 변환된 것을 확인할 수 있다.

 

3. Assemble

변환된 저급 언어를 .o확장자를 가진 기계어 파일(오프젝트 파일)을 생성하는 과정이다.

대부분 이진 형태로 저장된다.

gcc는 as를 사용하여 어셈블 과정을 수행한다.

 

경우에 따라 .o 확장자를 가진 목적 파일 말고도 .a 확장자를 가진 라이브러리 파일이 생기기도 한다.

 

4. Link

ld를 활용하여 목적 파일을 결합시켜 최종적인 실행 파일을 만드는 과정이다.

여러 목적 파일을 합치는 것 뿐만 아니라 주소를 조작하고, 외부 라이브러리와 연결하는 등 다양한 작업들을 한다.

실행 파일 형태로 최종적인 파일이 생성된다.

 


2진수, 8진수, 16진수 변환 및 C언어에서의 선언

C언어에는 값을 8진수, 16진수로 출력해주는 자료형이 존재한다.

만약 출력된 값이 8진수인지 16진수인지 명확히 표현할 수 있도록 하고 싶다면 #을 추가하면 된다.

int num = 10;

printf("%o", num);	// 12
printf("%x", num);	// A

printf("%#o", num);	// 012
printf("%#x", num);	// 0xa

8진수와 16진수로 바꿔주는 함수는 존재하지만 2진수로 바꿔주는 함수는 존재하지 않는다.

 

C언어에서 10진수를 2진수로 변환하는 방법은 여러 가지가 있지만 비트 연산을 활용할 수 있다.

int num = 3;
 
 for (int i=7;i>0;i--) {	// 8비트까지만 출력
 	int result = num >> i & 1;	// 비트 시프트 연산자와 비트 AND 연산자로 비교
   	printf("%d", result);
 }

 

C언어에서 정수 변수를 선언할 때는 특별한 설정이 없으면 암묵적으로 10진수로 정수가 선언된다.

16진수와 8진수를 정수 변수로 설정하고 싶다면 각각 앞에 0x와 0을 붙이면 된다.

int num1 = 10;
int num2 = 0xa;
int num3 = 012;

printf("%d %d %d", num1, num2, num3);	// 10 10 10

 

728x90
반응형

댓글