C语言printf,格式化字符串,缓冲区
前言
用惯了 Python 字符串的 format,感觉 C 的 printf 用起来好别扭,于是写这篇来帮忙记忆一些细节。
然后这是cpp官方文档 (C和C++的printf是一样的)
http://www.cplusplus.com/reference/cstdio/printf/
格式化字符串
printf 常用的format标签:
这里用了为了方便,用了宏函数(不过宏函数是不能滥用的)。
关于宏函数的一些特殊用法,可参考这篇博客:
https://blog.csdn.net/q2519008/article/details/80934815
顺带一提,我用 VSCODE 写C或C++代码时,宏函数也有高亮,很方便。
#include <stdio.h>
#include <stdbool.h>
#include <complex.h>
// 下面这是一个用于输出结果的宏函数
// ... 表示任意数量参数,对应后面的 __VA_ARGS__
// #A 表示把A字符串化,这对__VA_ARGS__也一样
// ##A 表示把A与前面的进行拼接,但是##__VA_ARGS__行为不太一样
// ##__VA_ARGS__ 表示:当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错
// 关于如何把 FORMAT 参数嵌入字符串里,这了用上了字符串拼接的写法,即形如"xx""xx"的写法
#define PRINTF(FORMAT,...) printf("(%s) ---> ("FORMAT")\n",#__VA_ARGS__,##__VA_ARGS__);
int main(){
int intNum = 3;
long longNum = 3L;
long long LLNum = 3LL;
short ShortNum = 3;
char c='3';
unsigned short UShortNum = 3U;
unsigned int UIntNum = 3U;
unsigned long long ULLNum = 3ULL;
float FloatNum = 3.0f;
double DoubleNum = 3.0;
long double LDNum = 3.0;
//C99新类型
_Bool boolVar = true; // 注意,这里的 true 只是一个宏而已(int的1),和C++的true是不同的
long double _Complex LDComplexNum = 3.0+3.0I;// 或者 3+3*I,I是complex.h里的宏
PRINTF("%d",intNum); // 还有一种 %i 的写法,但我没怎么用过
PRINTF("%ld",longNum);
PRINTF("%lld",LLNum);
PRINTF("%hd",ShortNum);
PRINTF("%c",c);
PRINTF("%hu",UShortNum);
PRINTF("%u",UIntNum);
PRINTF("%llu",ULLNum);
PRINTF("%f",FloatNum);
PRINTF("%lf",DoubleNum);
PRINTF("%llf",LDNum);
PRINTF("%llf+%llfi",creall(LDComplexNum),cimagl(LDComplexNum));
PRINTF("%d",boolVar);
PRINTF("%lu",sizeof(_Bool));
return 0;
}
输出:
(intNum) ---> (3)
(longNum) ---> (3)
(LLNum) ---> (3)
(ShortNum) ---> (3)
(c) ---> (3)
(UShortNum) ---> (3)
(UIntNum) ---> (3)
(ULLNum) ---> (3)
(FloatNum) ---> (3.000000)
(DoubleNum) ---> (3.000000)
(LDNum) ---> (3.000000)
(creall(LDComplexNum),cimagl(LDComplexNum)) ---> (3.000000+3.000000i)
(boolVar) ---> (1)
(sizeof(_Bool)) ---> (1)
其他format指示符
%s
这个对应字符串,也很常用。
%p
输出指针指向的地址
其实是"%0[some number]X"
#include <stdio.h>
int main(){
int a=3;
int *b=&a;
printf("%X\n",b);
printf("%p\n",b);
}
结果:
61FE44
000000000061FE44
%a
https://stackoverflow.com/questions/4826842/the-format-specifier-a-for-printf-in-c
The %a formatting specifier is new in C99. It prints the floating-point number in hexadecimal form. This is not something you would use to present numbers to users, but it’s very handy for under-the-hood/technical use cases.
As an example, this code:
printf("pi=%a\n", 3.14);
prints:
pi=0x1.91eb86p+1
The excellent article linked in the comments explains that this should be read “1.91EB86_16 * 2^1
” (that is, the p
is for power-of-two
the floating-point number is raised to). In this case, “1.91EB86_16
” is “1.5700000524520874_10
”. Multiply this by the “2^1
”, and you get "3.140000104904175_10"
.
Note that this also has the useful property of preserving all bits of precision, and presenting them in a robust way.
%n
这个相当特殊,它不会输出任何东西。
这个参数必须对应一个有符号整数的指针(不过我实验过unsigned,也行),它存储它出现之前打印的所有字符数。
类似的,有%hn(short),%hhn(byte)
下面的代码在linux下跑没问题
但是在windows下,似乎有问题:%n无效,而且其后一直到换行符的内容都没了(包括换行符)
我花了一段时间在网上查找该问题的原因(我猜一定是系统或者MinGW的问题)。
果然我找到了:
Debug Assertion when using %n in printf
其实原因很简单:就是微软认为%n不安全,已经不再直接支持了。
解决办法也在上面官网文档里给出来了(_set_printf_count_output),不过我用MinGW试了一下之后报链接错误,这应该是头文件里有定义,但找不到函数体。所以没有成功,我也没有深究。
(顺带一提,java printf的%n表示兼容型换行符;linux换行\n, macOS是\r, windows是\r\n; java为了兼容,就自己搞出来个 %n; https://stackoverflow.com/questions/1883345/whats-up-with-javas-n-in-printf)
//以下代码在deepin中正常运行 (基于Debian的一种linux发行版,是国产的)
# include <stdio.h>
int main(void)
{
int i = 46;
printf("%d\n",i);
//%n的作用:计算%n之前字符数量
//如下面的xxxx,共4个
//将结果放到对应整型指针指向的空间
puts("(test 1)+++++++++++++++++++++++++++++++++");
//举个例子
printf("xxxx%n\n",&i);
printf("%d\n",i);
puts("(test 2)+++++++++++++++++++++++++++++++++");
//转义字符是看成一个字符
printf("x\\%%xxx%n\n",&i);
printf("%d\n",i);
puts("(test 3)+++++++++++++++++++++++++++++++++");
//对拼接起来的字符串当成一个整体来看待
printf("xxx" "xxx" "%n\n",&i);
printf("%d\n",i);
puts("(test 4)+++++++++++++++++++++++++++++++++");
//对非ASCII字符,结果与其编码方式有关
printf("你好%n\n",&i);
printf("%d\n",i);
puts("(test 5)+++++++++++++++++++++++++++++++++");
//如果前面有 format specifier,则计算将他们转化为字符串之后的总长度
printf("x%dxxx%n\n",100,&i);
printf("%d\n",i);
printf("x%dxxx%n\n",1000,&i);
printf("%d\n",i);
return 0;
}
结果:
46
(test 1)+++++++++++++++++++++++++++++++++
xxxx
4
(test 2)+++++++++++++++++++++++++++++++++
x\%xxx
6
(test 3)+++++++++++++++++++++++++++++++++
xxxxxx
6
(test 4)+++++++++++++++++++++++++++++++++
你好
6
(test 5)+++++++++++++++++++++++++++++++++
x100xxx
7
x1000xxx
8
子格式控制符
#include <stdio.h>
#define PRINTF(FORMAT, ...) \
printf("format (\"%s\"): (%s) ---> (" FORMAT ")\n", FORMAT, #__VA_ARGS__, ##__VA_ARGS__);
#define __TEST__(N) printf("\n(__TEST__%3d)\n", (N));
int main()
{
/*
The format specifier can also contain sub-specifiers: flags, width, .precision
and modifiers
*/
int a = 11;
//负号表示左对齐(默认右对齐)
// 3表示要输出的字符的最小数目,不足补空格
__TEST__(1);
PRINTF("%-3d", a);
// 0表示用0而不是空格来补全,经测试这种写法只有右对齐时才有效
__TEST__(2);
PRINTF("[%03d] [%-03d]", a, a);
//*表示一个可以用整数替换的占位符,可以在运行时指定宽度
//经过试验 %0*2d 这样也能生效,参数为1时,变成 %012d
//不过不建议纠结于这种细节,会按照常规用法使用就行
__TEST__(3);
PRINTF("%0*d", 3, a);
//虽然下面这样能运行,但别这样用,可读性太差
//PRINTF("%0*2d", 1, a);
//对正数补加号
//不过对0也生效这点比较坑,估计是根据最高位是否为0判断正负的吧
__TEST__(4);
PRINTF("%+d", a);
PRINTF("[%+d] [%+d]", 0, -0);
PRINTF("[%-+3d] [%+-3d]", a, a);
//%[若干空格][不写或者number]d
//补足空格,但和 %[number]d 不同的是至少会保留一个空格
__TEST__(5);
PRINTF("% d", a);
PRINTF("% d", a);
PRINTF("% 4d",a);
PRINTF("% 2d",a);
//%#[某格式控制符]
// http://www.cplusplus.com/reference/cstdio/printf/
//(1) o,x,X
// the value is preceeded with 0, 0x or 0X respectively for values
// different than zero.
__TEST__(6);
PRINTF("%o", a);
PRINTF("%#o", a);
PRINTF("%x", a);
PRINTF("%#x", a);
//(2) a, A, e, E, f, F, g or G
// it forces the written output to contain a decimal point even if no more
// digits follow. By default, if no digits follow, no decimal point is
// written.
__TEST__(7);
PRINTF("%g", (float)(a));
PRINTF("%#g", (float)(a));
// (%.precision(精度))
//(这个可能是最麻烦的了)
//(1) d, i, o, u, x, X
// precision 指定了要写入的数字的最小位数。
// 如果写入的值短于该数,结果会用前导零来填充。
// 如果写入的值长于该数,结果不会被截断。
// 精度为 0 意味着不写入任何字符。
__TEST__(8);
//%03d 有点像;不同在于如果用%-.3d,左对齐无效
PRINTF("[%.3d] [%-.3d]", a, a);
//(2) e,E,f
// 要在小数点后输出的小数位数,规则是“四舍五入”
// %f 默认情况下其实是%.6f
__TEST__(9);
PRINTF("%.3f", 0.123456);
PRINTF("%.3f", 0.666666);
PRINTF("%.3f", 0.555555);
PRINTF("%.3f", 0.444555);
PRINTF("%.3f", 0.444545);
PRINTF("%08.2f", 33.3333);
//(3) g,G
// 要输出的最大有效位数
__TEST__(10);
PRINTF("%.3g", 31.123456);
PRINTF("%.3g", 1231231.123456);
//(4) s
// 要输出的最大字符数。默认情况下,所有字符都会被输出,直到遇到末尾的空字符。
__TEST__(11);
PRINTF("%.3s", "hahahahahahah");
//(5) c [无影响]
__TEST__(12)
PRINTF("%.123c", 'a');
//(6) If the period is specified without an explicit value for precision, 0 is assumed.
//*******************************
// 关于这点,网上搜到的基本上是下面这句:
// """当未指定任何精度时,默认为 1。如果指定时不带有一个显式值,则假定为 0。"""
// 其实这一句我之前没理解,实验的时候,%.s 那就是默认0啊。
// 直到我到 http://www.cplusplus.com/reference/cstdio/printf/ 看了英文原版。。。
// 前面那条 “默认为1” 根本就没有。。。。。。。。。。。
// 果然还是英文资料比较好啊。。。
//*******************************
__TEST__(13)
PRINTF("%.f", 33.3333);
PRINTF("%.s", "hahahahahahah");
//(6) %.*
// 其实这个和%*d那个用法一样,就不多说了
__TEST__(14);
PRINTF("%.*f", 4, 0.123456);
}
结果:
(__TEST__ 1)
format ("%-3d"): (a) ---> (11 )
(__TEST__ 2)
format ("[%03d] [%-03d]"): (a, a) ---> ([011] [11 ])
(__TEST__ 3)
format ("%0*d"): (3, a) ---> (011)
(__TEST__ 4)
format ("%+d"): (a) ---> (+11)
format ("[%+d] [%+d]"): (0, -0) ---> ([+0] [+0])
format ("[%-+3d] [%+-3d]"): (a, a) ---> ([+11] [+11])
(__TEST__ 5)
format ("% d"): (a) ---> ( 11)
format ("% d"): (a) ---> ( 11)
format ("% 4d"): (a) ---> ( 11)
format ("% 2d"): (a) ---> ( 11)
(__TEST__ 6)
format ("%o"): (a) ---> (13)
format ("%#o"): (a) ---> (013)
format ("%x"): (a) ---> (b)
format ("%#x"): (a) ---> (0xb)
(__TEST__ 7)
format ("%g"): ((float)(a)) ---> (11)
format ("%#g"): ((float)(a)) ---> (11.0000)
(__TEST__ 8)
format ("[%.3d] [%-.3d]"): (a, a) ---> ([011] [011])
(__TEST__ 9)
format ("%.3f"): (0.123456) ---> (0.123)
format ("%.3f"): (0.666666) ---> (0.667)
format ("%.3f"): (0.555555) ---> (0.556)
format ("%.3f"): (0.444555) ---> (0.445)
format ("%.3f"): (0.444545) ---> (0.445)
format ("%08.2f"): (33.3333) ---> (00033.33)
(__TEST__ 10)
format ("%.3g"): (31.123456) ---> (31.1)
format ("%.3g"): (1231231.123456) ---> (1.23e+006)
(__TEST__ 11)
format ("%.3s"): ("hahahahahahah") ---> (hah)
(__TEST__ 12)
format ("%.123c"): ('a') ---> (a)
(__TEST__ 13)
format ("%.f"): (33.3333) ---> (33)
format ("%.s"): ("hahahahahahah") ---> ()
(__TEST__ 14)
format ("%.*f"): (4, 0.123456) ---> (0.1235)
相关库函数
printf // 用于打印信息
scanf // 用于输入(不过用起来和printf的有所区别,我还没深究)
//上两个函数的safe版本,这是c11标准的
//如果用 VS2015 写代码时使用 scanf 可能会提示换成 scanf_s
//如果有理由不能换,则可加 #pragma warning(disable:4996)
printf_s
scanf_s
sprintf // 格式化字符串
fprintf // 写到文件
// https://en.cppreference.com/w/c/io/fprintf
Defined in header <stdio.h>
int printf( const char *format, ... );
(until C99)
int printf( const char *restrict format, ... );
(since C99)
int fprintf( FILE *stream, const char *format, ... );
(until C99)
int fprintf( FILE *restrict stream, const char *restrict format, ... );
(since C99)
int sprintf( char *buffer, const char *format, ... );
(until C99)
int sprintf( char *restrict buffer, const char *restrict format, ... );
(since C99)
int snprintf( char *restrict buffer, size_t bufsz,
const char *restrict format, ... );
(since C99)
int printf_s(const char *restrict format, ...);
(since C11)
int fprintf_s(FILE *restrict stream, const char *restrict format, ...);
(since C11)
int sprintf_s(char *restrict buffer, rsize_t bufsz,
const char *restrict format, ...);
(since C11)
int snprintf_s(char *restrict buffer, rsize_t bufsz,
const char *restrict format, ...);
(since C11)
printf 缓冲区(缓存区)问题
fprintf函数,stdout,stderr,缓冲区
- stdout 是标准输出,stderr 是标准错误
- stdout 是有缓冲区的,stderr 没有缓冲区(为了立即输出错误)
- printf(…) 其实等价于 fprintf(stdout,…),所以是有缓冲区的
- 如果缓冲区不被刷新,那么printf是不会将缓冲区里暂存的内容输出到显示区的
- 一般情况下,stdout和stderr用起来似乎没有区别,但如果在运行期遇到异常时,他们表现出来的差异比较大。如果用stdout,在刷新前遇到异常,程序终止,那不会输出任何东西。另外如果混用两者,则不能保证输出的顺序符合预期。
什么是缓冲区
https://www.cnblogs.com/csdndreamer/p/5490660.html
缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
为什么需要缓冲区
协调低速的IO设备和高速的CPU,使整个系统高效工作。
缓冲区类型
缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。
- 全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
- 行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
- 不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
缓冲区的大小
https://www.jianshu.com/p/bd4fc453215b
如果我们没有自己设置缓冲区的话,系统会默认为标准输入输出设置一个缓冲区,这个缓冲区的大小通常是512个字节的大小。
缓冲区大小由 stdio.h
头文件中的宏 BUFSIZ
定义,如果希望查看它的大小,包含头文件,直接输出它的值即可:printf("%d", BUFSIZ);
缓冲区的大小是可以改变的,也可以将文件关联到自定义的缓冲区,详情可以查看 setvbuf()
和 setbuf()
函数。
缓冲区什么时候会被刷新(清空)
https://www.jianshu.com/p/bd4fc453215b
下列情况会引发缓冲区的刷新:缓冲区满时;行缓冲区遇到回车时;关闭文件;使用特定函数刷新缓冲区。
- 程序正常退出(正常结束),如exit(0),main函数里return 0 .
- 遇到 \n , \r 时会刷新缓冲区.
- 缓冲区满时自动刷新.
- 关闭文件(如果是在文件读写的情景下)
- 手动刷新
- 调用 fflush 标准库函数:
#include <stdio.h>
int fflush(FILE *stream)
- C++ 的 std::endl
- 其实C++的endl有刷新缓冲区的作用,但这篇博客是讲C的,就不多说了。
- C++ unitbuf操纵符
- https://blog.csdn.net/zdplife/article/details/50864690
cout<<unitbuf;
- 调用 fflush 标准库函数:
- 调用某些函数时,会自动刷新缓冲区:
- https://blog.csdn.net/Infect_your_breath/article/details/75944176
- scanf()函数在缓存里面读数据时,会先将缓存区刷新,然后再读取数据。另外getc()、gets()、fgetc()、fgets()这几个读取数据时,也会会先将缓存区刷新,然后再读取数据。当然gets()是不建议使用的,就差被逐出标准了。
getchar, getch, getche 的区别:
- getchar 从缓冲区中读取字符
- getch 非标准库函数,要包含 conio.h。 从键盘读取字符(常用来实现“按任意键继续”),不回显
- getche 非标准库函数,要包含 conio.h。 头文件(unix系列平台的编译器一般不包含这个头文件),与getch唯一的不同:会回显
详情请见下面这篇博客:
https://www.jianshu.com/p/bd4fc453215b
其他
%x、%X、%#x、%#X 的区别
# include <stdio.h>
# define PRINTF(FORMAT,...) printf("format (%s) output: "FORMAT"\n",FORMAT,__VA_ARGS__);
int main(void)
{
int i = 46;
PRINTF("%x",i);
PRINTF("%X",i);
PRINTF("%#x",i);
PRINTF("%#X",i);
return 0;
}
结果
format (%x) output: 2e
format (%X) output: 2E
format (%#x) output: 0x2e
format (%#X) output: 0X2E
关于大写输出 (%X %G %E %A)
如果输出的格式中可能带有字母,想要大写输出就可以用对应的大写字母格式符。
%X %G %E %A
但不是所有格式控制符都有对应大写版本,比如下面这几个就是无效的:
//无效
printf("%D\n",3);
printf("%S\n","asfsdfas");
而这几个是有效的:
//有效
printf("%X\n",333);
printf("%E\n",333.333);
printf("%G\n",333.333);
printf("%A\n",333.333);
/*
输出为:
14D
3.333330E+002
333.333
0X1.4D553FP+8
*/
关于%I64d
和 %lld
//由于64位整数在最初并不是标准的一部分
//导致后来编译器各自实现自己的标准
//这就出现了不兼容的 %I64d 和 %lld
//不过据说后来是都可以了,没实验过(虽然我不信)
如何输出 %d、\ 和双引号
# include <stdio.h>
int main(void)
{
printf("%%d\n"); // 两个百分号输出百分号
printf("\\\n"); // 两个反斜杠表示反斜杠
printf("\"\"\n"); // 使用转义字符的表示双引号
return 0;
}
如果 printf %d 后不提供参数会怎么样?
Behaviour of printf when printing a %d without supplying variable name
#include <stdio.h>
int main() {
/*
下面代码我某次执行结果是这样的:
13384896
726006320
726006320
726006320
726006320
这显然不是随机数这么简单的问题
事实上这时printf的行为与编译器有关
不同编译器下,可能会有不同结果,有的甚至可能会报错
上面这些是printf从stack里取得的垃圾数据
我们不应该让这种情况发生
*/
//(注意,C89 不允许for里int)
for (int i = 0; i < 5; i++) {
// printf %d with out supplying variable...
printf("%d\n");
}
return 0;
}
利用C11特性 _Generic 写的泛型print
源自这篇博客
https://blog.csdn.net/qq_31243065/article/details/80904613
// 注意,我这里用的是新版的gcc (支持C11)
// 如果用其他编译器不能保证编译成功
//
// 说起来我上个星期对C语言的认知还是“古老”
// 这个星期听说了C11后才对C改观
#include <stdio.h>
#include <stdbool.h>
#include <complex.h>
#define PRINT_VAR_NAME(A) printf("%s ---> ",(#A))
#define CUSTOM_GENERIC(A) _Generic((A), \
/*signed char*/ signed char : printf("type signed char, var:%d\n", (A)), \
/*signed short*/ signed short : printf("type signed short, var:%hd\n", (A)), \
/*signed int*/ signed int : printf("type signed int, var:%d\n", (A)), \
/*signed long int */ signed long int : printf("type signed long int, var:%ld\n", (A)), \
/*signed long long int*/ signed long long int : printf("type signed long long int, var:%lld\n", (A)), \
/*unsigned char*/ unsigned char : printf("type unsigned char, var:%c\n", (A)), \
/*unsigned short*/ unsigned short : printf("type unsigned short, var:%hu\n", (A)), \
/*unsigned int*/ unsigned int : printf("type unsigned int, var:%u\n", (A)), \
/*unsigned long int*/ unsigned long int : printf("type unsigned long int, var:%lu\n", (A)), \
/*unsigned long long int*/ unsigned long long int : printf("type unsigned long long int, var:%llu\n", (A)), \
/*float*/ float : printf("type float, var:%f\n", (A)), \
/*double*/ double : printf("type double, var:%lf\n", (A)), \
/*long double*/ long double : printf("type long double, var:%llf\n", (A)), \
/*_Bool*/ _Bool : printf("type _Bool, var:%d\n", (A)), \
/*float _Complex*/ float _Complex : printf("type float _Complex, var:%f+%fi\n", crealf((A)), cimagf((A))), \
/*double _Complex*/ double _Complex : printf("type double _Complex, var:%lf+%lfi\n", creal((A)), cimag((A))), \
/*long double _Complex*/ long double _Complex : printf("type long double _Complex, var:%llf+%llfi\n", creall((A)), cimagl((A))), \
/*default*/ default : printf("type default!\n") \
)
# define MY_PRINT(X) {\
PRINT_VAR_NAME(X);\
CUSTOM_GENERIC(X);\
}
int main(){
int intNum = 3;
long longNum = 3L;
long long LLNum = 3LL;
short ShortNum = 3;
char c='3';
unsigned short UShortNum = 3U;
unsigned int UIntNum = 3U;
unsigned long long ULLNum = 3ULL;
float FloatNum = 3.0f;
double DoubleNum = 3.0;
long double LDNum = 3.0;
//C99新类型
_Bool boolVar = true; // 注意,这里的 true 只是一个宏而已(int的1),和C++的true是不同的
long double _Complex LDComplexNum = 3.0+3.0I;// 或者 3+3×I,I是complex.h里的宏
MY_PRINT(intNum);
MY_PRINT(longNum);
MY_PRINT(LLNum);
MY_PRINT(ShortNum);
MY_PRINT((signed char)(c));
MY_PRINT((unsigned char)(c));
MY_PRINT(c);
MY_PRINT(UShortNum);
MY_PRINT(UIntNum);
MY_PRINT(ULLNum);
MY_PRINT(FloatNum);
MY_PRINT(DoubleNum);
MY_PRINT(LDNum);
MY_PRINT(boolVar);
MY_PRINT(LDComplexNum);
MY_PRINT(sizeof(_Bool));
return 0;
}
输出:
intNum ---> type signed int, var:3
longNum ---> type signed long int, var:3
LLNum ---> type signed long long int, var:3
ShortNum ---> type signed short, var:3
(signed char)(c) ---> type signed char, var:51
(unsigned char)(c) ---> type unsigned char, var:3
c ---> type default!
UShortNum ---> type unsigned short, var:3
UIntNum ---> type unsigned int, var:3
ULLNum ---> type unsigned long long int, var:3
FloatNum ---> type float, var:3.000000
DoubleNum ---> type double, var:3.000000
LDNum ---> type long double, var:3.000000
boolVar ---> type _Bool, var:1
LDComplexNum ---> type long double _Complex, var:3.000000+3.000000i
sizeof(_Bool) ---> type unsigned long int, var:1