01
// 수다스럽지만 정보를 주는 프로그램
#include <stdio.h>
#include <string.h> // strlen() 프로토타입을 사용하기 위해 선언
#define DENSITY 62.4 // 신체밀도(입방피트당 파운드 수)
int main(void)
{
float weight, volume;
int size, letters;
char name[40]; // name은 영문자로 40개를 넣을 수 있는 배열이다.
printf("하이! 이름이 뭐예요?\n");
scanf("%s", name);
printf("%s씨, 몸무게는 몇 파운드나 나가요?\n", name);
scanf("%f", &weight);
size = sizeof name;
letters = strlen(name);
volume = weight / DENSITY;
printf("음, %s,씨의 몸은 %2.2f입방피트를차지합니다.\n",
name, volume);
printf("그리고 이름이 %d글자니까,\n", letters);
printf("저장하려면 %d Byte가 필요합니다.\n", size);
return 0;
}
scanf() ①
scanf("%s", name);
scanf("%f", &weight);
- 문자열의 입출력을 다루기 위해 %s 변환 지정을 사용한다.
- weight와는 달리 name은, scanf() 함수에서 접두사 &를 사용하지 않는다.
- &weight와 name 모두 주소값을 가진다.
strlen(문자배열)
letters = strlen(name);
- strlen() 함수에 문자열이 들어있는 배열을 넣으면 배열 안에 들어있는 문자열의 길이가 반환된다.
- 순수하게 문자열의 길이만 구하며 NULL 부분은 포함하지 않는다.
- ★ 배열의 크기는 문자열의 길이와 상관이 없으며, 배열의 크기가 1억이든 1이든 문자열의 길이만 구한다.
02
/* 몇 가지 유형의 문자열들을 사용한다 */
#include <stdio.h>
#define PRAISE "넌 특별한 존재야."
int main(void)
{
char name[40];
printf("너 이름이 뭐니?\n");
scanf("%s", name);
printf("반가워, %s야. %s\n", name, PRAISE);
return 0;
}
배열 name
scanf("%s", name); → 입력 : jeongˇhee / 출력 : jeong
- 공백이 포함된 문자열을 문자열 배열에 저장할 때, 공백 앞까지만 저장된다!!!
- 공백을 포함하고 싶은 경우에는 어떻게 해야 하는지 찾아봤다.
① [^\n]을 추가해주면 공백포함 문자열을 저장할 수 있다. 예시) scanf("%[^\n]s", name)
② 공백을 포함시켜 저장할 수 있는 다른 방법은 gets() 함수를 사용하는 것이다.
gets() 함수는 기본적으로 공백을 포함해서 입력 받는 함수이다.
03
/* 사용하는 시스템이 %zd를 인식하지 못한다면 %u 또는 %lu를 사용한다. */
#include <stdio.h>
#include <string.h>
#define PRAISE "넌 특별한 존재야."
int main(void)
{
char name[40];
printf("너 이름이 뭐니?\n");
scanf("%s", name);
printf("반가워, %s야. %s\n", name, PRAISE);
printf("이름은 %zd글자인데, 메모리 셀 %zd개를 차지합니다.\n",
strlen(name), sizeof name);
printf("감탄문은 %zd글자인데, ", strlen(PRAISE));
printf("메모리 셀 %zd개를 차지합니다. \n", sizeof PRAISE);
return 0;
}
헤더 파일
#include <stdio.h> → printf(), scanf() 함수는 표준 입출력 함수 계열에 속한다.
#include <string.h> → strlen() 함수는 문자열 관련 함수들과 같은 계열에 속한다.
- C는 함수 라이브러리를, 관련된 함수들끼리 계열 단위로 묶고, 각 계열마다 하나씩 헤더파일을 제공한다.
- 일부 ANSI 이전의 Unix 시스템들은 문자열 함수들의 선언을 포함하기 위해 string.h 대신 strings.h를 사용한다.
- 11장에서 자세히!!
긴 printf()문 다루기
printf("이름은 %zd글자인데, 메모리 셀 %zd개를 차지합니다.\n",
strlen(name), sizeof name);
printf("감탄문은 %zd글자인데, ", strlen(PRAISE));
printf("메모리 셀 %zd개를 차지합니다. \n", sizeof PRAISE);
- 긴 printf()문을 다루기 위해 두 가지 방법을 사용한다.
① 하나의 printf() 문을 두 라인에 걸쳐 사용한다. 라인은, 문자열의 중간, 즉 좌우 양편의 큰 따옴표 사이가 아니라
printf()에 전달될 전달인자들 사이에서 분리할 수 있다.
② 두 개의 printf() 함수를 사용해서 한 라인을 출력한다. 개행 문자(\n)는 두 번째 문장에만 들어 있다.
널NULL 문자
j | e | o | n | g | h | e | e | \0 |
printf("이름은 %zd글자인데, 메모리 셀 %zd개를 차지합니다.\n", strlen(name), sizeof name);
printf("메모리 셀 %zd개를 차지합니다. \n", sizeof PRAISE);
- 배열 name은 40개의 메모리 셀을 가진다. (sizeof 연산자가 그렇다고 출력함)
- 그런데 jeonghee를 저장하려면 8개의 셀만 있으면 된다. (strlen() 함수가 그렇다고 함)
- 배열 name의 9번째 셀에는 NULL 문자가 들어있다. 이 널 문자는 strlen() 함수에게 언제 카운트를 멈추어야 하는지 알리는 역할을 한다. = strlen() 함수는 널 문자(\0)에서 카운트를 멈춘다.
* 널(null) 문자 : 아스키코드값 0에 해당. symbol(name)은 NUL. 숫자 0과 구분해주기 위해 '\0'으로 사용!!!!!!!!
printf("메모리 셀 %zd개를 차지합니다. \n", sizeof PRAISE);
감탄문은 24글자인데, 메모리 셀 25개를 차지합니다.
- strlen()은 그 문자열에 있는 공백과 구두점까지 포함한 문자의 정확한 개수를 알려준다.
- sizeof 연산자는 문자열을 끝내기 위해 사용한 보이지 않는 널 문자까지 카운트하기 때문에, 그보다 하나 더 큰 수를 알려준다.
- sizeof 연산자의 괄호 사용 여부는 데이터형의 크기를 원하는지, 아니면 어떤 특정 양에 대한 크기를 원하는지에 달렸다. 데이터형에는 괄호를 반드시 사용해야 하지만, 어떤 특정 양에 괄호를 사용하는것은 옵션이다.
(예시) sizeof(char), sizeof(int), sizeof name, sizeof 6.28=sizeof(6.28)
04
/* 피자 가게에서 쓰는 용어로 상수를 정의한다 */
#include <stdio.h>
#define PI 3.14159
int main(void)
{
float area, circum, radius;
printf("피자의 반지름이 얼마냐?\n");
scanf("%f", &radius);
area = PI * radius * radius;
circum = 2.0 * PI *radius;
printf("피자의 기본 매개변수는 다음과 같다:\n");
printf("circumference = %1.2f, area = %1.2f\n", circum, area);
return 0;
}
원하는 소수점 자리까지 출력하기
printf("circumference = %1.2f, area = %1.2f\n", circum, area);
circumference = 37.70, area = 113.10
- printf()문에서 %1.2f는 소수점 아래 2자리까지 출력한다.
상수를 만드는 방법
#define PI 3.14159
- 상수 : 변하지 않는 값
- 처음 선언할 때 값을 할당 받고 이후 변경 불가능!! 반드시 선언과 동시에 초기화를 같이 해야 한다.
- 일반적으로 상수는 대문자로 만든다. - ① const 변경자를 사용하는 방법
- const 키워드를 사용하여 변수 선언을 상수 선언으로 변환할 수 있다.
- const int MONTHS = 12; // MONTHS는 12를 나타내는 기호 상수이다.
- 이것은 MONTHS를 읽기 전용 값으로 만든다. 표시하거나 계산할 수 있지만 MONTHS의 값은 변경할 수 없다. - ② #define을 사용해서 매크로 상수를 만들어 값을 치환시키는 방법
- #define 지시자는 문자 상수와 문자열 상수에도 사용할 수 있다. 문자 상수 : 작은 따옴표, 문자열 상수 : 큰 따옴표
- 다음 예들은 유효하다.
#define BEEP '\a'
#define TEE 'T'
#define ESC '\033'
#define OOPS "와, 너무 졸리다."
- 상수 이름은, 상수 이름 뒤에 정의 된 모든 것으로 대체된다는 것을 기억하기!!!!
#define TOES = 20 은 TOES의 값이 20이라는 것이 아니라 TOES 값 자체가 = 20 로 대체된다. - const와 define의 차이점 (const가 define보다 효율적인 이유
- define은 단순히 치환 시켜준다는 의미로,
PI라는 기호식 이름으로 보이지만 컴파일러에게는 3.14159라는 값으로 밖에 안보인다.
- define은 전처리기(main() 함수 바깥에 쓰는 매크로 상수)이므로 컴파일 전에 선행처리자가 치환시켜버리기 때문!
- 치환과정에서 한 번의 연산이 들어가야해서 연산상의 불이익도 있고 컴파일러가 쓰는 기호 테이블에 들어가지 않음
- 기호 테이블에 들어가지 않으면 에러 발생 시 원인을 찾기 힘들다거나 하는 등의 다양한 문제가 발생한다. - 하지만 const를 사용하여 상수화하면 컴파일 과정에서 이 변수를 상수로 인식하며, 기호 테이블에 들어간다.
- 또한 const를 사용하면 컴파일 과정에서 상수 하나를 계속 돌려 쓰지만 define을 사용했을 때는 define으로 정의된 값들을 치환하는 과정에서 사용한 상수만큼의 사본이 생겨나 비효율적이다.
05
//* limits.h와 float.h에 정의되어 있는 기호 상수들을 사용한다. */
#include <stdio.h>
#include <limits.h> // 정수의 한계
#include <float.h> // 실수의 한계
int main(void)
{
printf("이 시스템이 표현하는 수의 한계:\n");
printf("int형 최댓값: %d\n", INT_MAX);
printf("long long형 최솟값: %lld\n", LLONG_MIN);
printf("이 시스템에서 1 byte는 %d bit이다.\n", CHAR_BIT);
printf("double형 최댓값: %e\n", DBL_MAX);
printf("float형 최솟값: %e\n", FLT_MIN);
printf("float형 정밀도는 소수점 아래 %d자리까지\n", FLT_DIG);
printf("float형 epsilon = %e\n", FLT_EPSILON);
return 0;
}
명단 상수의 사용
#include <limits.h>
#include <float.h>
- C의 헤더 파일, limits.h와 float.h는 정수형과 부동소수점형의 크기 제한에 관련된 자세한 정보를 각각 제공한다.
- 각 파일에는 사용자 시스템에 적용되는 명단 상수들이 시리즈로 기재되어 있다.
- (예시) limits.h 파일에는 다음과 비슷한 내용이 들어있다.
#define INT_MAX +32767 // 시스템이 32비트 int형을 사용한다면, 다른 값으로 정의되어 있을 것이다.
#define INT_MIN -32768 // 이 상수들은 int형이 가질 수 있는 최댓값과 최솟값을 나타낸다. - 마찬가지로 float.h는 float형과 double형이 지원하는 유효숫자의 자릿수를 나타내는 FLT_DIG, DBL_DIG와 같은 상수들을 정의하고 있다.
06 printf()의 사용
/* 변환 지정자를 사용한다 */
#include <stdio.h>
#define PI 3.141593
int main(void)
{
int number = 7;
float pies = 12.75;
int cost = 7800;
printf("%d명의 경쟁자가 %f판의 딸기파이를 먹었다.\n", number, pies);
printf("The value of pi is %f.\n", PI);
printf("잘 가세요! 그대는 내가 소유하기에 과분하네요.\n");
printf("%c%d\n", '$', 2 * cost);
return 0;
}
printf() 함수의 전달인자
printf("%d명의 경쟁자가 %f판의 딸기파이를 먹었다.\n", number, pies);
- printf() 함수의 제어문은 서로 구별되는 두 가지 정보를 가지고 있다.
① 실제로 출력될 문자들 : You look great in
② 변환 지정자 : %s - 문구만 출력하려 한다면, 변환 지정자를 사용할 필요가 없다.
printf("잘 가세요! 그대는 내가 소유하기에 과분하네요.\n"); - 데이터만 출력하려 한다면, 설명 문구를 사용할 필요가 없다.
printf("%c%d\n", '$', 2 * cost); - printf() 함수가 % 기호를 사용하여 변환 지정을 인식하므로, % 기호 자체를 출력하려 할 때는 %%라고 쓰면 된다.
printf() 변경자/변환지정자
- 문법 : % [매개 변수] [플래그] [너비] [.정밀도] [길이] 형식 지정자 유형 // [ ]괄호 안은 옵션
- %와 변환 문자 사이에 변경자를 삽입하면, 기본적인 변환 지정을 변경할 수 있다.
- 여러 개의 변경자를 동시에 사용하여 여러 개의 데이터에 서로 다른 형식을 지정해 출력할 수 있다.
- 포맷 문자열 뒤에 오는 각 출력할 항목마다 하나씩 변경자를 1:1로 대응시켜야 한다!!
- printf("볼카운트가 %d 스트라이크, %d 볼이었다.\n", strike); → 두번째 %d에 대응할 값을 빠뜨림
변경자 | 의미 |
% | '%'문자를 출력하고자 할 때는, '%%'와 같이 두번 입력 |
c | 문자(ASCII코드 값) |
s | 문자열 |
d(=i) | 부호가 있는 10진 정수 |
u | 부호가 없는 10진 정수 |
x | 부호가 없는 16진 정수(알파벳 소문자) |
X | 부호가 없는 16진 정수(알파벳 대문자) |
p | 포인터. 메모리 주소. |
n | 현재까지 출력한 문자의 개수(int 포인터를 인수로 받음) |
f | 소수점을 표기하는 실수(알파벳 소문자) |
e | 지수 표기법으로 표기하는 실수(알파벳 소문자) |
g | %f와 %e중 길이가 짧은 것(알파벳 소문자) |
변경자 | 의미 | 예시 | 실행 결과 | |
정수 | %d, %i | 10진 정수 | printf(“%d %i”,-20,12); | -20 12 |
%u | 양의 10진 정수 | printf(“%u”,12); | 12 | |
%o | 양의 8진 정수 | printf(“%o”,19); | 23 | |
%x | 양의 16진 정수(소문자) | printf(“%x”,14); | e | |
%X | 양의 16진 정수(대문자) | printf(“%X”,14); | E | |
%lu | unsigned long | printf(“%lu”,100); | 100 | |
%ld | signed long | printf(“%ld”,1000); | 1000 | |
%llu | unsigned long long | printf(“%llu”,144); | 144 | |
%lld | signed long long | printf(“%lld”,169); | 169 | |
실수 | %f | 실수의 소수점 표현(소문자,float) | printf(“%f”,1.2); | 1.200000 |
%F | 실수의 소수점 표현(대문자,float) | printf(“%f”,1.2); | 1.200000 | |
%e | 실수의 지수 표현법(소문자,float) | printf(“%e”,1.2); | 1.200000e+00 | |
%E | 실수의 지수 표현법(대문자,float) | printf(“%E”,1.2); | 1.200000E+00 | |
%g | %f 와 %e 중에서 짧은 것을 표현(소문자) | printf(“%g”,1,2); | 1.2 | |
%G | %F 와 %E 중에서 짧은 것을 표현(대문자) | printf(“%G”,1.2); | 1.2 | |
%a | 실수를 16진법으로 표현(소문자) | printf(“%a”,1.2); | 0x1.3333333333333p+0 | |
%A | 실수를 16진법으로 표현(대문자) | printf(“%A”,1.2); | 0X1.3333333333333P+0 | |
%lf | 실수의 소수점 표현(소문자,double) | printf(“%lf”,1.2); | 1.200000 | |
%lF | 실수의 소수점 표현(대문자,double) | printf(“%lF”,1.2); | 1.200000 | |
%le | 실수의 지수 표현법(소문자,double) | printf(“%le”,1.2); | 1.200000e+00 | |
%lE | 실수의 지수 표현법(대문자,double) | printf(“%lE”,1.2); | 1.200000E+00 | |
문자 | %c | 문자 | printf(“%c”,’A’); | A |
%s | 문자열 | printf(“%s”,”Test String”); | Test String | |
포인터 | %p | 포인터의 메모리 주소 |
07 정수를 출력할 때 필드 너비 변경자가 가져오는 효과
/* 필드 너비 */
#include <stdio.h>
#define PAGES 959
int main(void)
{
printf("*%d*\n", PAGES);
printf("*%2d*\n", PAGES);
printf("*%10d*\n", PAGES);
printf("*%-10d*\n", PAGES);
return 0;
}
변경자와 플래그의 조합
printf("*%d*\n", PAGES); ─ ①
printf("*%2d*\n", PAGES); ─ ②
printf("*%10d*\n", PAGES); ─ ③
printf("*%-10d*\n", PAGES); ─ ④
- 서로 다른 4개의 변환 지정을 사용하여 같은 값을 네 번 출력한다.
- 각각의 필드가 시작하고 끝나는 위치를 표시하기 위해 애스터리스크(*)를 사용한다.
- ① 첫 번째 변환 지정은 변경자를 사용하지 않은 %d다. 이것은 출력될 정수와 같은 너비의 필드를 만든다. <디폴트>
- ② 두 번째 변환 지정은 너비가 2인 필드를 만들어야 한다. 그런데 3자리가 필요한 정수이기 때문에 그 수에 맞게 필드가 자동으로 확장된다.
- ③ 다음 변환 지정은 %10d다. 이것은 스페이스 10개에 해당하는 필드 너비를 확보하고, 좌우 양편의 * 사이에 7개는 블랭크로, 3개는 숫자로 채운다. 이때 숫자들은 오른쪽으로 정렬하여 출력한다.
- ④ 마지막 지정은 %-10d다. 이것 역시 스페이스 10개에 해당하는 필드 너비를 확보하지만, - 플래그 때문에 숫자들을 왼쪽으로 정렬하여 출력한다.
printf() 변경자 조합
- 하나 이상의 변경자를 사용할 때는, 표의 순서를 따라야 한다.
- 모든 조합이 가능한 것은 아니며, 시스템에 따라 지원하는 것이 다르다.
변경자 | 의미 |
플래그 | 아래 표보다 많거나 없는 플래그가 있을 수 있다. |
숫자(들) | <최소 필드 너비> 그 필드가 출력될 수나 문자열에 맞지 않으면, 더 넓은 필드 너비가 사용된다. "%4d" |
.숫자(들) | <정밀도> %e, %E, %f 변환 지정자의 경우- 소수점 아래에 출력될 자릿수 <정밀도> %g, %G 변환 지정자의 경우- 최대 유효숫자 자릿수 <정밀도> %s 포맷 지정자의 경우 - 출력될 최대 문자 수, <정밀도> 정수형 변환의 경우 - 출력할 최소 자릿수. 이를 맞추기 위해 필요하다면 여분의 0을 붙인다. <정밀도> 소수점(.)만 있으면, 소수점 뒤에 0이 있다는 것을 의미한다. → %.f == %.0f |
h | 정수형 변환 지정자에 함께 사용되어 short int형 또는 unsigned char형 값을 나타낸다. "%hu", "%hx" |
hh | 정수형 변환 지정자에 함께 사용되어 signed char형 또는 unsigned char형 값을 나타낸다. "%hhu", "%hhx" |
j | 정수형 변환 지정자에 함께 사용되어 (stdint.h에 정의되어 있는) intmax_t형 또는 uintmax_t형 값을 나타낸다. |
l | 정수형 변환 지정자에 함께 사용되어 long int형 또는 unsigned long int형 값을 나타낸다. "%ld", "%8lu" |
ll | 정수형 변환 지정자에 함께 사용되어 long long int형 또는 unsigned long long int형 값을 나타낸다. (C99) "%lld", "%8llu" |
L | 부동소수점형 변환 지정자에 함께 사용되어 Long double형 값을 나타낸다. "%Lf", "%10.4Le" |
t | 정수형 변환 지정자에 함께 사용되어 ptrdiff_t형 값을 나타낸다. 이것은 두 포인터 사이의 차에 대응하는 데이터형이다.(C99) "%td", "%12ti" |
z | 정수형 변환 지정자에 함께 사용되어 size_t형 값을 나타낸다. 이것은 sizeof에 의해 리턴되는 데이터형이다. (C99) "%zd" "%12zx" |
- 플래그와 그 외 옵션
플래그 | 의미 | 예시 |
- | 부호가 없는 오른쪽 정렬이 기본이며, - 가 붙으면 왼쪽 정렬로 출력한다. | "%-20s" |
+ | + 부호 있으면, 오른쪽 정렬상태로, 양수엔 +, 음수엔 -기호를 붙여 출력한다. | "%+6.2f" |
0 | 0 이 숫자 앞에 있으면, 앞자리의 공백부분을 0으로 출력한다. - 플래그가 있거나, 정수형에 정밀도가 지정되어 있으면 이 플래그는 무시된다. |
"%010d", "08.3f" |
# | 변환 지정자(#)에 대한 대체형식으로 0 (8진수), 0x (16진수)를 부가하여 출력 %o의 경우에는 하나의 0을 앞에 붙인다. %x와 %X의 경우에는 0x와 0X를 각각 붙인다. 모든 부동소수점형에 대해, #은 소수점 아래에 아무 것도 없더라도 소수점 출력을 보장한다. %g와 %G형의 경우에는 뒤에 붙는 0이 제거되지 않도록 한다. |
"%#o", "%#8.0f" |
스페이스 | 부호 있는 수를 출력할 때, 양수면 부호 없이 스페이스를 하나 붙이고 음수이면 -를 붙인다. + 플래그는 스페이스를 무시한다. |
"%6.2f" |
- 필드폭 설정 예시
출 력 | 결 과 | 설 명 |
1. 정수 필드폭 설정 ( %d, %i, %u, %o, %x, %X ) | ||
printf("|%d|\n", 153); printf("|%5d|\n", 153); printf("|%-5d|\n", 153); printf("|%05d|\n", 153); |
|153| | 153| |153 | |00153| |
3자리로, 출력 5자리로, 오른쪽 정렬 5자리로, 왼쪽 정렬 5자리로, 빈칸에 0으로 채워 출력 |
printf("|%#o|\n", 0123); printf("|%#x|\n", 0x123); |
|0123| |0x123| |
변환지정자(#)를 사용하여, 8진수 0을 표시하여 출력 변환지정자(#)를 사용하여, 16진수 0x를 표시하여 출력 |
2. 실수 필드폭 설정 (%f, %e, %E, %g, %G ) | ||
printf("|%7.1f|\n", 153.78); printf("|%-7.1f|\n", 153.78); printf("|%07.1f|\n", 153.78); |
| 153.8| |153.8 | |00153.8| |
7자로 소수점 1자리로 반올림하여, 오론쪽 정렬 출력 7자로 소수점 1자리로 반올림하여, 왼쪽 정렬 출력 7자로 소수점 1자리로 반올림하여, 빈칸 0으로 채워 출력 |
3. 문자(열) 필드폭 설정 (%c, %s) | ||
printf("|%c|\n", 'A'); printf("|%5c|\n", 'A'); printf("|%-5c|\n", 'A'); |
|A| | A| |A | |
문자 한글자 출력 5자리로, 오른쪽 정렬하여 출력 5자리로, 왼쪽 정렬하여 출력 |
printf("|%s|\n", "ABC"); printf("|%5s|\n", "ABC"); printf("|%-5s|\n", "ABC"); |
|ABC| | ABC| |ABC | |
문자열 출력 5자리로, 오른쪽 정렬하여 출력 5자리로, 왼쪽 정렬하여 출력 |
- 사용 예시
#include <stdio.h>
int main()
{
/* width옵션과 .옵션의 비교 */
printf("1 |%03d|\n", -1);
printf("2 |%.3d|\n", -1);
printf("3 |%5.3d|\n", -1);
/* .0옵션과 정수 0 */
printf("4 |%.0d|\n", 0);
/* .옵션과 %s */
printf("5 |%.3s|\n", "hello");
return (0);
}
#include <stdio.h>
int main()
{
printf("↓ 1칸 공백\n");
printf("%4d\n",144); //%d의 출력 넓이를 4로 지정, 남는 공간을 공백으로 채움
printf("%5d\n",144); //%d의 출력 넓이를 5로 지정, 남는 공간을 공백으로 채움
printf("↑ 2칸 공백\n\n");
printf("%04d\n",144); //%d의 출력 넓이를 4로 지정, 남는 공간에 공백이 아닌 0으로 채움
printf("%05d\n\n",144); //%d의 출력 넓이를 5로 지정, 남는 공간에 공백이 아닌 0으로 채움
printf("% d\n",144); //양수일 때는 부호를 표시하지 않고 공백으로 표시
printf("% d\n\n",-144); //음수일 때는 부호를 표시
printf("%+d\n",144); //양수일 때는 +부호를 표시
printf("%+d\n\n",-144); //음수일 때는 -부호를 표시
printf("%.2f\n",1.2); //소수 둘째 자리 까지 표시
printf("%.2e\n\n",1.2); //소수 둘째 자리 까지 표시, 지수도 표현
printf("%010.3f\n",1.2);//출력 넓이 10, 소수 3째 자리까지 표시, 남는 공간을 0으로 채움
printf("%010.3e\n\n",1.2);//출력 넓이 10, 소수 3째 자리까지 표시, 남는 공간을 0으로 채움, 지수도 표현
printf("%#o\n",19); //8진수면 앞에 0을 표시함
printf("%#x\n",15); //16진수 소문자 출력, 앞에 0x를 표시함
printf("%#X\n\n",0xa1); //16진수 대문자 출력, 앞에 0X를 표시함
printf("↓ 4칸 공백\n");
printf("%5c\n",'S'); //출력 넓이를 5로 지정, 남는 공간을 공백으로 채움
printf("%5s\n","Test"); //출력 넓이를 5로 지정, 남는 공간을 공백으로 채움
printf("↑ 1칸 공백\n");
return 0;
}
09
/* 몇 가지 포맷 변경자 플래그 */
#include <stdio.h>
int main(void)
{
printf("%x %X %#x\n", 31, 31, 31);
printf("**%d**% d**% d**\n", 42, 42, -42);
printf("**%5d**%5.3d**%0.5d**%5.3d**\n", 6, 6, 6, 6);
return 0;
}
포맷 지정자와 변경자의 더 많은 조합
1f 1F 0x1f
- 출력의 첫 번째 라인에서, 1f는 31의 16진수 표기이다. 지정문자 x는 1f, X는 1F를 출력한다.
- # 플래그는 앞에 0x를 붙여서 출력한다.
**42** 42**-42**
- 출력의 두 번째 라인은 지정자에 스페이스를 사용했을 때, 양수의 경우에는 앞에 스페이스를 넣고 음수의 경우 스페이스를 넣지 않는다는 것을 보여준다. 이것을 이용하면 유효숫자가 같은 양수와 음수가 동일한 필드 너비에 출력되기 때문에 보기 좋은 출력을 얻을 수 있다.
1f 1F 0x1f
- 출력의 세 번째 라인은 정수형에 정밀도 지정자(%5.3d)를 사용하여 그 수를 지정한 최소 자릿수(이 경우에는 3)에 맞추는 데 필요한 만큼만 0으로 채운다는 것을 보여준다. 그러나 0 플래그를 사용하면, 그 수를 필드 너비 전체에 맞추는 데 필요한 만큼 0으로 채운다. 마지막으로, 0 플래그와 정밀도 지정자를 함께 사용하면 0 플래그는 무시된다.
10
/* stringf.c -- 문자열 포맷 */
#include <stdio.h>
#define BLURB "Authentic imitation!"
int main(void)
{
printf("[%2s]\n", BLURB);
printf("[%24s]\n", BLURB);
printf("[%24.5s]\n", BLURB);
printf("[%-24.5s]\n", BLURB);
return 0;
}
문자열 옵션
printf("[%24.5s]\n", BLURB);
printf("[%-24.5s]\n", BLURB);
- 정밀도 지정이 출력될 문자 수를 어떻게 제한하는지 보면, 포맷 지정자에서 .5는 printf()에게 5개의 문자만 출력하라고 지시한다.
- - 변경자는 문자들을 왼쪽으로 정렬시킨다.
15
/* &를 언제 사용하는가 */
#include <stdio.h>
int main(void)
{
int age; // 변수
float assets; // 변수
char pet[30]; // 문자열
printf("나이, 재산, 좋아하는 애완동물을 입력하세요.\n");
scanf("%d %f", &age, &assets); // 여기에는 &를 사용한다.
scanf("%s", pet); // 문자 배열에는 &를 사용하지 않는다.
printf("%d $%.2f %s\n", age, assets, pet);
return 0;
}
scanf() ②
scanf("%d %f", &age, &assets); // 여기에는 &를 사용한다.
scanf("%s", pet); // 문자 배열에는 &를 사용하지 않는다.
- 키보드로부터 입력되는 것은 텍스트다. 정수 2014를 입력하려면 4개의 문자 2,0,1,4를 차례로 타이핑해야 한다. 이것을 문자열이 아닌 수치값으로 저장하려면 프로그램은 그 문자열의 문자 하나하나를 수치값으로 변환해야 한다. 바로 이것이 scanf()가 하는 일이다.
- scanf()는 문자열 입력을 다양한 포맷(정수, 부동소수점 수, 문자, C 문자열)으로 변환한다. 포맷 문자열은, 문자들의 입력 스트림을 변환할 데이터형들을 지시한다.
- scanf()를 사용하기 위해 포인터에 대해 어떠한 것도 알 필요가 없고 다음 규칙만 알면 된다.
⑴ scanf()를 사용하여 기본 데이터형의 값을 읽는다면, 변수 이름 앞에 & 기호를 사용한다.
⑵ scanf()를 사용하여 문자열을 읽어 문자 배열 안에 넣는다면, & 기호를 사용하지 않는다. - scanf() 함수는 사용자의 입력을 개별적인 필드들로 나누기 위해 화이트스페이스(개행, 탭, 스페이스)를 사용한다.
- scanf() 함수는 중간에 들어 있는 화이트스페이스는 건너뛰면서, 연속된 변환 지정자들을 연속된 필드에 각각 연결시킨다.
- 위 샘플 실행에서 입력이 두 라인에 걸쳐 있다는 것에 주목하면, 각각의 입력 항목 사이에 최소한 하나의 개행, 스페이스, 탭을 넣기만 한다면 한 라인에 입력할 수도 있고, 다섯 라인에 걸쳐 입력할 수도 있다.
scanf()에 대한 ANSI C 변환 지정자
변환 지정자 | 의미 |
%c | 입력을 문자로 해석한다. |
%d | 입력을 부호 있는 10진 정수로 해석한다. |
%e, %f, %g, %a | 입력을 부동소수점 수로 해석한다. (%a는 C99) |
%E, %F, %G, %A | 입력을 부동소수점 수로 해석한다. (%A는 C99) |
%f | 입력을 부호 있는 10진 정수로 해석한다. |
%o | 입력을 부호 있는 8진 정수로 해석한다. |
%p | 입력을 포인터(주소)로 해석한다. |
%s | 입력을 문자열로 해석한다. 입력은 화이트스페이스가 아닌 첫 문자로 시작하며, 다음 번 화이트스페이스 앞까지의 모든 것을 포함한다. |
%u | 입력을 부호 없는 10진 정수로 해석한다. |
%x, %X | 입력을 부호 없는 8진 정수로 해석한다. |
- scanf() 함수는 printf() 함수와 거의 동일한 변환 지정 문자들을 사용한다.
- 주요한 차이점은!! print()함수는 float형과 double형 모두에 %f, %e, %E, %g, %G를 사용하지만,
scanf() 함수는 float형에 대해서만 이들을 사용하고 double형에 대해서는 l 변경자를 요구한다는 것이다.
scanf()의 변환 변경자
변경자 | 의미 | 예시 |
* | 지정을 무시한다. | "%*d" |
자릿수(들) | 최대 필드 너비, 입력이 최대 필드 너비에 도달하거나 첫번째 화이트 스페이스를 만나면 중지된다. | "%10s" |
hh | 하나의 정수를 signed char형 또는 unsigned char형으로 읽는다. | "%hhd", "%hhu" |
ll | 하나의 정수를 long long형 또는 unsigned long long형으로 읽는다. | "%lld", "%llu" |
h, l, L | "%hd"와 "%hx", "%hu"는 그 값이 unsigned short int형으로 저장될 것을 나타낸다. "%le", "%lf", "%lg"는 그 값이 double형으로 저장될 것이라는 것을 나타낸다. 이러한 변경자들이 없으면 d, f, o, x는 int형을 나타내고 e, f, g는 float형을 나타낸다. |
|
j | 정수형 지정자가 뒤에 붙으면 intmax_t형 또는 uintmax_t형을 나타낸다. (C99) | "%jd", "%ju" |
z | 정수형 지정자가 뒤에 붙으면 sizeof가 리턴하는 데이터형을 사용하여 나타낸다. (C99) | "%zd", "%zo" |
t | 정수형 지정자가 뒤에 붙으면 두 포인터 간의 차이를 나타내는데 쓰이는 데이터형을 사용하여 나타낸다. (C99) | "%td", "%tx" |
- 변환 지정자를 사용하는 것은 꽤나 복잡하다. 표에 생략된 기능들도 있는데 주로 펀치 카드나 그 밖의 데이터 기록 매체와 같이 매우 정형화되어 있는 자료로부터 선택된 데이터를 쉽게 읽을 수 있게 하는 것들이다.
- 이 책은 프로그램에 데이터를 대화식으로 제공하는데 주로 사용하기 때문에 난해한 기능까지는 ...........^^이다.
scanf()가 입력을 읽는 방법
- 하나의 정수를 읽기 위해 %d를 사용한다고 했을 때, scanf() 함수는 한 번에 한 문자씩 입력을 읽기 시작한다.
- 화이트스페이스(스페이스, 탭, 개행)를 만나면, 화이트스페이스가 아닌 문자가 나타날 때까지 건너뛴다.
- 정수를 읽으려 시도하기 때문에 scanf()는 하나의 숫자나 부호(+ 또는 -)를 만나게 될것을 예상하고 있다.
- 하나의 숫자나 부호를 만나면 그 문자를 저장하고 다음 문자를 읽는다.
- 읽은 문자가 숫자이면 그것을 저장하고 다음 문자를 읽는다.
- 숫자가 아닌 문자를 만날 때까지 scanf()는 계속해서 숫자를 읽어 저장한다.
- 그러다가 숫자가 아닌 문자를 만나면 정수의 끝에 도달했다고 판단하고 입력을 끝낸다.
- scanf()는 마지막으로 읽은 숫자가 아닌 문자를 입력에 되돌려 준다.
= 다음 번에 프로그램이 입력을 읽으려 시도할 때 이전의 입력이 되돌려 놓은 숫자가 아닌 그 문자부터 읽기가 시작된다는 뜻 - 끝으로, scanf()는 읽은 숫자(그리고 부호)들에 해당하는 수치값을 계산하여 지정한 변수에 넣는다.
- 필드 너비를 사용한다면, scanf()는, 필드의 끝에 도달했거나 첫 화이트스페이스를 만났거나 둘 중 먼저 해당되는 경우에서 입력을 끝낸다.
%d일 때 문자 입력
- 화이트스페이스가 아닌 첫 번째 문자가 숫자가 아니라 가령 A라면 어떻게 될까?
- 그러면 scanf()는 그 즉시 읽기를 멈추고 A(또는 무엇이라도)를 입력으로 되돌려 놓는다.
= 지정된 변수에 어떠한 값도 대입되지 않는다. - 프로그램이 입력을 읽는 다음 번에 다시 A에서부터 시작한다.
- 프로그램이 %d 지정자들만 가지고 있다면, scanf()는 결코 A를 건너가지 못할 것이다. 또한 scanf()에 여러 개의 포맷 지정자를 사용한다면, C는 첫 번째 실패했을 때 scanf()가 읽기를 멈추어야 한다고 규정하고 있다.
수치형 지정자
- 다른 수치형 지정자들을 사용하여 입력을 읽는 것도 %d의 경우와 거의 비슷하게 동작한다. 주요한 차이는, scanf()가 더 많은 문자들을 수의 일부로 인식할 수 있다.
- 예를 들면, %x 지정자는 scanf()가 16진수 숫자 a-f와 A-F를 인식하도록 지정한다. 부동소수점형 지정자들은 scanf()가 소수점, e-표기 그리고 새로운 p-표기도 인식하도록 지정한다.
문자형 지정자 %s
- %s 지정자를 사용하면, 화이트스페이스가 아닌 어떠한 문자도 받아들일 수 있다.
- 따라서, scanf()는 화이트스페이스가 아닌 첫 문자가 나타날 때까지 건너뛰고, 다시 화이트스페이스를 만날 때까지 계속해서 화이트스페이스가 아닌 문자들을 읽는다.
- 결과적으로 이것은 scanf()에게 하나의 단어(화이트스페이스가 들어있지 않은 문자열)를 읽게 만든다.
- 필드 너비를 사용할 경우, scanf()는 그 필드의 끝에서 멈추거나 첫 화이트스페이스에서 입력을 멈춘다.
- 필드 너비를 사용하여 하나의 %s에 지정자에 대해 scanf()가 하나 이상의 단어를 읽게 만들 수 있다.
- 끝으로, scanf()는 지정된 배열에 그 문자열을 넣을 때, 그 배열의 내용을 C의 문자열로 만드는 '\0'을 끝에 추가한다.
문자형 지정자 %c
- %c 지정자를 사용하면, 모든 문자들을 공평하게 입력할 수 있다.
- 다음 입력 문자가 스페이스나 개행일지라도 하나의 스페이스나 개행이 지정된 변수에 대입된다.
- 화이트스페이스들도 건너뛰지 않는다.
scanf()가 C에서 가장 일반적으로 사용되는 입력 함수는 아니다. C는 문자들을 하나씩 읽거나 스페이스가 들어있는 문자열을 읽는 것과 같은, 특정한 작업에 보다 적합한 getchar()와 fgets()과 같은 다른 입력 함수를 가지고 있다. >> ch7/11/13
포맷 문자열에 들어 있는 일반 문자
- scanf() 함수는 포맷 문자열 안에 일반 문자들을 넣는 것을 허용한다.
- 스페이스 문자를 제외한 일반 문자들은 입력하는 문자열과 정확히 일치해야 한다.
- 예를 들어, 다음과 같이 두 지정자 사이에 실수로 콤마(,)를 넣었다고 가정하자.
scanf("%d,%d", &n, &m);
scanf() 함수는 이것을, 사용자가 하나의 수를 타이핑하고, 이어서 콤마(,)를 타이핑하고, 다시 하나의 수를 타이핑할 것이라고 해석한다. 즉 포맷 문자열에서 콤마(,)가 %d 바로 뒤에 있기 때문에, 사용자는 다음과 같이 88바로 뒤에 콤마(,)를 타이핑해야 한다.
88,121 또는 88,
121 - 포맷 문자열에 있는 하나의 스페이스는 다음에 입력될 항목 앞에 놓여 있는 모든 화이트스페이스를 무시하라는 뜻이다.
- 여기서 모든 화이트스페이스라는 말은 화이트스페이스가 없는 경우까지도 그 개념에 포함한다.
- 예를 들어, 다음과 같은 문장은
scanf("%d ,%d", &n, &m);
다음과 같이 입력한 라인들을 모두 받아들인다.
88,121 또는 88 ,121 또는 88 , 121 - %c를 제외한 지정자들은, 입력 값 앞에 오는 화이트스페이스를 자동으로 무시한다.
따라서, scanf("%d%d, &n, &m)는 scanf("%d %d", &n, &m); 와 동일하게 동작한다.
%c의 경우 포맷 문자열에 하나의 스페이스 문자를 추가하면 다르게 동작한다. - 예를 들어, 포맷 문자열의 %c 앞에 하나의 스페이스가 있으면, scanf()는 화이트스페이스가 아닌 첫 문자가 나타날 때까지 건너뛴다.
- 즉, scanf("%c", &ch) 명령은 입력에서 만나는 첫 문자를 무조건 읽는다.
그러나 scanf(" %c", &ch) 는 화이트스페이스는 건너뛰고, 화이트스페이스가 아닌 첫 문자를 읽는다.
scanf()의 리턴값
- scanf() 함수는 성공적으로 읽은 항목의 수를 리턴한다. 읽은 항목이 없을 때(이와 같은 일은 수치 값이 기대되는 곳에 수치가 아닌 문자열을 타이핑했을 때 일어난다), scanf()는 0을 리턴한다.
- 파일의 끝이라고 알려진 어떤 조건을 만나면 EOF를 리턴한다. *EOF >> 6장
(EOF는 stdio.h에 정의되어 있는 하나의 특별한 값이다. 일반적으로 #define 지시자에 의해 EOF를 -1로 정의한다.) - if문과 while문을 배운 후에, scanf()의 리턴 값을 일치하지 않는 입력을 찾아서 처리하는 데 사용할 수 있다.
16
/* 가변적인 필드 너비 */
#include <stdio.h>
int main(void)
{
unsigned width, precision;
int number = 256;
double weight = 242.5;
printf("필드 너비를 입력하세요:\n");
scanf("%d", &width);
printf("Number:%*d:\n", width, number);
printf("필드 너비와 정밀도를 함께 입력하세요:\n");
scanf("%d %d", &width, &precision);
printf("Weight = %*.*f\n", width, precision, weight);
printf("! 종료 !\n");
return 0;
}
print()에서의 * 변경자
printf("Number:%*d:\n", width, number);
printf("Weight = %*.*f\n", width, precision, weight);scanf("%s", name);
- printf()와 scanf()는 둘 다 * 변경자를 사용하여 지정자의 의미를 변경시킬 수 있다. 하지만 방식은 서로 다르다!
- printf()에서 * 변경자의 역할
- 필드 너비를 사용자가 미리 지정하지 않고, 프로그램이 알아서 지정하게 만들고 싶을 때,
필드 너비를 지정하는 수 대신에 *를 사용함으로써 이것을 할 수 있다.
그러나 필드 너비가 얼마인지 알려주는 별도의 전달 인자를 사용해야 한다.
- 즉, 변환 지정자로 %*d를 사용한다면, 전달인자 리스트에 *를 위한 값과 d를 위한 값이 함께 들어있어야 한다.
- 부동소수점형 값의 필드 너비뿐만 아니라 정밀도를 지정하는 데에도 이 방법을 사용할 수 있다.
- 변수 width는 필드 너비이고, 변수 number는 출력할 정수이다.
- 지정자에서 *가 d보다 앞에 있으므로, printf()의 전달인자 리스트에서 width가 number 앞에 온다.
- 마찬가지로 width와 precision은 weight를 출력하기 위한 포맷 정보를 제공한다.
- 다음은 실행 예시이다.
- 첫 번째 요구에 6으로 응답했기 때문에, 필드 너비로 6이 사용된다.
- 두 번째 입력 요구에 8 3으로 응답했기 때문에 필드 너비로 8, 소수점 아래 자릿수로 3이 사용되었다.
- 좀 더 일반적으로, 프로그램은 weight의 값을 먼저 조사한 훼 이 변수들의 값을 결정할 수 있다
17
/* 입력에서 처음 2개의 정수를 건너뛴다 */
#include <stdio.h>
int main(void)
{
int n;
printf("3개의 정수를 입력하세요:\n");
scanf("%*d %*d %d", &n);
printf("마지막으로 입력한 정수는 %d입니다.\n", n);
return 0;
}
scanf()에서의 * 변경자
scanf("%*d %*d %d", &n);
- printf()와 scanf()는 둘 다 * 변경자를 사용하여 지정자의 의미를 변경시킬 수 있다. 하지만 방식은 서로 다르다!
- scanf()에서 * 변경자의 역할
- %와 지정자 문자 사이에 *를 넣으면, scanf()가 그에 해당하는 입력을 건너뛴다.
- 다음은 실행 예시이다.
- 이러한 건너뛰기 기능은, 데이터가 일정한 열 형식으로 정렬되어 있는 파일에서 하나의 특정한 열을 읽을 필요가 있을 때 유용하다.
'C언어 > 스터디' 카테고리의 다른 글
ch09 함수 (0) | 2024.08.10 |
---|---|
ch7 제어문_분기와 점프 (0) | 2024.08.09 |
ch6 제어문_루프 문제 (0) | 2024.08.08 |
ch6 제어문_루프 (2) | 2024.08.08 |
ch5 연산자, 표현식, 문장 (0) | 2024.08.07 |