printf

|

- int printf(conast char* format,...)

이 함수는 서식 문자에 맞춰 인자로 들어온 데이터를 모니터에 출력한다. 사용 예시는
printf("%s , %s","Hello","World");
앞의 " " 안에는 서식문자를 넣고, , 로 구분시킨 후 뒤에 변수나 문자열들을 나열해주면 된다. 앞의 %s에는 "Hello"가, 뒤에 %s는 "World"가 들어가게 된다. 주의할점은, 앞의 서식문자가 들어가는 " " 안에 ,나 공백을 넣어주면, 그 형태대로 출력하게 된다. 위에 예제에서는 , 가 들어가있기 때문에 모니터에는 Hello , World 로 출력된다. 마찬가지로 \n도 앞의 서식문자쪽에 넣어줄 수 있다.

 

 

format = 서식문자.

%d, %i   10진수 정수
%ld 부호있는 long 정수
%lld 부호있는 long long 정수

%x, %o  16진수 정수,8진수 정수.(10진수만 양수 표현 가능)

printf("0%o\n",017) , printf("0x%X\n",0x1F) 이런식으로 구분을 쉽게 하기 위해 앞에 표기를 위한 문자를 더 붙이기도 한다.
8진수는 앞에0을, 16진수는 앞에 0x를 붙인다.

%f, %lf   10진수 실수 (f는 float, lf는 double. scanf에서는 구분되지만 printf에서는 구분없이 %f로만 써도 좋다.)

(실수의 표현에서, .을 기준으로 왼쪽은 숫자 표현을 위한 전체 공간, 오른쪽은 소수점 이하 자릿수를 지정해줄 수 있다.
%.2f 면 소수점 이하 자릿수를 2자리로 한다는 뜻. %8.4f면 전체 공간은 8개, 소수점 이하 자리수는 4자리다.)

%Lf long double형 실수
%c   한 개의 문자           (char형에 아스키값을 넣고 %d로 뽑으면 정수가, %c로 뽑으면 문자가 나온다.)

%s   문자열
%u   부호없는 정수(양수만 표현 가능 - %d보다 두 배 넓은 출력 범위.)
%lu 부호 없는 long 정수
%llu 부호 없는 long long 정수
%e   e 표기법(지수 표기법)에 의한 실수

%E   E 표기법에 의한 실수(소문자,대문자 차이뿐임)
%Le long double 형 실수를 지수 표기법으로.
%g   소수점 이하 자리수(6자리)에 따라 6자리를 넘으면 %e로, 넘지 않으면 %f로.
%G   위와 같지만 %E 사용.
%%  %출력.
%ld  long형 10진수
%lx  long형 16진수
%lo  long형 8진수
%a %A 실수를 16진법으로 표기(소문자,대문자)
%p 포인터의 메모리 주소

 

서식 지정자에 다양한 플래그나 폭,정밀도,길이를 조합해서 사용할 수 있다.

포맷은 다음과 같다 : %[플래그][폭][.정밀도][길이]서식지정자


printf("%6d\n",20);
이렇게 하면 폭이 6칸으로 지정된다. 오른쪽부터 20을 채우니 앞에는 공백이 4칸 생긴다.

폭과 플래그를 함께 사용하면

printf("%06d\n",20)

출력 폭을 6칸으로 하고, 남는 공간을 0으로 채우게 되므로 000020이 출력된다.
플래그 부분에는 +혹은 공백(이것이 우리가 흔히 쓰는)을 넣을 수도 있다.

printf("% d\n", 10); : 10출력
printf("% d\n", -10); : -10 출력

printf("%+d\n", 200); : +200
printf("%+d\n",-200); : -200 출력

공백을 넣으면 양수일 때는 부호를 출력하지 않고 공백으로 표시하고 음수일 때는 - 부호를 출력한다. 플래그를 +로 지정하면 양수는 + , 음수는 - 부호를 출력한다.
또는, #을 넣으면 8진수나 16진수를 출력할 때 자동으로 0,0x,0X를 붙여준다.

printf("%#o\n"0721);
printf("%#x\n",0xfl);


실수의 경우 .을 넣어서 정밀도를 지정할 수 있다.

printf("%.2f\n", 1.2f) -> .2f로 적었으므로 소수 둘째 자리까지 출력한다는 뜻.

 실수에 플래그와 폭,정밀도를 함께 적용하면

printf("%010.2f\n",1.2f) -> 출력 폭은 10칸, 둘째자리까지, 남는 공간은 0으로.

 

문자와 문자열에도 마찬가지로 출력 폭을 지정 가능하다.

printf("20s\n","Hello,World!"); -> 출력 폭이 20칸으로 지정되어 오른쪽에서부터 Hello world가 출력됨.

 

 

**** 지수 표기법 ****

2100 -> 2.1e+3
2.1e-2 -> 0.021

정수 부분은 한 자릿수만 적고 소수 뒤에 e와 지수 표기. -지수면 -로 +지수면 +로.



특수문자 (escape character, 혹은 확장 문자)

\a   경고음
\b   백 스페이스
\f    폼 피드
\n   개행
\r    캐리지 리턴
\t    수평 탭
\v   수직 탭
\\   \
\'    '
\"    "

 이것들도 문자 배열에 직접 삽입할 수 있으며, 1바이트씩 차지한다.

 

 실수형의 기본 타입은 double이기 때문에 float를 사용하고 싶다면 float a = 1.23f 이런식으로 뒤에 f를 붙여 표기해야 하는데,
이런식으로 접미사(suffix)를 붙여 리터럴의 크기를 명확하게 표현할 수 있다.

 

 

대문자,소문자 구분 없이 사용한다.

 

#include <stdio.h>
#include <float.h>
int main()
{
 printf("%ld\n", -10L);                       // long 크기의 정수 리터럴
 printf("%lld\n", -1234567890123456789LL);    // long long 크기의 정수 리터럴

 printf("%u\n", 10U);                         // unsigned int 크기의 정수 리터럴
 printf("%lu\n", 1234567890UL);               // unsigned long 크기의 정수 리터럴

 printf("%lu\n", 10UL);                       // unsigned long 크기의 정수 리터럴
 printf("%llu\n", 1234567890123456789ULL);    // unsigned long long 크기의 정수 리터럴

 printf("0%lo\n", 017L);             // long 크기의 8진 정수 리터럴
 printf("0%lo\n", 017UL);            // unsigned long 크기의 8진 정수 리터럴

 printf("0x%lX\n", 0x7FFFFFL);       // long 크기의 16진 정수 리터럴
 printf("0x%lX\n", 0xFFFFFFFFUL);    // unsigned long 크기의 16진 정수 리터럴

 printf("%f\n", 0.1f);     // 0.100000: float 크기의 실수 리터럴
 printf("%f\n", 0.1F);     // 0.100000: float 크기의 실수 리터럴

 printf("%f\n", 0.1);      // 0.100000: double 크기의 실수 리터럴
 
 printf("%Lf\n", 0.1l);    // 0.100000: long double 크기의 실수 리터럴
 printf("%Lf\n", 0.1L);    // 0.100000: long double 크기의 실수 리터럴

 printf("%f\n", 1.0e-5f);     // 0.000010: float 크기의 실수 리터럴
 printf("%f\n", 1.0e-5F);     // 0.000010: float 크기의 실수 리터럴
 
 printf("%f\n", 1.0e-5);      // 0.000010: double 크기의 실수 리터럴
 
 printf("%Lf\n", 1.0e-5l);    // 0.000010: long double 크기의 실수 리터럴
 printf("%Lf\n", 1.0e-5L);    // 0.000010: long double 크기의 실수 리터럴

 return 0;
}

 

 

 

 

'ComputerEngineering > C/C++' 카테고리의 다른 글

printf  (0) 2016.11.21
전처리기  (0) 2016.08.25
가변 인자  (0) 2016.08.18
동적 할당(C)  (0) 2016.08.17
memory.h  (0) 2016.08.13
time.h (c++의 경우 ctime)  (0) 2016.08.10
trackback 0 And comment 0

전처리기

|

소스파일을 컴파일해 실행하는 과정은 다음과 같다 :

프로그램 작성 -> 전처리 -> 컴파일 -> 링크 -> 실행

컴파일 전에 하는 과정은 전처리라고 하고,이를 수행하는 장치를 전처리기라고 한다. # 문자로 시작하는 문장을 가리켜 전처리기 지시자라고 한다. 대표적으로 헤더 파일을 인클루드 하는 #include가 있다. 이번 포스팅에서는 이 전처리 문장들을 알아보기로 한다.

- 매크로

#define으로 시작되는 전처리 문장을 매크로라고 하며, 매크로는 크게 매크로 상수와 매크로 함수로 나뉘어진다.
첫번째로 매크로 상수의 정의는 다음과 같다 :

#define PI 3.14

PI는 매크로 상수 이름, 3.14는 치환될 값이다. 매크로 상수를 정의하면(참고로 전처리기 지시자 뒤에는 세미콜론을 붙이지 않는다.) 전처리기는 소스 파일에서 PI를 3.14로 인식하고 소스코드에 있는 PI를 3.14로 바꾸고 컴파일러에게 전달한다.
즉, area = PI * radius * radius 라는 문장이 있다면, 전처리기는 이를 area = 3.14 * radius * radius로 바꿔 컴파일러에 전달한다는 뜻이다.

이렇게 매크로 상수를 정의하면 프로그램을 쉽게 수정할 수 있고,큰 숫자들을 직관적인 문자열로 표현할 수 있다. 그리고 변수와 달리 매크로 상수는 별도의 메모리 공간을 필요로 하지 않는다. 보통 매크로 상수는 main 함수가 등장하기 전 최상단에 몰아서 정의한다.

매크로 상수를 해제하는 전처리기 지시자도 있다. #undef는 기존 매크로의 정의를 해제하고 이후부터 치환을 중지한다.

#include <stdio.h>

#define PI 3.14

int main()
{
 printf("%lf\n", PI);
#undef PI
#define PI 3.141592
 printf("%lf\n", PI);
 return 0;
}

예제는 PI를 3.14로 매크로 정의 했다가 해제하고 3.141592로 재정의해서 쓰고 있다.



매크로 "함수"는 함수처럼 인자를 설정할 수 있는 매크로다. 이름은 함수지만, 기능은 단순히 치환하는 것이므로 실제로 함수는 아니다.
매크로 함수의 정의는 다음과 같다 :

#define MUL(a,b) a*b

MUL(a,b)는 매크로 함수의 이름, a*b는 함수의 기능이다.
예를 들어 MUL(3,4)라고 쓴다면, 치환하여 3*4가 되는 식이다. 매크로 함수의 장점은 인자의 자료성을 가리지 않고, 속도가 빠르다. 하지만 매크로 함수의 내부에서 자기 자신을 호출할 수 없고, 한~두줄 정도의 간단한 내용만 매크로 함수로 정의할 수 있다는 점이다.

매크로 함수를 사용할 때 주의할 점은, 매크로 함수는 실제로 함수가 아니라 그저 문장을 치환해준다는 사실이다.


#include <stdio.h>

#define MUL(x,y) x*y
int mul(int x, int y)
{
 return x*y;
}
int main()
{
 int a = 3, b = 4;
 printf("%d\n",MUL(a + 1, b + 1));
 printf("%d\n", mul(a + 1, b + a));

 return 0;
}

위 예제를 보면 MUL은 단순히 치환되기 때문에, 인자로 들어온 a+1,b+1을 x와 y자리에 넣어 a+1 * b+1로 만든다. *가 연산자 우선순위에서 높기 때문에, 사용자가 바라는 (a+1)*(b+1)로는 기능하지 않는다.따라서,이를 고려하여 매크로 함수의 정의를 MUL(x,y) ((x)*(y)) 로 변경해야 한다.

매크로 함수를 정의할 때 사용되는 연산자들은 #, ##, 가 있다.

#는 매크로 함수의 인자를 문자열로 바꿔준다. #define OUTPUT(a) #a라고 기술하고,
printf("%s\n",OUTPUT(13579)); 를 해보면 "문자열로" 13579가 나온다. 즉 # 연산자는 매크로 함수의 인자를 문자열로 치환한다.(참고로 매크로 함수에서 =나 +같은 기호를 문자열로 쓰고 싶다면 " " 안에 넣으면 된다.)

##는 토큰 결합 연산자로서,매크로 함수 안에서 토큰을 결합한다. 토큰이란 문법 분석의 단위( int a = 3; 이라면 int , a , = , 3 , ; 가 모두 토큰이다)로서, 이런 토큰을 분석하는 프로그램을 파서라고 하고 이 파서는 컴파일러 안에 포함되어 있다.파서는 (더 자세히는 프로그래밍 언어론에서 다루지만) 코드의 문법을 파악하기 위해 코드를 토큰 단위로 분리하고 의미를 파악한다. 아무튼, 매크로 함수에서의 사용법은

#define OUTPUT(a,b,c) a ## b ## c 라고 기술하고 printf("%d \n",OUTPUT(a, = ,5)); 라고 매크로 함수를 호출하면
3개의 토큰이 결합하여 a=5라는 문장이 나오게 된다.


C에는 기본적으로 정의되어 있는 매크로 들도 있다. 주로 __XXXX__ 형식으로 되어 있는데 몇개만 소개해보자면,

__FILE__ 현재 소스 코드의 파일 이름을 나타냄.
__LINE__ 현재 위치의 소스 코드의 행 번호를 나타냄.
__DATE__ 현재 소스 코드의 컴파일 날짜를 나타냄.
__TIME__ 현재 소스 코드의 컴파일 시간을 나타냄.



- 조건부 컴파일

조건부 컴파일이란, 매크로 상수의 존재 유무 혹은 매크로 상수의 값을 보고 특정 조건이 만족될 때만 컴파일이 되도록 하는 기법이다.

#if ~ #endif

#if는 if와 비슷하게, 매크로 상수를 비교하는 산술,관계,논리 연산자를 이용해 조건을 확인한다. 차이점이라면 #if는 조건식의 결과에 따라 컴파일을 수행하고, if는 조건식의 결과에 따라 문장을 실행한다.

 

#include <stdio.h>

#define CODE 1
int main()
{
 int a = 3, b = 5;
 int result;
#if(CODE==1)
 result = a + b;
 printf("%d\n", result);
#endif

#if(CODE==2)
 result = a*b;
 printf("%d\n", result);
#endif
 return 0;
}

간단한 예제로 , 매크로 상수 CODE가 1이면 덧셈을, 2면 곱셈을 하는 예제다. #if를 사용하면 꼭 #endif로 조건부 컴파일 구간이 끝났음을 명시해야 한다. CODE가 1인 경우, #if(CODE==2) 이하부터 #endif까지는 컴파일이 되지 않는다.
if와 마찬가지로, #if에서도 else와 else if(단 여기서는 #elif)를 사용할 수 있다.

#if 조건식
1.컴파일 문장
#elif 조건식2
2.컴파일 문장
#else
3.컴파일 문장
#endif

이런식으로.

#if와 비슷한 전처리기 연산자로 #ifdef가 있다. 이 전처리기 연산자는 매크로 상수의 값이 아닌 매크로가 정의되어 있는지를 판단한다. 반대로,
#ifndef는 매크로가 정의되어 있지 않으면 하단을 실행한다. 이 명령어들은 헤더 파일의 중복 검사에 사용되는데, 자세한 설명은
http://mypensieve.tistory.com/85 여기서 다루기로 한다.





 

 

'ComputerEngineering > C/C++' 카테고리의 다른 글

printf  (0) 2016.11.21
전처리기  (0) 2016.08.25
가변 인자  (0) 2016.08.18
동적 할당(C)  (0) 2016.08.17
memory.h  (0) 2016.08.13
time.h (c++의 경우 ctime)  (0) 2016.08.10
trackback 0 And comment 0

가변 인자

|

가변 인자는 함수 인자 수를 고정하지 않고 함수를 호출하는 방법이다.

 

#include <stdio.h>
#include <stdlib.h>

void add(int num, ...)
{
 int *p = &num + 1;

 for (int i = 0; i < num; i++)
  printf("%d\n", p[i]);
}

int main()
{
 int a, b, c;
 a = 10;
 b = 20;
 c = 30;

 add(3, a, b, c);
 
 return 0;
}


함수 add는 인자로 int num을 받고, 그 이후는 ... 으로 쓰여있다. num은 인자의 수를 저장하고, ...은 이 함수가 가변 인자 함수임을 알린다.

호출 시 add(3,a,b,c)는 인자가 3개 있고, 그 인자들이 a,b,c라는 뜻.
함수는 내부에서 포인터 p가 num 이후,즉 num 변수 바로 다음(+1은 int에서 4바이트씩 뛰는 것이므로)인자를 가리킨다.
a,b,c에 저장된 10,20,30은  &num+1,&num+2,&num+3에 각각 복사된다. p는 num+1, 즉 a의 주소를 가리키고 있으므로 반복문은 a부터 num만큼(3) 3개를 출력한다. 


가변 인자 함수는 인자가 여러개일 때, 함수 내부적으로 num의 값을 통해 인자의 개수에 따라 유동적으로 기능을 정할 수 있다는 장점이 있다.





#include <stdio.h>
#include <stdarg.h>

int add(int num, ...)
{
 va_list ptr;
 int sum = 0;
 va_start(ptr, num); // ptr을 초기화 한다. 고정 인자(num) 다음을 가리키게 함.
 // 이 때 두번쨰 인자는 함수에 있는 마지막 고정 인수가 되어야 한다.

 for (int i = 0; i < num; i++)
  sum += va_arg(ptr, int); // 인자 반환 - 반복 호출 시 다음 인자를 반환한다.

 va_end(ptr); // ptr에 NULL 저장.

 return sum;
}
int main()
{
 int sum;
 sum = add(10,1,2,3,4,5,6,7,8,9,10);
 printf("%d", sum);


 return 0;
}

위 예제는 함수의 가변 인자수와 인자를 넣으면, 고정 인자를 제외한 가변 인자들의 합을 구하는 예제다.

가변 인자의 사용을 위해서는 stdarg.h 를 include 해야 한다.
- void va_start(va_list ap,last)
- type va_arg(va_list,type)
- void va_end(va_list ap)

가변 인자를 가리키는 포인터인 va_list 타입 포인터를 정의하고, 그 포인터를 va_start롤 통해 초기화 시켜야 한다.
이 때 꼭 두번째 인자는 함수에 있는 마지막 고정 인수가 되어야 한다.(즉 가변 인자의 개수를 나타내는 값은 마지막 고정 인수가 되어야 함)
va_arg는 포인터가 가리키는 주소에서 두번째 인자 만큼 값을 읽어 반환하고,그 다음 인자로 이동한다.
사용을 마쳤으면 va_end로 포인터를 NULL로 만들어준다.


'ComputerEngineering > C/C++' 카테고리의 다른 글

printf  (0) 2016.11.21
전처리기  (0) 2016.08.25
가변 인자  (0) 2016.08.18
동적 할당(C)  (0) 2016.08.17
memory.h  (0) 2016.08.13
time.h (c++의 경우 ctime)  (0) 2016.08.10
trackback 0 And comment 0
prev | 1 | 2 | 3 | 4 | 5 | 6 | ··· | 42 | next