출시·고도화 중
C 안내서 · 2/6
C의 문법은 작고 단순합니다. 이 장에서는 프로그램의 시작점인 main, 변수와 연산자, 함수와 프로토타입, 제어 흐름, 그리고 printf와 scanf로 입출력하는 방법을 살펴봅니다.
C 프로그램은 main 함수에서 시작합니다. 표준이 보장하는 형태는 두 가지입니다.
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("인수 개수: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}명령줄 인수가 필요 없다면 int main(void)로 씁니다. argc는 인수 개수, argv는 각 인수 문자열의 배열이며 argv[0]은 보통 프로그램 이름입니다. main에서 return을 생략하면 0을 돌려준 것으로 처리되지만, 명시적으로 적는 편이 읽기 쉽습니다.
변수는 쓰기 전에 타입과 함께 선언합니다. C99부터는 블록 중간이나 for 문 안에서도 선언할 수 있습니다. 지역 변수는 초기화하지 않으면 값이 정해지지 않으므로 선언할 때 바로 초기값을 주는 습관을 들입니다.
int count = 0;
double ratio = 0.75;
const int max_users = 100; /* 바꿀 수 없는 값 */
int a = 7, b = 2;
int q = a / b; /* 3: 정수 나눗셈은 0 쪽으로 버림 */
int r = a % b; /* 1: 나머지 */
double d = (double)a / b; /* 3.5: 한쪽을 실수로 바꾸면 실수 나눗셈 */
count += 5; /* count = count + 5 */
count++; /* 1 증가 */자주 쓰는 연산자는 다음과 같습니다.
+, -, *, /, %==, !=, <, <=, >, >= (결과는 참이면 1, 거짓이면 0인 int)&&, ||, ! (왼쪽만으로 결과가 정해지면 오른쪽은 평가하지 않습니다)&, |, ^, ~, <<, >>=, +=, -=, *=, /= 등C에서는 0이 거짓이고 0이 아닌 값은 모두 참입니다. <stdbool.h>를 포함하면 bool, true, false를 쓸 수 있습니다(C23에서는 헤더 없이도 키워드입니다). 조건에 =를 잘못 써서 if (x = 0)처럼 대입이 되는 실수가 흔한데, -Wall이 이를 경고해 줍니다.
함수는 반환 타입, 이름, 매개변수 목록으로 정의합니다. 컴파일러는 파일을 위에서 아래로 읽기 때문에, 함수를 정의보다 먼저 호출하려면 프로토타입(선언)을 앞에 두어야 합니다.
#include <stdio.h>
/* 프로토타입: 이런 함수가 있다고 미리 알림 */
int max_of(int a, int b);
void greet(const char *name);
int main(void) {
greet("C");
printf("%d\n", max_of(3, 9));
return 0;
}
int max_of(int a, int b) {
return a > b ? a : b;
}
void greet(const char *name) {
printf("안녕하세요, %s!\n", name);
}C의 인수는 항상 값으로 전달됩니다. 함수 안에서 매개변수를 바꿔도 호출한 쪽의 변수는 바뀌지 않습니다. 호출한 쪽의 값을 바꾸려면 포인터를 넘겨야 하는데, 이는 6장에서 자세히 다룹니다. 매개변수가 없는 함수는 int f(void)처럼 void를 적어야 "인수를 받지 않는다"는 뜻이 정확히 전달됩니다.
if/else, switch, while, do-while, for가 있고, 반복을 빠져나가는 break와 다음 반복으로 넘어가는 continue가 있습니다.
for (int i = 1; i <= 15; i++) {
if (i % 15 == 0) {
printf("FizzBuzz\n");
} else if (i % 3 == 0) {
printf("Fizz\n");
} else if (i % 5 == 0) {
printf("Buzz\n");
} else {
printf("%d\n", i);
}
}
int day = 6;
switch (day) {
case 6:
case 7:
printf("주말\n");
break; /* break가 없으면 다음 case로 이어서 실행됨 */
default:
printf("평일\n");
break;
}
int n = 0;
do {
n++;
} while (n < 3); /* 본문을 최소 한 번 실행 */switch는 정수 값에만 쓸 수 있고, case 끝에 break를 빠뜨리면 다음 case로 넘어가 실행됩니다. 의도한 경우가 아니라면 항상 break를 적습니다.
printf는 형식 문자열의 지정자 자리에 인수를 넣어 출력합니다. 지정자와 인수 타입이 맞지 않으면 정의되지 않은 동작이므로 정확히 맞춰야 합니다.
| 지정자 | 타입 | 예 |
|---|---|---|
%d | int | printf("%d", 42) |
%ld | long | printf("%ld", 42L) |
%u | unsigned int | printf("%u", 7u) |
%zu | size_t | printf("%zu", sizeof(int)) |
%f | double | printf("%.2f", 3.14159) |
%c | 문자 | printf("%c", 'A') |
%s | 문자열 | printf("%s", "text") |
%p | 포인터 | printf("%p", (void *)&x) |
scanf는 입력을 읽어 변수에 저장하므로 변수의 주소(&)를 넘깁니다. 반환값은 성공적으로 읽은 항목 수이므로 반드시 확인합니다. scanf에서 double은 %lf로 읽는다는 점이 printf와 다릅니다.
#include <stdio.h>
int main(void) {
int age;
double height;
printf("나이와 키를 입력하세요: ");
if (scanf("%d %lf", &age, &height) != 2) {
fprintf(stderr, "입력 형식이 올바르지 않습니다.\n");
return 1;
}
printf("나이 %d, 키 %.1f\n", age, height);
return 0;
}문자열 한 줄을 읽을 때는 버퍼 크기를 넘길 수 있는 fgets(buf, sizeof buf, stdin)가 더 안전합니다. 크기 제한 없는 %s는 버퍼를 넘쳐 쓸 수 있으므로 피합니다.
int main(void) 또는 int main(int argc, char *argv[])에서 시작합니다.switch의 case에는 break를 잊지 않습니다.printf 지정자는 인수 타입과 정확히 맞추고, scanf의 반환값은 항상 확인합니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.