우당탕탕 개발일지
[C언어] 기본 입출력 및 비트 연산자 본문
Linux man 사용법
$ man [options] [section] command
[SPACE] : 한 페이지 밑으로 내려간다
[ENTER] : 한 줄 밑으로 내려간다.
[b] : 전 페이지로 올라간다.
[q] : man 명령을 종료한다.
1. printf(), sprintf(), snprintf()
✅ printf 형식
int printf(const char *format, …)
✅ printf 출력 형식
%d %i %u %X %x %o %p %s %c %C %f %e %E %g %G
| 출력 형식 | 인수의 형을 지정 |
| %d | int값을 10진수로 출력 |
| %c | 문자열 하나 출력 |
| %p | 포인터값을 16진수로 출력 |
| %x | int값을 부호없는 16진수로 출력, 10~15은 'a'~'f'로 표시 |
| %u | int 값을 부호없는 10진수로 출력 |
| %o | int 값을 부호없는 8진수로 출력 |
| %s | 문자열 출력, ‘\0’인 문자를 만날때까지 출력 |
| %ld | 부호있는 long 형 정수 출력 |
| %lu | 부호없는 long 형 정수 출력 |
| %e | double 값을 지수로 출력 |
✅ sprintf 형식
#include <stdio.h>
int sprintf(char *buffer, const char *format, argument-list);
- 콘솔에 출력하는 대신 일반 문자열에 저장하는 함수
- 첫 인수는 저장할 문자 버퍼, 두번째는 포맷 문자열, 세번째는 형식에 대응되는 데이터들이다.
✅ snprint 형식
#include <stdio.h>
int sprintf(char *buffer, const char *format, argument-list);
- snprintf 함수의 취약점을 보완하고자 나온 함수로 지정한 크기만큼만 데이터를 버퍼에 저장하고 초과되는 데이터는 무시하는 함수
- 첫 인수는 문자열을 저장할 배열, 두번째는 배열 크기, 세번째는 포맷 문자열, 네번째는 형식에 대응되는 데이터들이다.
2. scanf(), sscanf()
✅ scanf 형식
int scanf(const char *format, ...)
- 사용자의 키보드 입력에서 받은 데이터를 포맷 스트링대로 분리해서 각 변수에 저장해주는 함수
- %s는 스페이스를 끝으로 인식해 그 다음 0을 넣어 문자열을 종결시킨다.
✅ sscanf 형식
#include <stdio.h>
int sscanf(const char *buffer, const char *format, argument-list);
- scanf( )와 동일하지만 입력 대상이 표준 입력이 아닌 매개변수로 전달되는 문자열 버퍼라는 차이가 있다.
✅ scanf, sscanf 특징
- %c를 제외하고 모든 데이터는 한단어씩 읽는다. 기본적으로 공백 문자(스페이스, 탭, 개행 문자 등)도 포함하여 처리한다.
- %d, %f, %s 와 같은 다른 형식 지정자들은 공백 문자를 무시한다.
- 배열의 크기보다 큰 문자열이 들어오면 버퍼오버플로우가 일어난다.
- 서식자에 맞지않는 데이터가 들어오면 오류가 발생한다.
- scanf함수의 경우 stdin(입력스트림)의 형식으로 입력을 받는다.
| 성공 | 실패 | |
| scanf | 읽어들인 것의 개수 | 0(아무것도 못읽었을 경우) |
| sscanf | 읽어들인 것의 개수 | 0(아무것도 못읽었을 경우) |
3. getchar(), gets()
✅ getchar 형식
#include <stdio.h>
int getchar(void);
- 표준 입력으로 들어온 문자열을 한 문자씩 읽어오는 함수
- 반환값이 int인 이유 → EOF(-1)를 처리하기 위함.
✅ gets 형식
#include <stdio.h>
int *gets(char *s);
- 표준 입력으로 들어온 문자열을 char*, char[ ] 타입으로 저장해주는 함수
- 문자열이라고 감지하는 기준은 개행(\n)이다. 즉, 표준 입력으로 들어온 문자열을 개행한 부분 앞까지 잘라서 char* 타입의 문자열로 저장해주고, 자동으로 문자열 맨 끝에 ‘\n’을 넣어서 문자열을 완성해준다.
✅ gets 사용 예제
#define BUFFER 256
int main(void)
{
char string[BUFFER];
char *result;
result = gets(string);
printf("%s\n", result);
}
✅ getchar, gets특징
- gets 함수는 자동으로 끝에 (\0)을 넣어준다.
- gets는 버퍼 오버플로우가 날 수 있다.
| 성공 | 실패 | |
| getchar | 읽어들인 문자 | EOF(-1) |
| gets | 읽어들인 문자열 | NULL 포인터 |
🔷 사용자가 개행 문자를 입력할 때까지 입력받는 my_getline() 함수
함수 프로토타입: int my_getline(char *buf, int buf_len)
- getline 함수는 터미널에서 사용자가 개행문자를 입력할 때까지 입력한 문자열을 buf에 저장하는 함수이다.
- 사용자가 입력한 문자열이 buf의 길이보다 작을 때는 buf에 사용자가 입력한 문자열을 저장하고 개행문자를 널문자로 치환한다.
- 사용자가 입력한 문자열이 buf의 길이보다 큰 경우에는 buf_len-1 만큼만 buf에 저장하고 마지막에 널문자를 저장하고 이후 사용자가 개행문자를 입력할 때까지 입력한 문자열은 저장하지 않고 버린다.
- buf는 입력받은 문자열을 저장할 인자이고 buf_len은 buf의 길이이다.
- 리턴값은 실패인 경우에 -1, 성공한 경우에는 사용자가 입력한 문자열의 길이를 리턴한다.
#include <stdio.h>
#include <string.h>
int my_getline(char *buf, int buf_len) {
char ch;
int len = 0;
if(buf == NULL) {
puts("buf is NULL");
return -1;
}
if(buf_len <= len) {
puts("buf_len is too small");
return -1;
}
while(1) {
ch = getchar();
if(ch == EOF) {
return -1;
}
if(ch == '\n') {
buf[len++] = '\0';
break;
}
if(len >= buf_len - 1) {
buf[buf_len - 1] = '\0';
break;
}
buf[len++] = ch;
}
return len;
}
4. putchar(), puts()
✅ putchar 형식
#include <stdio.h>
int putchar(int c);
- char*, char[ ] 타입을 표준 출력(stdout)으로 보내는 함수
✅ puts 형식
#include <stdio.h>
int puts(const char *s);
- putchar 함수는 문자 한개를 출력할 때 유용한 함수
✅ putchar, puts 특징
- puts 함수는 자동으로 마지막에 개행 문자를 넣어준다.
- putchar 함수는 한 문자만 출력하며, 개행 문자를 자동으로 추가하지 않는다.
| 성공 | 실패 | |
| puts | 음이아닌값 | EOF(-1) |
| putchar | 출력된문자 | EOF(-1) |
✅ EOF
EOF = End Of File 이며, 파일의 끝을 표현하기 위해 정의해 놓은 상수이다.
EOF를 반환하는 경우
- 함수 호출의 실패
- 윈도우에서 ctrl + z, 리눅스에서 ctrl + d를 입력 했을 경우
5. 비트 연산자
| 연산자 | 연산자의 기능 |
| & | 비트단위로 AND 연산을 한다. |
| | | 비트단위로 OR 연산을 한다. |
| ^ | 비트단위로 XOR 연산을 한다. |
| ~ | 단항 연산자로서 피연자의 모든 비트를 반전시킨다. |
| << | 피연산자의 비트 열을 왼쪽으로 이동시킨다. |
| >> | 피연산자의 비트 열을 오른쪽으로 이동시킨다. |
NOT(~) 연산자
비트를 0에서 1로, 1에서 0으로 발전시키는 NOT 연산을 하며, 보수연산이라고도 불린다.
| 연산 | 결과 |
| ~ 0 | 1 |
| ~ 1 | 0 |
AND(&) 연산자
두 개의 비트가 모두 1일때 1을 반환하는 AND 연산을 한다.
| 연산 | 결과 |
| 0 & 0 | 0 |
| 0 & 1 | 0 |
| 1 & 0 | 0 |
| 1 & 1 | 1 |
OR(|) 연산자
두 개의 비트 중 하나라도 1이면 1을 반환하는 OR 연산을 한다.
| 연산 | 결과 |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
XOR(^) 연산자
두 개의 비트가 서로 다른 경우에 1을 반환하는 XOR 연산을 한다.
| 연산 | 결과 |
| 0 ^ 0 | 0 |
| 0 ^ 1 | 1 |
| 1 ^ 0 | 1 |
| 1 ^ 1 | 0 |
Shift(<<, >>) 연산자
<< 연산자는 비트를 왼쪽으로, >> 연산자는 비트를 오른쪽으로 이동하는 shift 연산이다.
🔷 수강 신청 정보를 입력받아 관리하는 수강 신청 프로그램 (비트연산 응용)
- 수강 신청 정보는 최대로 5개까지 저장해야 하며, 5개 초과되는 정보를 입력한 경우에는 에러를 출력한다.
- 입력된 수강 신청 정보는 메모리에 보관하고 프로그램 재기동시에 리셋된다.
- 프로그램은 수강 신청 입력, 수강 신청 출력, 종료 3개 메뉴를 출력하고 사용자가 종료를 선택하기 전까지 반복해서 수강 신청 입력/출력/종료를 반복한다.
- 수강 신청 정보 입력은 my_getline() 함수를 사용한다.
🔹 입력
1. 사용자가 메뉴에 해당하는 번호를 입력한다.
- 1은 수강 신청 정보 입력, 2는 수강 신청 정보 출력, 3은 종료
- 메뉴에 없는 번호를 입력한 경우, 다시 메뉴를 출력한다.
2. 수강 신청 정보 입력을 선택하면 다음과 같이 출력하고 ID와 수강 신청 과목 정보를 입력받는다.
ID:
수강 신청 과목:
- ID는 정수로 1 <= ID <= 100 의 범위 값을 가진다. 범위에 벗어나는 값을 입력한 경우 에러 문구 출력하고 메뉴를 출력한다.
- 수강 신청 과목은 컴퓨터개론, 이산수학, C언어, JAVA초급, 리눅스구조, 자료구조, 컴파일러, 네트워크개론이다.
- 수강 신청 과목은 컴퓨터개론, 이산수학, C언어, JAVA초급, 리눅스구조, 자료구조, 컴파일러, 네트워크개론으로 각 과목을 신청할지 말지를 O, X로 입력받는다(각 과목당 한줄). O은 신청, X는 미신청
컴퓨터개론: O
이산수학: X
C언어: O
JAVA초급: X
리눅스구조:O
자료구조: X
컴파일러: O
네트워크개론:O - 수강 신청 과목 정보 입력시 O 또는 X 이외의 문자를 입력하면 에러를 출력하고 메뉴를 출력한다.
3. 수강 신청 출력을 선택하면, 저장된 모든 수강 신청 정보를 출력한다.
4. 종료를 선택하면 프로그램을 종료한다.
#include <stdio.h>
#include <stdlib.h>
#include "my_getline.h"
#define MIN_ID 1
#define MAX_ID 100
#define MAX_SUBJECTS 8
#define MAX_SUBJECTS_NUM 5
#define MAX_STORE 100
typedef struct _subjects_info_s {
int id;
unsigned char subjects;
} subjects_info_t;
typedef struct _subjects_data_s {
subjects_info_t subjects_info[MAX_STORE];
size_t subjects_info_size;
} subjects_data_t;
const char *subjects[] = {
"컴퓨터개론", "이산수학", "C언어", "JAVA초급",
"리눅스구조", "자료구조", "컴파일러", "네트워크개론"
};
int subjects_input(int n, subjects_info_t *subjects_data) {
if (subjects_data == NULL) {
printf("subjects_input : subjects_data is null\n");
return -1;
}
if (subjects_data->subjects_info_size >= MAX_STORE) {
printf("subjects_input : subjects_data is full\n");
return -1;
}
int i, id;
char buf[4];
printf("ID: ");
if(my_getline(buf, 4) == -1) return -1;
id = atoi(buf);
if(id < MIN_ID || id > MAX_ID) {
printf("wrong id input\n");
return -1;
}
subjects_data[n].id = id;
subjects_data[n].subjects = 0;
for(i = 0; i < MAX_SUBJECTS; i++) {
printf("%s: ", subjects[i]);
if(my_getline(buf, 2) == -1) {
return -1;
}
// O -> 원소 추가
if(buf[0] == 'O'){
subjects_data[n].subjects |= (1 << i);
} else if(buf[0] != 'X'){
printf("O 또는 X 이외의 문자");
return -1;
}
}
subjects_data->subjects_info_size++;
return 0;
}
void subjects_output(int n, subjects_info_t *subjects_data) {
if(n <= 0) {
printf("신청 내역이 없습니다.\n");
return;
}
int i, j;
for(i = 0; i < n; i++) {
printf("ID: %d // 수강신청 과목: ", subjects_data[i].id);
int first_flag = 1;
for(j = 0; j < MAX_SUBJECTS; j++) {
if((subjects_data[i].subjects & (1 << j)) != 0) {
if(!first_flag) printf(", ");
printf("%s", subjects[j]);
first_flag = 0;
}
}
printf("\n");
}
}
int main(void) {
int num = 0;
char buf[2];
subjects_data_t subjects_data;
memset(&subjects_data, 0x00, sizeof(subjects_data));
while(1) {
printf("--------------------------------------------------------- \n");
printf("1. 수강 신청 정보 입력\n2. 수강 신청 정보 출력\n3. 종료\n");
printf("select num: ");
if (my_getline(buf, 2) < 0) {
printf("get line fail\n");
continue;
}
int ans = 0;
switch(buf[0]) {
case '1':
if (subjects_input(&subjects_data) < 0) {
printf("subject information input error\n");
}
break;
case '2':
subjects_output(num, subjects_data);
break;
case '3':
exit(0);
default:
break;
}
}
return 0;
}728x90
'Server > Linux, C' 카테고리의 다른 글
| [C언어] Thread (4) | 2025.06.25 |
|---|---|
| [C언어] Makefile / GDB (0) | 2025.05.25 |
| [C언어] 전처리기 (0) | 2025.05.25 |
| [Linux] 리눅스 파일과 파일 시스템 (1) | 2025.05.11 |
| [C언어] 문자열 (feat. strcpy, strtok_r 활용 예제) (0) | 2025.05.11 |