Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

基础

Hello World

老规矩我们还是先来看一下在C语言里是怎么写 Hello World 的:

1
2
3
4
5
6
#include <stdio.h>

int main() {
printf("Hello, World!\n");
return 0;
}

其中:

  • #include <stdio.h>:引入标准输入输出库,printf 就在这里面定义。

    include的部分一般被叫做头文件。

  • int main():程序的主函数,是程序执行的起点。

  • printf("Hello, World!\n");:向屏幕输出 Hello, World!\n 表示换行。

  • return 0;:表示程序正常结束。

printf()

printf 是 C 标准库函数,用于格式化文本并将其输出到标准输出。

它的语法相比于其他的编程语言(比如说Python,C++之类的)会复杂很多。

printf的参数主要分成2部分:格式化字符串以及变量名。其中格式化字符串( format string)可以参考以下定义(我个人觉得这个定义比较清楚):

The format string is a character string which contains two types of objects: plain characters, which are simply copied to the output channel, and conversion specifications, each of which causes conversion and printing of arguments.

(来源:https://ocaml.org/manual/5.0/api/Printf.html)

我们来分情况看一下printf的具体语法:

1. 只有字符串的情况:

1
2
3
4
5
6
# include <stdio.h>
int main(void)
{
printf("Hello World!\n"); // \n表示换行
return 0;
}

这种情况下就非常简单,直接在引号里输入希望输出的内容即可。

2. 涉及变量的情况:

1
2
3
4
5
6
7
# include <stdio.h>
int main(void)
{
int i = 10;
printf("%d\n", i); // %d是输出控制符,d 表示十进制,后面的 i 是输出参数*
return 0;
}

我们需要在格式化字符串( format string)里给定符合要求的格式化。

格式化占位符的语法如下:

1
%[parameter][flags][field width][.precision][length]type

- Parameter:指定用于格式化的参数位置(从1开始)

字符 说明
n$ 其中n是参数位置

例子:

1
2
printf("%2$d %1$d", 11, 22);
// 会输出 22 11

- Flags

标志 说明
- 左对齐(默认是右对齐)
+ 总是显示正号或负号(例如 +10)
(空格) 正数前加空格,负数前加负号
0 用0填充未占满的宽度
# 对于%o%x%X等,添加前缀(如0x);对于%f等,始终包含小数点

- Field Width:指定最小输出字符数,不足时用空格(或0)填充,如果要使用变量指定宽度,可以用 *

例子:

1
2
3
4
5
printf("%d", 42);
// 会输出 " 42" (前面有3个空格)

printf("%*d", 5, 42);
// 会输出 " 42" (前面有3个空格)

- Precision:指定数字小数点后的位数或字符串的最大输出长度:

  • 对于浮点数(如 %f):表示小数点后保留的位数,如 %.2f
  • 对于字符串(如 %s):表示最大输出字符数,如 %.5s
  • 可以使用 * 表示由参数动态提供

- Length:指出浮点型参数或整型参数的长度

修饰符 说明
hh signed charunsigned char
h shortunsigned short
l longunsigned long
ll long longunsigned long long
L long double(用于%Lf
z size_t
t ptrdiff_t
j intmax_tuintmax_t

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>

int main() {
signed char a = -5;
printf("%hhd\n", a);
// 会输出 "-5"

short s = 32000;
printf("%hd\n", s);
// 会输出 "32000"

long l = 123456789L;
printf("%ld\n", l);
// 会输出 "123456789"

long long ll = 9223372036854775807LL;
printf("%lld\n", ll);
// 会输出 "9223372036854775807"

long double ld = 3.141592653589793238L;
printf("%Lf\n", ld);
// 会输出 "3.141593"(默认保留6位小数)

size_t sz = 100;
printf("%zu\n", sz);
// 会输出 "100"

ptrdiff_t diff = -8;
printf("%td\n", diff);
// 会输出 "-8"

intmax_t im = 9223372036854775807;
printf("%jd\n", im);
// 会输出 "9223372036854775807"

return 0;
}

- Type:也称转换说明(conversion specification/specifier),指定具体的数据类型,有以下选择

字符 说明
%d 打印十进制整数(int)
%f 打印浮点数(float/double)
%.2f 打印浮点数,保留小数点后2位
%s 打印字符串(char*)
%c 打印单个字符(char)
%x 打印十六进制(小写)
%% 输出一个百分号 %

其中只有Type是必须要给的,其他均可以省略。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int main() {
int i = 123;
float pi = 3.14159;
char letter = 'A';
char name[] = "hello";
int hex = 255;

printf("整数:%d\n", i);
printf("浮点数(默认):%f\n", pi);
printf("浮点数(保留两位):%.2f\n", pi);
printf("字符串:%s\n", name);
printf("字符:%c\n", letter);
printf("十六进制:%x\n", hex);
printf("百分号:%%\n");

return 0;
}

注意:在第二部分一定要给定变量,如果没有给,则会从错误的内存地址读取数据,导致不可预期的行为。

此外还有一个比较特殊的格式符:%n 。这个格式符会让 printf 把当前已经打印的字符数量写入 n 所在的地址。

比如说下面这个例子

1
2
3
4
5
6
7
8
#include <stdio.h>

int main() {
int n;
printf("hello%n", &n);

return 0;
}

n的值会被存储为5。

由于它的特殊性以及危险性,很多现代系统在 libc 中禁用了 %n,或者在格式化函数上增加了保护(如 glibc 中对 %n 的格式检查)。