Midterm Guide / 중간고사 가이드
1. C 시작하기
C는 1970년대에 시스템 프로그래밍 언어로 개발되었다.
- 1960: ALGOL 60 - International Algorithmic Language
- 1967: BCPL (Basic Combined Programming Language) - Martin Richards
- 1969: B - Ken Thompson
- 1972: C - Dennis Ritchie
- 1978: K&R C - Brian Kernighan, Dennis Ritchie
- 1989: ANSI C - American National Standards Institute
- 1990: ISO C - International Organization for Standardization
- 1999:
C99- ISO C - 2011:
C11- ISO C - 2018:
C17- ISO C - 2022:
C2x- ISO C - 2024:
C23- ISO C
C 프로그래밍 언어의 인기는C의 정신spirit of C 이라고 하는 다음과 같은 언어의 여러 신조에서 기인했올 가능성이 크다.
- 신뢰: 프로그래머를 신뢰해야 한다.
- 해제: 프로그래머가 해야 할 일을 하지 못하도록 막으면 안 된다.
- 간간한: C 언어를 작고 간단하게 유지해야 한다.
- 보존: 연산 수행은 한 가지 방법만 제공해야 한다.
- 빠른: 이식성이 보장되지 않더라도 코드가 빠르게 동작하도록 만들어야 한다.
첫 번째 C 프로그램 개발하기
#include <stdio.h>
#include <stdlib.h>
int main(void) {
puts("Hello, world!\n");
// printf("%s\n", "Hello, world!\n");
return EXIT_SUCCESS;
}
- puts 함수는 stdout에 문자열을 쓰는 간단한 함수입니다.
- printf 함수를 사용해 형식화된 출력formatted output을 인쇄해야 한다.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
printf("%s %d %c %f\n", "Hello, world!", 42, 'a', 3.14);
return EXIT_SUCCESS;
}
C 프로그램 개발하기 및 컴파일러 사용하기
- C 프로그램을 작성하려면 소스 파일이라고 하는 C 언어 텍스트 파일을 만들고 편집해야 합니다. 프로그램이 C로 작성되면 기계가 읽을 수 있는 언어로 번역될 준비가 됩니다.
- 컴파일러는 C 명령문을 기계 명령문으로 변환합니다. 우리가 사용하는 컴파일러는
GCC(GNU Compiler Collection) 라고 하며 C, C++, Objective-C 및 기타 언어에 대한 프런트엔드를 포함합니다. -
컴파일러는 소스 코드와 최종 실행 코드 사이의 중간 단계인 목적 코드를 생성합니다. 링커는 목적 코드를 실행 가능한 실행 파일로 결합합니다.
- 소스 파일 (
*.c) : C언어 문법을 사용해서 작성한 파일 - 목적 파일 (
*.obj) : 컴파일러(번역기)가 번역(컴파일) 한 파일 - 실행 파일 (
*.exe) : 컴퓨터에서 실행할 수 있는 파일
2. 개체와 함수, 형식
메모리와 비트 그리고 바이트
메모리의 최소 저장 단위는 비트입니다.
- 1비트는
0또는1을 저장할 수 있는 공간입니다. - 2비트는 각 비트에
0,1중 한 개를 저장할 수 있으니00,01,10,11입니다. 2진수라고 합니다. - 3비트는
000,001,010,011,100,101,110,111입니다. 10진수로 바꿔 보면0,1,2,3,4,5,6,7까지 입니다.
- 1비트bit는 숫자 2개(
0~1) 중 하나, - 2비트는 숫자 4개(
0~3) 중 하나, - 3비트는 숫자 8개(
0~7) 중 하나…
비트가 8개 모이면 새로운 단위를 사용하며 이것을 바이트byte라고 합니다.
- 부호를 고려하는 1바이트에서 각 비트의 값이
01111111이라면 127입니다 (양수). - 또 1바이트의 각 비트 값이
10000000이라면 -128입니다 (음수).
자료형
| 자료형 | 크기 | 비트의 수 | 범위 |
|---|---|---|---|
char |
1바이트 | 28 | -128 ~ 127 |
unsigned char |
1바이트 | 28 | 0 ~ 255 |
short |
2바이트 | 216 | -32,768 ~ 32,767 |
unsigned short |
2바이트 | 216 | 0 ~ 65,535 |
int |
4바이트 | 232 | -2,147,483,648 ~ 2,147,483,647 |
unsigned int |
4바이트 | 232 | 0 ~ 4,294,967,295 |
long |
4바이트 | 232 | -2,147,483,648 ~ 2,147,483,647 |
unsigned long |
4바이트 | 232 | 0 ~ 4,294,967,295 |
long long |
8바이트 | 264 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
unsigned long long |
8바이트 | 264 | 0 ~ 18,446,744,073,709,551,615 |
개체와 함수, 형식, 그리고 포인터
C의 모든 형식은 개체형(object type)이거나 함수형(function type)이라는 것이다.
- 개체object 는 값을 표현할 수 있는 스토리지storage다.
- 변수variable에는 해당 값이 나타내는 개체의 종류를 알려주도록 형식type을 선언 해야 한다.
- 함수function는 개체가 아니지만, 형식을 갖는다.
함수형function type은 반환형return type뿐만 아니라 함수의 매개변수 개수 및 유형을 갖는다.
범위
- 지역 범위 (local scope) : 함수 내에서 선언된 개체
- 블록 범위 (block scope) : 중괄호로 묶인 블록 내에서 선언된 개체
- 함수 범위 (function scope) : 함수 내에서 선언된 개체
- 파일 범위 (file scope) : 함수 외부에서 선언된 개체
- 전역 범위 (global scope) : 파일 범위에서 선언된 개체
개체 형식
부율 형식 (0과 1만) : _Bool로 선언한 개체는 0과 1만 저장할 수 있다. bool을 사용하면 <stdbool.h>를 포함해야 한다.
#include <stdbool.h>
bool b = true;
문자 형식 : char, signed char, unsigned char
char c = 'a';
signed char sc = 'b';
unsigned char uc = 'c';
숫자 형식 (정수 형식) : short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long
short s = 1;
unsigned short us = 2;
int i = 3;
unsigned int ui = 4;
long l = 5;
unsigned long ul = 6;
long long ll = 7;
unsigned long long ull = 8;
숫자 형식 (부동 소수점 형식) : float, double, long double (실수 형식)
float f = 3.14;
double d = 3.141592;
long double ld = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
숫자 형식 (열기형) : enum으로 선언한 개체는 정수 형식의 값을 저장할 수 있다.
enum { RED, GREEN, BLUE };
void 형식
조금 색다른 데이터 형식이다. 키워드 void는 (그 자체로) “어떤 값도 가질 수 없다”는 것을 의미한다.
함수 형식
함수 형식function type은 파생된 형식derived type이다.
이 경우 형식은 반환 형식return type과 함수 매개변수의 개수와 형식을 따른다.
함수의 반환 형식은 배열 형식이 될 수는 없다.
int func(int, int);
파생된 형식
포인터 형식pointer type
참조된 형식reference type이라고 하는 포인터가 가리키는 함수나 개체 형식에서 파생된 것이다. & 연산자를 사용해 개체나 함수의 주소를 가져올 수 있다.
int i = 42;
int *pi = &i;
배열 형식array type
배열의 크기와 배열 요소의 형식에서 파생된 것이다. 배열array은 연속해서 모두 같은 요소 형식element type으로 할당된 개체의 시퀀스sequence다. 대괄호([ ])를 사용해 배열의 요소를 식별한다.
str이 배열 개체일 때 식 번호는 0부터 시작한다. str[0]은 배열의 첫 번째 요소를 가리킨다. 다차원 배열multi-dimensional array을 선언 할 수도 있다. str[0][0]은 2차원 배열의 첫 번째 요소를 가리킨다.
char str[4] = { 'a', 'b', 'c', 'd' };
char str[6] = "abcde";
char str[2][3] = { "ab", "cd" };
구조체 형식struct type
구조체 멤버의 형식에서 파생된 것이다. 구조체structure type struct에는 순차적으로 할당된 멤버 개체member object가 있다.
구조체는 관련 개체의 집합을 선언하는 데 유용하며 날짜나 고객 또는 인사 기록과 같은 것을 나타내는 데 사용할 수 있다.
구조체를 정의하고 나면 구조체의 멤버를 참조해야 한다. 구조체 개체의 멤버는 구조체 멤버 연산자 (.)를 사용해 참조할 수 있다.
구조체에 대한 포인터를 사용하면 -> 연산자로 구조체 멤버를 참조할 수 있다.
struct date {
int month;
int day;
int year;
};
struct date today;
today.month = 9;
today.day = 25;
today.year = 2021;
공용체 형식union type
공용체 멤버의 형식에서 파생된 것이다.
공용체union type는 멤버 개체가 사용하는 메모리가 겹친다는 점을 제외하고는 구조체와 비슷하다.
공용체는 주로 메모리를 절약하기 위해 사용된다.
- 구조체와 마찬가지로 (.) 연산자를 사용해 공용체 멤버에 접근할 수 있다.
- 공용체에 대한 포인터를 사용하면 -> 연산자로 공용체 멤버를 참조할 수 있다.
union {
int i;
float f;
} u;
u.i = 42;
printf("%d\n", u.i);
u.f = 3.14;
printf("%f\n", u.f);
태그
태그tag는 구조체와 공용체, 열거형에 이름을 부여하는 특별한 메커니즘이다.
태그 자체는 형식의 이름이 아니며 변수를 선언하는 데 사용할 수 없다.
struct date {
int month;
int day;
int year;
} today;
형식 한정자
const: 개체를 수정할 수 없다는 것을 나타낸다.volatile: 개체가 예기치 않게 변경될 수 있음을 나타낸다.restrict: 개체에 대한 포인터가 개체를 참조하는 유일한 포인터임을 나타낸다.
3. 산술 형식
정수
정수 형식은 유한 범위의 정수를 표현한다.
- 부호 있는 정수 형식 (
signed) - 부호 없는 정수 (
unsigned)
모든 정수 형식은 다음과 같은 세 가지 속성을 갖는다.
- 비트 수 : 정수 형식에 저장할 수 있는 비트 수
- 최소 범위 : 정수 형식에 저장할 수 있는 최소 값
- 최대 범위 : 정수 형식에 저장할 수 있는 최대 값.
그리고 정수의 값과 표현 방식에 따라 다음과 같은 두 가지 속성을 갖는다.
- 값 : 개체에 저장된 일반 수학적인 값
- 표현 : 체 값은 개체에 할당된 스토리지의 비트에 있는 값을 특정 인코딩
- 패딩 : 정수 형식의 비트 수가 정확히 정수의 비트 수와 일치하지 않는 경우
- 정밀도 : 정수 형식의 비트 수가 정수의 비트 수와 일치하는 경우
- 너비 : 정수 형식의 비트 수
#include<stdio.h>
int main(void) {
double Pi = 3.141592653589793238;
printf("|%-lf|\n", Pi);
printf("|%lf|\n", Pi); // default width
printf("|%30lf|\n", Pi); // width 30 rt
printf("|l%-30lf|\n", Pi); //width 30 lf
printf("|%030.10f|\n", Pi); // width 30 precision 10 right justify. Filling blank space with '0'.
printf("|%-6.3f|\n", Pi);
printf("|%-0.15f|\n", Pi);
printf("|%-030.15f|\n", Pi);
}
- 부호가 없는 정수 (
unsigned)- 최소 범위 : 0
- 최대 범위 : 최대값은 2비트 수 - 1이다.
- 부호가 있는 정수 (
signed)- 최소 범위 : 최소값은 -2비트 수 - 1이다.
- 최대 범위 : 최대값은 2비트 수 - 1 - 1이다.
<limits.h> 헤더 파일에서 정수 형식의 최소 및 최대 범위를 확인할 수 있다.
#include <limits.h>
#include <stdio.h>
int main(void) {
printf("CHAR_BIT: %d\n", CHAR_BIT);
printf("CHAR_MAX: %d\n", CHAR_MAX);
printf("CHAR_MIN: %d\n", CHAR_MIN);
printf("INT_MAX: %d\n", INT_MAX);
printf("INT_MIN: %d\n", INT_MIN);
printf("LONG_MAX: %ld\n", (long) LONG_MAX);
printf("LONG_MIN: %ld\n", (long) LONG_MIN);
printf("SCHAR_MAX: %d\n", SCHAR_MAX);
printf("SCHAR_MIN: %d\n", SCHAR_MIN);
printf("SHRT_MAX: %d\n", SHRT_MAX);
printf("SHRT_MIN: %d\n", SHRT_MIN);
printf("UCHAR_MAX: %d\n", UCHAR_MAX);
printf("UINT_MAX: %u\n", (unsigned int) UINT_MAX);
printf("ULONG_MAX: %lu\n", (unsigned long) ULONG_MAX);
printf("USHRT_MAX: %d\n", (unsigned short) USHRT_MAX);
return 0;
}
부호가 있는 정수
부호가 있는 정수 형식을 표현하는 것은 부호가 없는 정수를 표현하는 것보다 더 복잡하다.
- 부호와 크기 : 부호 있는 정수 형식은 부호와 크기를 나타내는 데 사용하는 비트를 포함한다.
- 1의 보수 : 부호 있는 정수 형식은 1의 보수를 사용해 음수를 나타낸다.
- 2의 보수 : 부호 있는 정수 형식은 2의 보수를 사용해 음수를 나타낸다.
1의 보수는 2진수의 비트를 반전시키는 것이다. 2의 보수는 비트를 반전시킨 다음 1을 더하는 것이다.
- 2진수:
11001010 - 1의 보수:
00110101 - 2의 보수:
00110110=00110101+ 1
랩 어라운드 (wrap around) vs 오버플로우 (overflow)
랩 어라운드wrap around는 부호 없는 정수 형식의 최대 범위를 초과하는 것이다.
- 부호 없는 정수 형식의 최대 범위를 초과하면 0부터 다시 시작한다.
유명한 랩 어라운드 예제는 “강남스타일 INT_MAX 오버플로, YouTube를 64비트로 강제 전환”이다.
오버플로우overflow는 부호 있는 정수 형식의 최대 범위를 초과하는 것이다.
- 부호 없는 정수 형식의 최대 범위를 초과하면 0부터 다시 시작한다.
- 부호 있는 정수 형식의 최대 범위를 초과하면 최소값부터 다시 시작한다.
The difference between wrap around and overflow in C is that wrap around is a feature of unsigned integer types, while overflow is a feature of signed integer types.
랩 어라운드와 오버플로우의 차이점은 랩 어라운드는 부호 없는 정수 형식의 특징이고 오버플로우는 부호 있는 정수 형식의 특징이다.
#include <stdio.h>
int main(void) {
unsigned char c = 0;
while (c >= 0) {
printf("%d\n", c);
c++;
}
return 0;
}
/* 출력:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... 255 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... 255 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... 255 ...
*/
정수 상수
- 정수 상수integer constant는 정수 형식의 값이다.
- 정수 상수의 형식은 정수 상수의 값과 표현 방식에 따라 결정된다.
- 정수 상수의 표현은 정수 상수의 값이 개체에 할당된 스토리지의 비트에 있는 값을 특정 인코딩하는 것이다.
정수 상수는 다음과 같은 형식을 갖는다.
- 10진수 상수 : 10진수 상수는 0부터 9까지의 숫자로 구성된다.
- 8진수 상수 : 8진수 상수는 0부터 7까지의 숫자로 구성된다.
- 16진수 상수 : 16진수 상수는 0부터 9까지의 숫자와 A부터 F까지의 문자로 구성된다.
#include <stdio.h>
int main(void) {
int i = 42; // 10진수 상수
int j = 0x2a; // 16진수 상수
int k = 052; // 8진수 상수
int b = 0b101010; // 2진수 상수
printf("%d\n", i);
printf("%d\n", j);
printf("%d\n", k);
printf("%d\n", b);
return 0;
}
Converting between decimal, binary, octal and hexadecimal / 10진수, 2진수, 8진수, 16진수 변환
| 10진수 | 2진수 | 8진수 | 16진수 |
|---|---|---|---|
| 0 | 000 | 000 | 0 |
| 1 | 001 | 001 | 1 |
| 2 | 010 | 002 | 2 |
| 3 | 011 | 003 | 3 |
| 4 | 100 | 004 | 4 |
| 5 | 101 | 005 | 5 |
| 6 | 110 | 006 | 6 |
| 7 | 111 | 007 | 7 |
| 8 | 1000 | 010 | 8 |
| 9 | 1001 | 011 | 9 |
| 10 | 1010 | 012 | A |
| 11 | 1011 | 013 | B |
| 12 | 1100 | 014 | C |
| 13 | 1101 | 015 | D |
| 14 | 1110 | 016 | E |
| 15 | 1111 | 017 | F |
| 16 | 10000 | 020 | 10 |
모든 진수 -> 10진수
- 2진수:
11001010-> 10진수:202=1 * 2^7 + 1 * 2^6 + 0 * 2^5 + 0 * 2^4 + 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 0 * 2^0 - 8진수:
052-> 10진수:42=5 * 8^1 + 2 * 8^0 - 16진수:
0x2a-> 10진수:42=2 * 16^1 + 10 * 16^0
2진수 -> 다른 진수
- 2진수:
0b10110100010101101- 8진수:
0o26515(8진수는 3비트씩 끊어서 계산 =0b10_110_100_010_101_101) - 10진수:
11309(2^14 + 2^12 + 2^10 + 2^8 + 2^6 + 2^4 + 2^2 + 2^0) - 16진수:
0x2b0ad(16진수는 4비트씩 끊어서 계산 =0b10_1011_0000_1010_1101)
- 8진수:
부동 소수점
부동 소수점 표현은 과학적 표기법을 사용해 숫자를 밀수base number와 지수exponent로 인코딩하는 기술이다.
- 10진수 123.456 = 1.23456 x 102
- 2진수 0b10100.110 = 1.0100110 x 24
종류
float: 4바이트- 1비트는 부호를 나타낸다.
- 8비트는 지수를 나타낸다.
- 23비트는 밀수를 나타낸다.
double: 8바이트- 1비트는 부호를 나타낸다.
- 11비트는 지수를 나타낸다.
- 52비트는 밀수를 나타낸다.
long double: 10바이트- 1비트는 부호를 나타낸다.
- 15비트는 지수를 나타낸다.
- 64비트는 밀수를 나타낸다.
long double: 16바이트 (IEC 605594배정밀도 (또는 binary128))- 1비트는 부호를 나타낸다.
- 14비트는 지수를 나타낸다.
- 113비트는 밀수를 나타낸다.
부동 소수점 상수
- 부동 소수점 상수floating-point constant는 부동 소수점 형식의 값이다.
#include <stdio.h>
int main(void) {
float f = 3.14;
double d = 3.141592;
long double ld = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
printf("%f\n", f);
printf("%lf\n", d);
printf("%Lf\n", ld);
return 0;
}
산술 변환
- 정수 산술 변환integer promotion은 정수 형식의 피연산자를 다른 정수 형식으로 변환하는 것이다.
- 부동 소수점 산술 변환floating-point promotion은 부동 소수점 형식의 피연산자를 다른 부동 소수점 형식으로 변환하는 것이다.
- 정수-부동 소수점 변환integer-floating conversion은 정수 형식의 피연산자를 부동 소수점 형식으로 변환하는 것이다.
- 부동 소수점-정수 변환floating-integer conversion은 부동 소수점 형식의 피연산자를 정수 형식으로 변환하는 것이다.
정수 변환 순위integer conversion rank
long double(재일 큼)doublefloatunsigned long longlong longunsigned longlongunsigned intintcharsigned charunsigned char_Bool(재일 작음)
4. 식과 연산자
C언어에 있는 연산자
| 연산자 | 설명 | 종류 |
|---|---|---|
| +, -, !, ~, ++, –, (type), * | Unary (단항) | Unary (단항) |
| +, -, *, /, % | Arithmetic (곱셈) | Binary (이항) |
| <, <=, >, >=, ==, != | Relational (관계) | Binary (이항) |
| &&, || | Logical (논리) | Binary (이항) |
| &, |, ^, «, » | Bitwise (비트) | Binary (이항) |
| =, +=, -=, *=, /=, %=, &=, |=, ^=, «=, »= | Assignment (할당) | Binary (이항) |
| ?: | Conditional (조건) | Ternary (삼항) |
4.1. 단순 할당
- 식expression은 피연산자operand와 연산자operator로 구성된다.
- 단순 할당simple assignment은 할당 연산자assignment operator를 사용해 개체에 값을 할당하는 것이다.
- 정의declaration하고
초기화initialization하는 선언이다.
- r-value와 l-value의 개념을 알아야 한다.
- r-value는 값을 가지는 것이고 l-value는 주소를 가지는 것이다.
- r-value는 l-value가 될 수 없다.
- l-value는 r-value가 될 수 있다.
- l-value는 메모리에 저장된다.
- r-value는 메모리에 저장되지 않는다.
- l-value는 주소를 가지고 있기 때문에 r-value를 가질 수 있다.
- 잘라내기truncation는 부동 소수점 형식의 값을 정수 형식으로 변환하는 것이다.
증가 및 감소 연산자
- 증가 연산자increment operator는 피연산자의 값을 1만큼 증가시킨다.
- 전위 증가 연산자pre-increment operator (
++i) - 후위 증가 연산자post-increment operator (
i++)
- 전위 증가 연산자pre-increment operator (
- 감소 연산자decrement operator는 피연산자의 값을 1만큼 감소시킨다.
- 전위 감소 연산자pre-decrement operator (
--i) - 후위 감소 연산자post-decrement operator (
i--)
- 전위 감소 연산자pre-decrement operator (
전위 증가 연산자와 후위 증가 연산자의 차이점은 전위 증가 연산자는 증가시킨 값을 반환하고 후위 증가 연산자는 증가시키기 전 값을 반환한다는 것이다.
#include <stdio.h>
int main(void) {
int i = 0;
printf("%d\n", ++i); // 1
printf("%d\n", i++); // 1
printf("%d\n", i); // 2
return 0;
}
평가 순서
비순차적 평가와 규정되지 않은 순차적 평가
- 이 식의 비순차적 평가unsequenced evaluation는 어떤 순서로든 수행할 수 있다.
- 이렇게 하면 컴파일러는 레지스터register에서 연산과 캐시 값cache value을 재정렬해reordering 전체 실행 속도를 높일 수 있다.
시퀀스 포인트
- 시퀀스 포인트sequence point는 모든 파생 작업이 끝나는 분기점juncture 이다.
- 시퀀스 포인트는 하나의 전체 식full expression (다른 식이나 선언의 일부가 아닌 식)과 평가할 디움 전체 식 간에 발생한다. 시퀀스 포인트는 호출된 함수로 들어가거나 빠져나올 때도 발생한다.
sizeof 연산자
- sizeof 연산자는 개체 또는 형식의 크기를 바이트 단위로 반환한다.
#include <stdio.h>
int main(void) {
int i = 42;
printf("%zu\n", sizeof(i)); // 4 (바이트)
return 0;
}
산술 연산자
- 단항 연산자
+와- - 논리 부정 연산자
! - 곱하기 연산자
\*/% - 더하기 연산자
+-
#include <stdio.h>
int main(void) {
int i = 42;
int j = 0;
printf("%d\n", +i); // 42
printf("%d\n", -i); // -42
printf("%d\n", !j); // 1
printf("%d\n", i * j); // 0
printf("%d\n", i / j); // 0
printf("%d\n", i % j); // 0
printf("%d\n", i + j); // 42
printf("%d\n", i - j); // 42
return 0;
}
비트 연산자
(기말고사 때 다시 공부하기)
- 보수 연산자
~ - 시프트 연산자
<<>> - 비트 AND 연산자
& - 비트 XOR 연산자
^(배타적 OR) - 비트 OR 연산자
|(포과적 OR)
#include <stdio.h>
int main(void) {
int i = 42;
int j = 0;
printf("%d\n", ~i); // -43
printf("%d\n", i << 1); // 84
printf("%d\n", i >> 1); // 21
printf("%d\n", i & j); // 0
printf("%d\n", i ^ j); // 42
printf("%d\n", i | j); // 42
return 0;
}
논리 연산자
- 논리 AND 연산자
&& - 논리 OR 연산자
||
#include <stdio.h>
int main(void) {
int i = 42;
int j = 0;
printf("%d\n", i && j); // 0
printf("%d\n", i || j); // 1
return 0;
}
형 변환 연산자
- 형 변환 연산자type conversion operator는 개체의 형식을 다른 형식으로 변환하는 것이다.
#include <stdio.h>
int main(void) {
int i = 42;
printf("%f\n", (float) i); // 42.000000
return 0;
}
조건부 연산자
- 조건부 연산자conditional operator는 조건식을 평가한 다음 조건식의 결과에 따라 두 개의 식 중 하나를 평가하는 것이다. (
_?_:_)
#include <stdio.h>
int main(void) {
int i = 42;
int j = 0;
printf("%d\n", i > j ? i : j); // 42
return 0;
}
_Alignof 연산자
Useful for testing (테스트에 유용하다)
- _Alignof 연산자는 개체의 정렬 요구 사항을 바이트 단위로 반환한다.
#include <stdio.h>
int main(void) {
int i = 42;
printf("%zu\n", _Alignof(int)); // 4
return 0;
}
관계형 연산자
- 관계형 연산자relational operator는 두 개의 피연산자를 비교하는 것이다.
- 적다
< - 적거나 같다
<= - 크다
> - 크거나 같다
>= - 같다
== - 같지 않다
!= - 관계형 연산자의 결과는 참 또는 거짓이다.
#include <stdio.h>
int main(void) {
int i = 42;
int j = 0;
printf("%d\n", i < j); // 0
printf("%d\n", i <= j); // 0
printf("%d\n", i > j); // 1
printf("%d\n", i >= j); // 1
printf("%d\n", i == j); // 0
printf("%d\n", i != j); // 1
return 0;
}
복합 할당 연산자
- 복합 할당 연산자compound assignment operator는 할당 연산자와 다른 연산자를 결합한 것이다.
#include <stdio.h>
int main(void) {
int i = 42;
printf("%d\n", i += 1); // 43
printf("%d\n", i -= 1); // 42
printf("%d\n", i *= 2); // 84
printf("%d\n", i /= 2); // 42
printf("%d\n", i %= 2); // 0
printf("%d\n", i &= 1); // 0
printf("%d\n", i |= 1); // 1
printf("%d\n", i ^= 1); // 0
printf("%d\n", i <<= 1); // 0
printf("%d\n", i >>= 1); // 0
return 0;
}
쉼표 연산자
- 쉼표 연산자comma operator는 두 개의 식을 평가한 다음 두 번째 식의 결과를 반환하는 것이다.
#include <stdio.h>
int main(void) {
int i = 42, j = 0;
printf("%d\n", (i++, i)); // 43
printf("%d\n", (j++, i)); // 43
return 0;
}
포인터 산술
- 포인터 산술pointer arithmetic은 포인터에 정수를 더하거나 뺀다.
#include <stdio.h>
int main(void) {
int i = 42;
int *p = &i;
printf("%p\n", p); // 0x7ffeeb0b3a3c
printf("%p\n", p + 1); // 0x7ffeeb0b3a40
printf("%p\n", p - 1); // 0x7ffeeb0b3a38
return 0;
}
5. 흐름 제어
- 식 문(expression statements),
- 복합 문(compound statements)
- 선택 문(selection statements)
- 반복 문(iteration statements),
- 점프 문(jump statements)
식문
- 식 문expression statement은 식을 평가한 다음 결과를 폐기하는 것이다.
#include <stdio.h>
int main(void) {
int i = 42;
printf("%d\n", i); // 42
i + 1; // 폐기
return 0;
}
복합문
- 복합 문compound statement은 여러 문을 하나의 문으로 결합하는 것이다.
#include <stdio.h>
int main(void) {
int i = 42;
{
int i = 0;
printf("%d\n", i); // 0
}
printf("%d\n", i); // 42
return 0;
}
선택문
if 문
- if 문if statement은 조건식을 평가한 다음 조건식의 결과에 따라 두 개의 문 중 하나를 실행하는 것이다.
#include <stdio.h>
int main(void) {
int i = 42;
if (i > 0) {
printf("%d\n", i); // 42
} else {
printf("%d\n", -i); // 42
}
return 0;
}
if else if 문
- if else if 문if else if statement은 여러 조건식을 평가한 다음 조건식의 결과에 따라 여러 문 중 하나를 실행하는 것이다.
#include <stdio.h>
int main(void) {
int i = 42;
if (i > 0) {
printf("%d\n", i); // 42
} else if (i < 0) {
printf("%d\n", -i); // 42
} else {
printf("%d\n", i); // 42
}
return 0;
}
switch 문
- switch 문switch statement은 조건식을 평가한 다음 조건식의 결과에 따라 여러 문 중 하나를 실행하는 것이다.
#include <stdio.h>
int main(void) {
int i = 42;
switch (i) {
case 0:
printf("%d\n", i); // 42
break;
case 1:
printf("%d\n", i); // 42
break;
default:
printf("%d\n", i); // 42
break;
}
return 0;
}
반복문
while 문
- while 문while statement은 조건식을 평가한 다음 조건식의 결과가 참인 동안 문을 반복해서 실행하는 것이다.
#include <stdio.h>
int main(void) {
int i = 42;
while (i > 0) {
printf("%d\n", i); // 42 41 40 ... 1
i--;
}
return 0;
}
do…while 문
- do…while 문do…while statement은 문을 실행한 다음 조건식을 평가한 다음 조건식의 결과가 참인 동안 문을 반복해서 실행하는 것이다.
#include <stdio.h>
int main(void) {
int i = 0;
do {
printf("%d\n", i); // 0
i--;
} while (i > 0);
return 0;
}
for 문
- for 문for statement은 초기식을 평가한 다음 조건식을 평가한 다음 조건식의 결과가 참인 동안 문을 반복해서 실행하는 것이다.
#include <stdio.h>
int main(void) {
for (int i = 0; i < 10; i++) {
printf("%d\n", i); // 0 1 2 ... 9
}
return 0;
}
점프문
goto 문
- goto 문goto statement은 레이블로 표시된 문으로 이동하는 것이다.
#include <stdio.h>
int main(void) {
int i = 0;
goto label;
i++;
label:
printf("%d\n", i); // 0
return 0;
}
continue 문
- continue 문continue statement은 반복문의 나머지 부분을 건너뛰는 것이다.
#include <stdio.h>
int main(void) {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) { // 짝수일 때
continue;
}
printf("%d\n", i); // 1 3 5 7 9
}
return 0;
}
break 문
- break 문break statement은 반복문을 종료하는 것이다.
#include <stdio.h>
int main(void) {
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
printf("%d\n", i); // 0 1 2 3 4
}
return 0;
}
return 문
- return 문return statement은 함수를 종료하는 것이다.
#include <stdio.h>
float divide(float a, float b) {
if (b == 0) {
return 0;
}
return a / b;
}
int main(void) {
printf("%f\n", divide(42, 0)); // 0.000000
printf("%f\n", divide(42, 1)); // 42.000000
return 0;
}