printf 끝에 붙어 있는 f는 Formatting의 약자로써, 서식화된 출력을 한다는 뜻입니다.

printf함수는 %로 시작하는 서식과 인수(변수)를 대응시켜 조립(Formatting)하는 과정을 거친 뒤 문자열로 출력합니다.


아래는 서식 표 입니다.

출처 책 : 혼자 연구하는 C/C++( page 86 )에 제가 조금 덧붙였습니다.

서식

의미

출력 타입

설명 

%hd

short integer

unsigned short 

 부호없는 10진 정수 출력

%d 또는 %i

Decimal, Integer

int 

10진 정수 출력

%ld


long

10진 정수 출력

%lld


long long

10진 정수 출력

%lu


unsigned long

부호없는 10진 정수 출력

%I64d


__int64

10진 정수 출력 

%I64u


unsigned __int64

부호없는 10진 정수 출력 

%o

Octal

unsigned int

8진 정수 출력

%x 또는 %X

heXadecimal

unsigned int

16진 정수로 출력.

대문자 X 쓰면 A~F도

대문자 출력

%u

Unsigned

unsigned int

부호없는 10진 정수 출력

%c

Character

char, unsigned char

1개의 문자 출력

%s

String

char*, string, CString

문자열 출력

%f

Float

float

고정 소수점 형식의 실수 출력

%lf


double

고정 소수점 형식의 실수 출력

%e 또는 %E


float, double

부동 소수점 형식의 실수 출력

%g 또는 %G


float, double

%e, %f 중 

더 짧은 형식으로 출력

%p

Pointer

void *

포인터의 번지값 출력

%n



출력된 문자 개수를

포인터에 대입

%%



%문자 자체를 출력


혹시 틀린 부분이 있거나 빠진게 있을 경우, 알려주시면 감사하겠습니다.



출처 : 잡동사니님 블로그


- 1Bytes 변수( char, unsigned char 등 )를 제외한 변수를 초기화 할 때는 0 이외의 값으로 초기화를 하면 안됨

- new, malloc 등을 이용하여 동적으로 배열을 생성하는 변수가 있는 struct, class에서는 memset으로 초기화를 하면 안됨

- CString은 절대 memset으로 초기화를 하면 안됨

- virtual function을 가지고 있는 struct, class에서는 절대 memset으로 초기화를 하면 안됨


memset을 사용할 때 위 4가지 경우만 기억을 하고 있으면 문제없이 동작합니다. 

각각에 대해서 간단하게 살펴보도록 하죠


-1Bytes 변수( char, unsigned char 등 )를 제외한 변수를 초기화 할 때는 0 이외의 값으로 초기화를 하면 안됩니다.

1
2
int n;
memset(&n, 1, sizeof(int));

으로 하면 Byte단위로 처리가 되어 

n = [00000001000000010000000100000001] = 16843009의 원하지 않는 값으로 초기화가 되버립니다. 

따라서 1Byte의 변수를 제외하고는 0으로만 초기화를 하는데 이용해야 합니다.


-new, malloc 등을 이용하여 동적으로 배열을 생성하는 변수가 있는 struct, class에서는 memset으로 초기화를 하면 안됩니다

문제가 되는 경우를 살펴보면,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct A
{
   int i;
   char* c;
};
  
void main()
{
   A a;
   a.c = new char[100];
   memset(&a, 0, sizeof(A));
   if(a.c != NULL)
   {
      delete[] a.c;
      a.c = NULL;
   }
}

여기서 sizeof(A)는 struct member alignment가 어떤 값이든 4(int i) + 4(char* c, address는 4) = 8Bytes가 됩니다. 

그러므로 위의 소스는 동적으로 생성한 변수는 초기화가 되지 못하고, char* c가 NULL로 초기화가 됨으로써, 

이전에 생성한 메모리는 메모리 누수가 발생하게 됩니다.

그러므로 위와 같이 동적으로 생성하는 경우는 아래와 같이 각각을 분리하여 초기화를 하여야 합니다.

1
2
a.i = 0;
memset(a.c, 0, sizeof(char)*100);


-CString은 절대 memset으로 초기화를 하면 안됩니다.

1.2와 같은 경우로 CString은 내부적으로 m_pchData 변수를 동적으로 생성하여 문자열을 저장합니다. 

이 변수에 대한 직접적인 접근은 private로 막혀 있습니다.

그래서 CString, 또는 CString을 member variable을 가지고 있는 

struct, class를 memset을 이용하여 초기화를 하면 안됩니다.

CString을 memset으로 초기화를 하면, 1.2와 같이 메모리 누수뿐만 아니라, run-time error도 발생을 합니다.


-virtual function을 가지고 있는 struct, class에서는 절대 memset으로 초기화를 하면 안됩니다.

여기서 virtual은 run-time에 실행함수를 binding하는 역할을 하는 것입니다.

한번 잊어버렸던 기억을 되살리는 의미로 예제를 살펴보면,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A
{
public:
   void fun() { printf("A::fun() "); }
};
  
class B: public A
{
public:
   void fun() { printf("B::fun() "); }
};
  
void main()
{
   A* a = new B();
   a->fun();
}

위의 경우 “A::fun()”이 출력됩니다.

하지만 상속을 하여 재정의를 한다는 목적은 재정의를 한 함수가 호출되기를 바라기 때문이죠.

이때 아래와 같이 class A만 간단히 변경하여 virtual만 추가를 하면,

1
2
3
4
5
class A
{
public:
   virtual void fun() { printf("A::fun() "); }
};

재정의한 함수가 실행이 되어 “B::fun()”이 출력됩니다. 이는 a→fun();가 실행이 될 때,

이 함수가 virtual이므로 a의 실제 instance(=new B)에 대응하는 실제 함수를 run-time으로 binding 되기 때문입니다.

이정도로만 virtual 동작에 대해서 기억을 되살려보는 것으로 마무리를 하고, 다시 memset으로 넘어오면,

1
2
3
4
5
6
class A
{
   int i;
   char* c;
   void fun();
};

을 sizeof(A)를 하면 4(int i)+4(char*c, address)=8Bytes로 member function은 영향을 주지 않습니다. 

하지만,

1
2
3
4
5
6
class A
{
   int i;
   char* c;
   virtual void fun();
};

의 경우는 다릅니다. 

위의 8Bytes외에 실제 fun()이 binding을 위한 

실행함수 주소를 저장할 공간을 가리키는(VPTR) 4Bytes를 추가적으로 가지므로, 

sizeof(A)는 총 12Bytes가 됩니다.

이때

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A
{
public:
   virtual void fun() { printf("A::fun() "); }
};
  
class B: public A
{
public:
   void fun() { printf("B::fun() "); }
};
  
void main()
{
   A* a = new B();
   memset(a, 0, sizeof(B));
   a->fun();
}

와 같이 memset을 이용하여 초기화를 하면, virtual function이 NULL영역으로 binding이 되어

a→fun();에서 run-time에 알 수 없는 에러가 발생한다는 것을 반드시 기억해야 합니다.

memset은 매우 편리하고, 강력하면서도, 조심해서 사용해야 한다는 것을 명심하세요.



x = i++;


선증가가 아닌 위와 같이 후증가로 코드를 작성하면 

증가 함수가 i의 원래 값에 복사본을 만든 뒤 i를 증가시키고 원래의 값을 반환,

임시 객체의 생성을 야기시켜 속도가 느려진다고 한다.


요즘엔 컴파일러 최적화가 잘 되어 있어 ++i, i++이나 속도 차이가 없다고 한다.






C언어 각 데이터형 범위 입니다.
출처는 MSDN입니다.




Type NameBytesOther NamesRange of Values

int

4

signed

–2,147,483,648 to 2,147,483,647

unsigned int

4

unsigned

0 to 4,294,967,295

__int8

1

char

–128 to 127

unsigned __int8

1

unsigned char

0 to 255

__int16

2

short, short int, signed short int

–32,768 to 32,767

unsigned __int16

2

unsigned short, unsigned short int

0 to 65,535

__int32

4

signed, signed int, int

–2,147,483,648 to 2,147,483,647

unsigned __int32

4

unsigned, unsigned int

0 to 4,294,967,295

__int64

8

long long, signed long long

–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

unsigned __int64

8

unsigned long long

0 to 18,446,744,073,709,551,615

bool

1

none

false or true

char

1

none

–128 to 127 by default

0 to 255 when compiled with /J

signed char

1

none

–128 to 127

unsigned char

1

none

0 to 255

short

2

short int, signed short int

–32,768 to 32,767

unsigned short

2

unsigned short int

0 to 65,535

long

4

long int, signed long int

–2,147,483,648 to 2,147,483,647

unsigned long

4

unsigned long int

0 to 4,294,967,295

long long

8

none (but equivalent to __int64)

–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

unsigned long long

8

none (but equivalent to unsigned __int64)

0 to 18,446,744,073,709,551,615

enum

varies

none

See Remarks.

float

4

none

3.4E +/- 38 (7 digits)

double

8

none

1.7E +/- 308 (15 digits)

long double

same as double

none

same as double

wchar_t

2

__wchar_t

0 to 65,535



+ Recent posts