Oh-My-C-Lang - Section 2, 3 - 01
42 Starter 스터디 모임에서 진행하는 C 언어 A to Z 스터디에 내용을 정리해서 포스팅을 진행합니다.
해당 스터디에서는 42 Piscine 과정에서 채우지못한 부족한 C언어 역량을 키우는 것을 목적으로 합니다.
1. 전처리
#include
는 전처리기 지시문 중 하나이다.
#include
은 포함할 헤더 파일을 열어서 그 내용을 복사한 뒤, 호출 파일에 모두 붙여넣은후 다시 컴파일을 진행하는 방식으로 동작한다.
2. main 함수
main
함수를 int형으로 하고 반환값을 통해 프로그램에 문제가 있는 지 없는 지를 나타낼 수 있습니다.
일반적으로 return (0);
은 프로그램에 문제가 없었다는 뜻을 의미합니다.
만약, 리턴 값이 0이 아닌 양수이면 특정 문제가 발생했다는 규약을 나타냅니다. 터미널 환경에서 프로그램을 실행한 후 echo $?
를 실행하면 프로그램의 반환값을 확인할 수 있습니다.
3. limits.h
limits.h
헤더를 통해 데이터형의 비트 수, 최소값, 최대값을 확인 또는 사용이 가능하다.
*추가정보: gcc는 OS환경에 따라 다르게 컴파일이 된다.
4. 연산자
sizeof()
는 컴파일 중에 평가가 이루어진다. 즉, 실행중이 아닌 컴파일 도중에 크기를 찾아 맵핑이 된다. 이렇기 때문에 컴파일할 때 모르는 크기는 찾을 수 없다.
sizeof()
가 반환하는 값은 부호없는 정수형의 상수로 size_t형이다.
size_t
는 부호없는 정수형이며 실제 데이터형을 나타낸 것은 아니다.
여기서 t의 의미는 typedef
된 것을 의미한다. 또한, c99 표준에서는 최소 16비트(최소 unsigned short
)이상이면 문제가 생기지 않는다.
size_t
의 용도는 어떤 것의 크기를 나타내기 위해 사용된다. 예를 들어 반복문이나 배열에 접근할 때 사용되거나 반복문의 카운터 변수에 음수가 필요 없을때가 있다.
size_t
는 부호없는 정수를 의미하는데 모든 비트가 다 찬 상태인 1111 1111
과 같은 상태라면 -1이 된다.
*추가정보1: 조건 문에 해당하는 값이 참이 아니라는 것은 해당 조건문의 값의 비트가 모두 0이라는 것을 의미한다.
*추가정보2: switch case
문을 사용할 때, 케이스문에 break;
가 없을 경우 /* intention fallthrough */
라는 주석을 달아놓아서 통상적으로 문제없는 case문 임을 알린다.
5. 구조적 언어
C 언어는 구조적 언어이므로 변수의 선언을 블록 맨위에 해주어야 한다. 또한 블록 안에서 선언된 변수는 그 블록 밖으로 나갔을 때 메모리(stack-code영역)에서 사라진다.
/* Bad case */
int sum;
sum = 0;
for (size_t i = 0; i < 10; ++i)
{
sum += i;
}
/* Good case */
int sum;
size_t i;
sum = 0;
for (i = 0; i < 10; ++i)
{
sum += i;
}
*추가정보: 조건이 들어가는 자리에 0 값과 비교할 때는 if (a-- != 0)
과 같이 명시적으로 적어주는 것이 좋은 습관이다.
6. 잘못된 증감 연산자
하나의 Statement에서 ++i
또는 i++
, i에 대한 할당을 두 번이상 사용하게되면 변수의 할당 순서가 결과가 정의 되어 있지 않은 순서로 동작하므로 어떤 결과가 되는 지 알 수 없고 예상과 다른 결과가 나올 수 있다. 따라서, 이러한 표현을 절대 사용하면 안된다.
/* Bad usage */
add(++i, ++i);
num = ++num + num++;
또한, 할당문에서 여러 함수의 반환 값으로 연산을 진행 되면 어떤 함수가 먼저 실행되는 지 알수 없다.
/* Example */
/* add(), subtract(), divide() 함수는 생략 */
int main(void)
{
int num1;
int num2;
int result;
num1 = 10;
num2 = 20;
/* add, subract, divide의 호출 순서를 알 수 없다. */
result = add(num1, num2) + subtract(num1, num2) * divide(num1, num2);
printf("result: %d\n", result);
return (0);
}
7. 시퀀스 포인트
시퀀스 포인트란 C 언어에서 모든 식의 계산(평가)이 완료되는 지점을 의미한다.
/* 시퀀스 포인트 목록 */
&&
||
;
return
()
?
if ( )
switch ( )
while ( )
do while ( )
for ( ; ; )
시퀀스 포인트를 생각하고 조건문을 생각하면 조건의 순서 판단이 가능하다. 예를 들어 &&
와 ||
를 사용하여 조건문을 꾸릴때, 어느 것이 우선적으로 되는지 논리적으로 판단할 수 있다.
다음 예시의 값을 판단해보자.
/* Example */
int i = 0;
int j = 0;
int k = 0;
if (++i || ++j && ++k)
{
printf("true!\n");
}
printf("%d, %d, %d\n", i, j, k);
시퀀스 포인트 단위로 조건문을 판별해보면 된다.
전위 증감연산자로 인해 i
는 1이 된다.
그 이후 시퀀스 포인트인 ||
가 나오므로 바로 true가 되어서 j와 k의 증감은 이루어 지지 않는다.
만약, i
의 초기값이 -1이라면 어떻게 될 지 한번 더 생각해보자.
전위 증감연산자로 인해 i
는 0이 된다.
그 이후 시퀀스 포인트인 ||
가 나오는데, ||
는 or을 의미하므로 앞에 값이 False
이면 뒤에 값을 보게된다. 따라서 j가 1이 된다.
그 이후에 시퀀스 포인트인 &&가 등장하게 된다. &&는 뒤에 조건도 봐야하므로, k의 값을 보게되는데 k 또한 전위 증감연산자로 인해 1이 되고 전체 조건이 True
가 된다.
*추가정보: 파일 범위라는 것이 존재하며 이것은 파일 범위에 있는 변수의 메모리 위치를 말한다. 즉 하나의 파일 변수가 존재하는 영역을 나타내는 메모리 영역을 의미한다.