首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >逐行拆解 C 语言:数据类型、变量

逐行拆解 C 语言:数据类型、变量

作者头像
用户11872857
发布2025-12-17 15:37:58
发布2025-12-17 15:37:58
2300
举报

前言

在编程的世界里,C 语言就像一座坚实的基石,支撑起无数软件和系统的构建。而数据类型与变量,正是这座基石的 “砖瓦” 与 “标尺”—— 它们决定了程序如何存储、解读和操作信息。从简单的整数计数,到字符的显示,再到小数的精确计算,C 语言丰富的数据类型为我们描绘现实世界的数据提供了精准的工具。只有先透彻理解这些数据类型,以及承载它们的变量,我们才能真正掌握 C 语言的编程逻辑,让代码准确地与计算机 “对话”。接下来,就让我们逐行拆解,走进 C 语言数据类型与变量的世界。


目录

一、数据类型的介绍

1.1 字符型

1.2 整型

1.3 浮点型

1.4 布尔类型

二、 数据类型大小

2.1 sizeof操作符

2.2 sizeof测试

2.3 数据类型的取值范围

2.3.1 signed 和 unsigned

三、变量

3.1 变量的创建

3.2 变量的初始化

3.3 变量的分类

3.3.1 全局变量

3.3.2 局部变量

3.3.3 同名变量的优先级

四、算数操作符

4.1 + 和 -:加法与减法

4.2 *:乘法

4.3 /:除法

4.4 %:取模(求余)

五、复制操作符

5.1基本赋值操作符 =

5.1.1 初始化与赋值的区别

5.1.2 连续赋值

5.2 复合赋值操作符

5.2.1 基本使用

5.2.2 常见的复合赋值操作符

六、单目操作符

6.1 自增(++)与自减(--)操作符

6.1.1 前置 ++

6.1.2 后置 ++

6.1.3 前置 --

6.1.4 后置 --

6.2 正号(+)与负号(-)操作符

6.2.1 正号(+)

6.2.2 负号(-)

七、强制类型转换

7.1 强制类型转换的语法形式

7.2 为什么需要强制类型转换?

7.3 强制类型转换的使用示例

7.4 使用建议:非必要不使用

八、printf的使用

8.1 基本用法

8.1.1 简单文本输出

8.1.2 换行输出

8.2 占位符的使用

8.2.1 基本占位符示例

8.2.2 字符串占位符

8.2.3 多个占位符

8.3 占位符列举

8.4 输出格式控制

8.4.1 限定宽度

8.4.2 总是显示正负号

8.4.3 限定小数位数

8.4.4 输出部分字符串

九、scanf的使用

9.1 基本用法

9.2 scanf 的返回值

9.3 常用占位符

9.4 赋值忽略符


一、数据类型的介绍

C语⾔提供了丰富的数据类型来描述⽣活中的各种数据。

使⽤整型类型来描述整数,使⽤字符类型来描述字符,使⽤浮点型类型来描述⼩数。

所谓“类型”,就是相似的数据所拥有的共同特征,比如:年龄是整型,‘c’是字符类型,编译器只有知道了数据的类型,才知道怎么操作数据。

下⾯盘点⼀下C语⾔提供的各种数据类型,本章节主要探讨内置数据类型。

1.1 字符型
代码语言:javascript
复制
char    //字符
[signed] char    //有符号的字符类型
unsigned char    //⽆符号的字符类型
1.2 整型
代码语言:javascript
复制
//短整型
short [int] 
[signed] short [int] //有符号短整型
unsigned short [int] //无符号短整型

//整型
int 
[signed] int //有符号整型
unsigned int //无符号整型

//⻓整型
long [int]
[signed] long [int] //有符号长整型
unsigned long [int] //无符号长整型

//更⻓的整型
//C99中引⼊
long long [int]  //长长整型
[signed] long long [int] //有符号长长整型 
unsigned long long [int] //无符号长长整型
1.3 浮点型
代码语言:javascript
复制
float //单精度浮点型
double //双精度浮点型
long double //扩展精度浮点数类型或者长双精度浮点数类型
1.4 布尔类型

C 语⾔原来并没有为布尔值单独设置⼀个类型,⽽是使⽤整数 0 表⽰假,⾮零值表⽰真。 在 C99 中引⼊了 布尔类型 ,是专⻔表⽰真假的

代码语言:javascript
复制
_Bool bool //布尔类型这两种表示方式都可以

布尔类型的使用得包括头文件#include<stdbool.h>

布尔类型的取值范围是:flase(假)或者true(真)

在C语言中0表示假,非0表示真
代码语言:javascript
复制
#include<stdbool.h>
#include<stdio.h>

//代码演示

int main()
{
    bool flag = true;//初始化布尔类型的变量为真

    if (flag)//为真就执行下面的语句,为假就不执行下面的语句
       printf("i like C\n");

    return 0;
}

二、 数据类型大小

每⼀种数据类型都有⾃⼰的⻓度,使⽤不同的数据类型,能够创建出⻓度不同的变量,变量⻓度的不同,存储的数据范围就有所差异。

2.1 sizeof操作符

sizeof 是⼀个关键字,也是操作符,专⻔是⽤来计算sizeof的操作符数的类型⻓度的,单位是字

节。

sizeof 操作符的操作数可以是类型,也可是变量或者表达式。

代码语言:javascript
复制
sizeof( 类型 )
sizeof 表达式

注意:

1、sizeof 的操作数如果不是类型,是表达式的时候,可以省略掉后边的括号的。

2、sizeof 后边的表达式是不真实参与运算的,根据表达式的类型来得出⼤⼩。

3、sizeof 的计算结果是 size_t 类型的

sizeof 运算符的返回值,C 语⾔只规定是⽆符号整数,并没有规定具体的类型,⽽是留给 系统⾃⼰去决定, sizeof 到底返回什么类型。 不同的系统中,返回值的类型有可能是unsigned int ,也有可能是 unsigned long ,甚⾄是 unsigned long long ,对应的 printf() 占位符分别是 %u 、 %lu 和 %llu 。这样不利于程序的可移植性。 C 语⾔提供了⼀个解决⽅法,创造了⼀个类型别名 size_t (无符号整型),⽤来统⼀表⽰ sizeof 的返回值类型。对应当前系统的 sizeof 的返回值类型,可能是 unsigned int ,也可能是unsigned long long ,总之是正整数。

2.2 sizeof测试

VS2022 X64配置下的输出:

在c语言的标准中规定,long类型长度大于等于int类型的长度,在不同的编译器和系统环境下,他们的具体字节数会有所不同。

sizeof 在代码进⾏编译的时候,就根据表达式的类型确定了,⽽表达式的执⾏却要在程序运⾏期间才能执⾏,在编译期间已经将sizeof处理掉了,所以在运⾏期间就不会执⾏表达式了。

2.3 数据类型的取值范围

2.3.1 signed 和 unsigned

在 C 语言里, signed和unsigned是用来修饰字符型( char)和整型(像 intshortlong等)的关键字,作用是明确这些类型能否表示负数,以及数值的取值范围:

1、signed(有符号)

表示该类型带有正负号,可以存储负数、零和正数。

对于int类型,默认就是有符号的,所以intsigned int是等价的,一般会省略signed不写,但写了也没错,比如:

代码语言:javascript
复制
signed int a; // 等价于 int a;

char为例,signed char的取值范围通常是 -128 ~ 127(不同系统可能有差异,但核心是能表示负数)。

2、unsigned(无符号)

表示该类型不带正负号,只能存储零和正整数。

若要让int等整型只表示非负整数,就得用unsigned声明变量,比如:

代码语言:javascript
复制
unsigned int a;

同样以char为例,unsigned char的取值范围通常是 0 ~ 255,相比signed char,在相同字节长度下,能表示的最大正整数翻了一倍。

另外要注意,char类型默认是否带正负号是由当前系统决定的,它不等同于signed char,可能是signed char,也可能是unsigned char;但int类型默认就是signed int。要是想查看不同数据类型的取值范围极限值,可参考limits.h(整型)和float.h(浮点型)里的相关常量,这样能增强代码的可移植性。

C 语言提供多种数据类型(如 short、int、long、long long 等),每种类型有各自的取值范围。为查看系统上不同数据类型的极限值,可借助limits.h(整型)和float.h(浮点型)头文件。为保证代码可移植性,应尽量使用这些头文件中定义的常量(如SCHAR_MININT_MAXUCHAR_MAX等)来获取各整数类型的极值。

代码语言:javascript
复制
1、 SCHAR_MIN , SCHAR_MAX :signed char 的最⼩值和最⼤值。
2、 SHRT_MIN , SHRT_MAX :short 的最⼩值和最⼤值。
3、 INT_MIN , INT_MAX :int 的最⼩值和最⼤值。
4、 LONG_MIN , LONG_MAX :long 的最⼩值和最⼤值。
5、 LLONG_MIN , LLONG_MAX :long long 的最⼩值和最⼤值。
6、 UCHAR_MAX :unsigned char 的最⼤值。
7、 USHRT_MAX :unsigned short 的最⼤值。
8、 UINT_MAX :unsigned int 的最⼤值。
9、 ULONG_MAX :unsigned long 的最⼤值。
10、ULLONG_MAX :unsigned long long 的最⼤值。

三、变量

在 C 语言里,经常变化的值被称为变量,而不变的值则称为常量。我们通过数据类型来创建变量,不同的数据类型决定了变量能存储的数据种类(如整数、字符、小数等)以及存储范围。

3.1 变量的创建
代码语言:javascript
复制
语法格式:data_type name;

其中,data_type 是数据类型(如 intchardouble 等),name 是变量名。

代码语言:javascript
复制
示例:
整型变量:int age;,用于存储整数,比如年龄。
字符变量:char ch;,用于存储单个字符,比如字母、符号。
浮点型变量:double weight;,用于存储小数,比如体重。
3.2 变量的初始化

变量在创建的时候就赋予一个初始值,这一操作称为初始化。初始化能避免变量因未赋值而出现的随机值问题,让程序更安全、可控。

代码语言:javascript
复制
int age = 18;          // 整型变量age初始化为18
char ch = 'w';         // 字符变量ch初始化为字符'w'
double weight = 48.0;  // 浮点型变量weight初始化为48.0
unsigned int height = 100; // 无符号整型变量height初始化为100
3.3 变量的分类

根据变量定义的位置,C 语言中的变量主要分为全局变量和局部变量

3.3.1 全局变量

定义:在大括号({})外部定义的变量就是全局变量。

作用范围:使用范围更广,在整个工程中,只要通过合适的方式(如 extern 声明),都能使用全局变量。

3.3.2 局部变量

定义:在大括号({})内部定义的变量就是局部变量。

作用范围:使用范围比较局限,只能在自己所在的局部范围内(如某个函数、某个代码块内)使用。

代码语言:javascript
复制
#include <stdio.h>

int global = 2023; // 全局变量,定义在main函数外的大括号外

int main()
{
    int local = 2018; // 局部变量,定义在main函数内的大括号内
    printf("%d\n", local);  // 打印局部变量local,输出2018
    printf("%d\n", global); // 打印全局变量global,输出2023
    return 0;
}

3.3.3 同名变量的优先级

当局部变量和全局变量名字相同时,局部变量优先使用。这是因为局部变量的作用范围更 “近”,编译器会优先识别局部范围内的变量。

代码语言:javascript
复制
#include <stdio.h>

int n = 1000; // 全局变量n

int main()
{
    int n = 10; // 局部变量n,与全局变量同名
    printf("%d\n", n); // 优先使用局部变量,输出10
    return 0;
}

问题:全局变量和局部变量在内存中存储在哪⾥呢?

⼀般我们在学习C/C++语⾔的时候,我们会关注内存中的三个区域:栈区、堆区、静态区。 1. 局部变量是放在内存的栈区 2. 全局变量是放在内存的静态区 3. 堆区是⽤来动态内存管理的(后期会介绍) 其实内存区域的划分会更加细致,以后在操作系统的相关知识的时候会介绍。

四、算数操作符

4.1 + 和 -:加法与减法

+ 用于完成加法运算,- 用于完成减法运算,它们都需要两个操作数,操作数位于操作符两端。

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int x = 4 + 22;
    int y = 61 - 23;
    printf("%d\n", x);
    printf("%d\n", y);
    return 0;
}

在这个例子中,4 + 22 计算出 x 的值为 2661 - 23 计算出 y 的值为 38,最后通过 printf 函数输出这两个结果。

4.2 *:乘法

* 操作符用于完成乘法运算。

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int num = 5;
    printf("%d\n", num * num); // 输出 25
    return 0;
}

这里 num * num 就是计算 5 乘以 5,结果为 25,然后输出该结果。

4.3 /:除法

/ 操作符用于完成除法运算。需要注意的是,如果除号两端都是整数,执行的是整数除法,得到的结果也是整数。

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    float x = 6 / 4;
    int y = 6 / 4;
    printf("%f\n", x); // 输出 1.000000
    printf("%d\n", y); // 输出 1
    return 0;
}

在这个例子中,虽然 xfloat 类型,但 6 / 4 因为两端都是整数,所以先进行整数除法,结果为 1,再赋值给 x,所以 x 的值是 1.0yint 类型,直接存储整数除法的结果 1

再看一个容易出错的例子:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int score = 5;
    score = (score / 20) * 100;
    // 这里 score / 20 是整数除法,结果为 0,所以乘以 100 后 score 还是 0
    return 0;
}

如果想要得到预想的结果,比如希望 5 / 20 能得到小数,进而计算出正确的比例,就需要将除数 20 改成 20.0,让整除变成浮点数除法,如下:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int score = 5;
    score = (score / 20.0) * 100;
    return 0;
}
4.4 %:取模(求余)

% 操作符表示求模(余)运算,即返回两个整数相除的余值。这个运算符只能用于整数,不能用于浮点数。负数求模的规则是,结果的正负号由第一个运算数的正负号决定。

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int x = 6 % 4; // 2
    return 0;
}

这里 6 % 4 计算的是 6 除以 4 的余数,结果为 2

再看负数求模的情况:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    printf("%d\n", 11 % -5);  // 1
    printf("%d\n", -11 % -5); // -1
    printf("%d\n", -11 % 5);  // -1
    return 0;
}

在这些例子中,第一个运算数的正负号(11-11)决定了结果的正负号。

五、复制操作符

5.1基本赋值操作符 =

5.1.1 初始化与赋值的区别

在变量创建的时候给一个初始值叫初始化,而在变量创建好后,再给一个值,这叫赋值。

代码语言:javascript
复制
int a = 100; // 初始化,创建变量a并赋予初始值100
a = 200;     // 赋值,变量a已创建,重新赋予值200

5.1.2 连续赋值

赋值操作符也可以连续赋值,赋值顺序是从右向左依次进行。

代码语言:javascript
复制
int a = 3;
int b = 5;
int c = 0;
c = b = a + 3; // 连续赋值,从右向左依次赋值

在这个例子中,先计算a + 3(结果为6),然后将6赋值给b,再将b的值(6)赋值给c。这样的写法在调试时,每一次赋值的细节都可以很方便地观察。

5.2 复合赋值操作符

在写代码时,我们经常会对一个变量进行自增、自减等操作。C 语言提供了复合赋值操作符,让这类操作的代码编写更加简洁方便。

5.2.1 基本使用

比如,我们要对变量a进行加3、减2的操作,普通写法如下:

代码语言:javascript
复制
int a = 10;
a = a + 3;
a = a - 2;

而使用复合赋值操作符后,代码可以更简洁:

代码语言:javascript
复制
int a = 10;
a += 3; // 等价于a = a + 3
a -= 2; // 等价于a = a - 2

5.2.2 常见的复合赋值操作符

C 语言中提供了多种复合赋值操作符,方便我们编写代码,常见的有:

+=-=

*=/=%=

还有一些涉及位操作的复合赋值操作符,如>>=<<=&=|=^=(这些会在后续的位操作相关内容中讲解)。

六、单目操作符

6.1 自增(++)与自减(--)操作符

++是自增操作符,能让变量的值加 1;--是自减操作符,可使变量的值减 1。它们又都分为前置和后置两种形式,不同形式的执行顺序有所区别。

6.1.1 前置 ++

前置++是先将变量的值加 1,然后再使用该变量的值。

代码语言:javascript
复制
int a = 10;
int b = ++a; // ++的操作数是a,且在a前面,为前置++
printf("a=%d b=%d\n", a, b);

计算口诀:先 + 1,后使用。a原本是10,先执行a = a + 1a变成11,然后将a的值赋给b,所以b也为11,最终ab都是11,等价于以下代码:

代码语言:javascript
复制
int a = 10;
a = a + 1;
b = a;
printf("a=%d b=%d\n", a, b);

6.1.2 后置 ++

后置++是先使用变量当前的值,然后再将变量的值加 1。

代码语言:javascript
复制
int a = 10;
int b = a++; // ++的操作数是a,且在a后面,为后置++
printf("a=%d b=%d\n", a, b);

计算口诀:先使用,后 + 1。a原本是10,先将a的值赋给bb得到10,然后执行a = a + 1a变成11,所以最终a11b10,等价于以下代码:

代码语言:javascript
复制
int a = 10;
int b = a;
a = a + 1;
printf("a=%d b=%d\n", a, b);

6.1.3 前置 --

前置--的逻辑与前置++类似,只是将加 1 操作换成了减 1 操作。

计算口诀:先 - 1,后使用。

代码语言:javascript
复制
int a = 10;
int b = --a; // --的操作数是a,且在a前面,为前置--
printf("a=%d b=%d\n", a, b); // 输出的结果是:9 9

6.1.4 后置 --

后置--的逻辑与后置++类似,只是将加 1 操作换成了减 1 操作。

计算口诀:先使用,后 - 1。

代码语言:javascript
复制
int a = 10;
int b = a--; // --的操作数是a,且在a后面,为后置--
printf("a=%d b=%d\n", a, b); // 输出的结果是:9 10
6.2 正号(+)与负号(-)操作符

这里的+是正号,-是负号,它们也属于单目操作符。

6.2.1 正号(+)

正号操作符对值的正负没有影响,是一个完全可以省略的操作符,但写了也不会报错。

代码语言:javascript
复制
int a = +10; 等价于 int a = 10;

6.2.2 负号(-)

负号操作符用来改变一个值的正负号,负数前面加上-会得到正数,正数前面加上-会得到负数。

代码语言:javascript
复制
int a = 10;
int b = -a;
int c = -10;
printf("b=%d c=%d\n", b, c); // 这里的b和c都是-10

int a = -10;
int b = -a;
printf("b=%d\n", b); // 这里的b是10

掌握单目操作符的这些用法,能让你在 C 语言编程中更灵活地处理变量的值,为编写更复杂的逻辑打下基础。

七、强制类型转换

在 C++ 编程中,类型是对数据的一种约束,不同类型的数据在内存中的存储方式、取值范围等都有所不同。但有时候,我们需要把一种类型的数据 “强行” 转换成另一种类型,这就是强制类型转换。

7.1 强制类型转换的语法形式

强制类型转换的语法很简单,形式为:

代码语言:javascript
复制
(类型)表达式

或者也可以用 static_cast<类型>(表达式) 的形式(不过这里先聚焦最基础的 C 风格强制转换)。

7.2 为什么需要强制类型转换?

举个例子,看下面的代码:

代码语言:javascript
复制
int a = 3.14;

这里,aint 类型(整数类型),而 3.14double 类型(浮点数类型)。因为两种类型不一致,编译器会给出警告,提示我们可能存在潜在的问题。

为了消除这种警告,让代码更 “合规”,我们就可以使用强制类型转换。

7.3 强制类型转换的使用示例

还是上面的场景,我们可以这样写:

代码语言:javascript
复制
int a = (int)3.14; // 将 3.14 强制类型转换为 int 类型

当执行 (int)3.14 时,C语言会把 3.14 这个浮点数的小数部分直接截断,只保留整数部分,所以最终 a 的值就是 3

7.4 使用建议:非必要不使用

俗话说,“强扭的瓜不甜”,强制类型转换也是如此。它是在万不得已的情况下才使用的操作。

因为强制类型转换可能会带来一些问题:

  • 数据精度丢失:就像上面的例子,浮点数转整数会丢失小数部分。如果是大整数转小整数(比如 longint,且数值超过 int 范围),还可能导致数据溢出,得到错误的结果。
  • 破坏类型安全性:C语言 是强类型语言,类型系统能帮我们避免很多错误。强制类型转换相当于 “绕开” 了类型系统的检查,可能让一些不符合逻辑的操作得以执行。

所以,如果不需要强制类型转换就能实现代码逻辑,那自然是更好的选择。只有当确实需要改变数据类型,且清楚知道转换后不会出问题(或者愿意承担潜在风险)时,再使用强制类型转换。

【总结】 强制类型转换是 C语言 中一种特殊的操作,能让我们把一种类型的数据转换成另一种类型。它语法简单,但使用时要谨慎,尽量在必要时才去 “强扭” 类型~

八、printf的使用

在 C 语言中,printf 函数是用于向标准输出(通常是屏幕)打印格式化数据的核心函数。它能让我们以灵活、可控的方式展示文本和变量内容,是控制台程序与用户交互、输出信息的重要工具。

8.1 基本用法

printf 的核心作用是将参数文本输出到屏幕,其名称中的 f 代表 format(格式化),意味着我们可以定制输出文本的格式。

要使用 printf,需先在源代码头部引入标准输入输出头文件 <stdio.h>

8.1.1 简单文本输出

最基础的用法是直接输出一段文本,示例代码如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("Hello World");
    return 0;
}

上述代码会在屏幕上输出一行文字 "Hello World"。需要注意的是,printf 不会在末尾自动添加换行符,运行结束后,光标会停在输出结束的地方,不会自动换行。

8.1.2 换行输出

为了让光标移到下一行的开头,可以在输出文本的结尾添加换行符 \n,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("Hello World\n");
    return 0;
}

如果文本内部需要换行,也可以通过插入 \n 来实现,比如:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("Hello\nWorld!\n");
    printf("你好\n");
    printf("世界\n");
    return 0;
}
8.2 占位符的使用

printf 可以在输出文本中指定占位符,所谓 “占位符”,就是这个位置可以用其他值代入。

8.2.1 基本占位符示例

以输出 "There are 3 apples" 为例,代码如下

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("There are %d apples\n", 3);
    return 0;
}

在输出文本 "There are %d apples\n" 里,%d 就是占位符,表示这个位置要用其他值来替换。占位符的第一个字符一定为百分号 %,第二个字符表示占位符的类型,%d 表示这里代入的值必须是一个整数。printf 的第二个参数就是替换占位符的值,上面的例子中是整数 3 替换 %d,执行后的输出结果就是 "There are 3 apples"

8.2.2 字符串占位符

常用的占位符除了 %d,还有 %s,它表示代入的是字符串。示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%s will come tonight\n", "zhangsan");
    return 0;
}

%s 表示代入的是一个字符串,所以 printf 的第二个参数必须是字符串,这个例子中是 "zhangsan",执行后的输出就是 "zhangsan will come tonight"

8.2.3 多个占位符

输出文本里可以使用多个占位符,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%s says it is %d o'clock\n", "lisi", 21);
    return 0;
}

输出文本 "%s says it is %d o'clock" 有两个占位符,第一个是字符串占位符 %s,第二个是整数占位符 %d,分别对应 printf 的第二个参数("lisi")和第三个参数(21),执行后的输出是 "lisi says it is 21 o'clock"

需要注意的是,printf 参数与占位符是一一对应关系,如果有 n 个占位符,printf 的参数就应该有 n + 1 个(第一个是格式字符串)。如果参数个数少于对应的占位符,printf 可能会输出内存中的任意值,造成错误。

8.3 占位符列举

printf 的占位符有许多种类,与 C 语言的数据类型相对应。下面列出常用的占位符,方便查找:

  • %a:十六进制浮点数,字母输出为小写。
  • %A:十六进制浮点数,字母输出为大写。
  • %c:字符。
  • %d:十进制整数(对应 int 类型)。
  • %e:使用科学计数法的浮点数,指数部分的 e 为小写。
  • %E:使用科学计数法的浮点数,指数部分的 E 为大写。
  • %i:整数,基本等同于 %d
  • %f:小数(包含 float 类型和 double 类型)。
  • %g:6 个有效数字的浮点数。整数部分一旦超过 6 位,就会自动转为科学计数法,指数部分的 e 为小写。
  • %G:等同于 %g,唯一的区别是指数部分的 E 为大写。
  • %hd:十进制 short int 类型。
  • %ho:八进制 short int 类型。
  • %hx:十六进制 short int 类型。
  • %huunsigned short int 类型。
  • %ld:十进制 long int 类型。
  • %lo:八进制 long int 类型。
  • %lx:十六进制 long int 类型。
  • %luunsigned long int 类型。
  • %lld:十进制 long long int 类型。
  • %llo:八进制 long long int 类型。
  • %llx:十六进制 long long int 类型。
  • %lluunsigned long long int 类型。
  • %LE:科学计数法表示的 long double 类型浮点数。
  • %LFlong double 类型浮点数。
  • %n:已输出的字符串数量。该占位符本身不输出,只将值存储在指定变量之中。
  • %o:八进制整数。
  • %p:指针(用来打印地址)。
  • %s:字符串。
  • %u:无符号整数(unsigned int)。
  • %x:十六进制整数。
  • %zdsize_t 类型。
  • %%:输出一个百分号。
8.4 输出格式控制

printf 可以定制占位符的输出格式,让输出更加规整、符合需求。

8.4.1 限定宽度

printf 允许限定占位符的最小宽度。示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%5d\n", 123); // 输出为 "  123"
    return 0;
}

%5d 表示这个占位符的宽度至少为 5 位。如果不满 5 位,对应的值的前面会添加空格。输出的值默认右对齐,即输出内容前面会有空格;如果希望改成左对齐,可以在占位符的 % 后面插入一个 - 号,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%-5d\n", 123); // 输出为 "123  "
    return 0;
}

输出内容 123 的后面添加了空格。对于小数,这个限定符会限制所有数字的最小整数宽度,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%12f\n", 123.45);
    return 0;
}

%12f 表示输出的浮点数最少要占 12 位。由于小数的默认显示精度是小数点后 6 位,所以 123.45 输出结果的头部会添加 2 个空格。

8.4.2 总是显示正负号

默认情况下,printf 不对正数显示 + 号,只对负数显示 - 号。如果想让正数也输出 + 号,可以在占位符的 % 后面加一个 +,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%+d\n", 12); // 输出 +12
    printf("%+d\n", -12); // 输出 -12
    return 0;
}

%+d 可以确保输出的数值,总是带有正负号。

8.4.3 限定小数位数

输出小数时,有时希望限定小数的位数。例如,希望小数点后面只保留两位,占位符可以写成 %.2f,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("Number is %.2f\n", 8.5);
    return 0;
}

如果希望小数点后面输出 3 位(如 8.500),占位符就要写成 %.3f。这种写法可以与限定宽度占位符结合使用,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%6.2f\n", 8.5);
    return 0;
}

%6.2f 表示输出字符串最小宽度为 6,小数位数为 2。所以,输出字符串的头部有两个空格。最小宽度和小数位数这两个限定值,都可以用 * 代替,通过 printf 的参数传入,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%*.*f\n", 6, 2, 8.5);
    // 等同于 printf("%6.2f\n", 8.5);
    return 0;
}

%*.*f 的两个星号通过 printf 的两个参数 62 传入。

8.4.4 输出部分字符串

%s 占位符用来输出字符串,默认是全部输出。如果只想输出开头的部分,可以用 %[m]s 指定输出的长度,其中 [m] 代表一个数字,表示所要输出的长度,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    printf("%.5s\n", "hello world");
    return 0;
}

占位符 %.5s 表示只输出字符串 "hello world" 的前 5 个字符,即 "hello"

通过对 printf 各种功能的灵活运用,我们能精准控制输出的格式,让程序的输出更加清晰、专业,便于阅读和调试。

九、scanf的使用

在 C 语言编程中,当我们需要从键盘获取用户输入的数据时,scanf 函数就派上了大用场。它能让程序与用户进行交互,接收用户输入的各种类型的数据,再配合 printf 函数输出,构建出更灵活的程序。

9.1 基本用法

scanf 函数用于读取用户的键盘输入。程序运行到 scanf 语句时,会暂停执行,等待用户从键盘输入数据。当用户输入数据并按下回车键后,scanf 会处理用户的输入,并将其存入指定的变量中。

scanf 的原型定义在头文件 <stdio.h> 中,它的语法和 printf 类似。例如:

代码语言:javascript
复制
scanf("%d", &i);

它的第一个参数是一个格式字符串,里面放置占位符(与 printf 的占位符基本一致),用于告诉编译器如何解读用户的输入,明确需要提取的数据类型。这是因为 C 语言的数据都是有类型的,scanf 必须提前知道用户输入的数据类型,才能正确处理数据。

它的其余参数就是存放用户输入的变量,格式字符串里面有多少个占位符,就需要有多少个变量。上面的例子中,scanf 的第一个参数 %d 表示用户输入的应该是一个整数,% 是占位符的标志,d 表示整数;第二个参数 &i 表示将用户从键盘输入的整数存入变量 i

需要注意的是,变量前面必须加上 & 运算符(指针变量除外),因为 scanf 传递的不是值,而是地址,这样才能将输入的值正确存入变量对应的内存位置。

scanf 还可以连续处理多个占位符,示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    int x;
    float y;
    // 用户输入类似“-13.45e12# 0”这样的内容
    scanf("%d%f", &x, &y);
    return 0;
}

在这个例子中,%d 占位符会忽略起首的空格,从有效字符开始获取数据,读取到不符合整数有效字符的地方停止;然后下一个 %f 占位符会接着从上次停止的地方继续读取符合浮点数(这里是科学计数法格式)的内容。

9.2 scanf 的返回值

scanf 的返回值用于指示成功读取的数据项数,另外,EOFend of file,文件结束标志)也会影响返回值。

看下面的示例代码:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    int a = 0;
    int b = 0;
    float f = 0.0f;
    int r = scanf("%d %d %f", &a, &b, &f);
    printf("a=%d b=%d f=%f\n", a, b, f);
    printf("r = %d\n", r);
    return 0;
}
  • 当输入 1 2 3.14 时,a 被赋值为 1b 被赋值为 2f 被赋值为 3.140000scanf 成功读取了 3 个数据项,所以返回值 r = 3
  • 如果输入 2 个数后,按 Ctrl + z 提前结束输入(比如输入 1 2 后按 Ctrl + z),在 VS 环境中,a1b2f0.000000scanf 成功读取了 2 个数据项,返回值 r = 2
  • 如果一个数字都不输入,直接按 3 次 Ctrl + zab0f0.000000,此时 scanf 返回 EOF(即 -1)。
9.3 常用占位符

scanf 常用的占位符如下,与 printf 的占位符基本一致:

  • %c:字符。
  • %d:整数。
  • %ffloat 类型浮点数。
  • %lfdouble 类型浮点数。
  • %Lflong double 类型浮点数。
  • %s:字符串。

需要注意的是,上面所有占位符中,除了 %c 以外,都会自动忽略起首的空白字符;%c 不忽略空白字符,总是返回当前第一个字符,无论该字符是否为空格。如果要强制跳过字符前的空白字符,可以写成 scanf(" %c", &ch),即 %c 前加上一个空格,表示跳过零个或多个空白字符。

对于 %s 占位符,它的规则是从当前第一个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。因为 %s 不会包含空白字符,所以无法用来读取多个单词,除非多个 %s 一起使用。这也意味着,scanf 不适合读取可能包含空格的字符串,比如名词或歌曲名。另外,scanf 遇到 %s 占位符,会在字符串变量末尾存储一个空字符 \0

由于 scanf 将字符串读入字符数组时,不检测字符是否超过了数组长度,很可能会导致数组溢出。为了防止这种情况,使用 %s 占位符时,应该指定读入字符的最长长度,即写成 %[m]s,其中 [m] 是一个整数,表示读取字符串的最大长度,后面的字符将被丢弃,这样就不会有数组溢出的风险了。示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    char name[11];
    scanf("%10s", name);
    printf("%s\n", name);
    return 0;
}

上面的例子中,name 是一个长度为 11 的字符数组,scanf 的占位符 %10s 表示最多读取用户输入的 10 个字符,后面的字符将被丢弃,这样就不会有数组溢出的风险了。

9.4 赋值忽略符

有时,用户的输入可能不符合预定的格式。例如,我们希望用户输入类似 2020-01-01 这样的日期格式来读取年、月、日,但用户可能输入其他格式,比如 2020/01/01,这种情况下,scanf 解析数据就会失败。

为了避免这种情况,scanf 提供了一个赋值忽略符(assignment suppression character)*。只要把 * 加在任何占位符的百分号后面,该占位符就不会返回值,解析后将被丢弃。示例如下:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    int year = 0;
    int month = 0;
    int day = 0;
    scanf("%d-%d-%d", &year, &month, &day);
    printf("%d %d %d\n", year, month, day);
    return 0;
}

上面的例子中,如果用户输入 2020-01-01,能正确解读出年、月、日;但如果输入 2020/01/01,就会解析失败。使用赋值忽略符后:

代码语言:javascript
复制
#include <stdio.h>
int main() {
    int year = 0;
    int month = 0;
    int day = 0;
    scanf("%d%*c%d%*c%d", &year, &month, &day);
    printf("%d %d %d\n", year, month, day);
    return 0;
}

这里的 %*c 就是在占位符的百分号后面加入了赋值忽略符 *,表示这个占位符没有对应的变量,解析后不赋值。这样,无论是 - 还是 / 作为分隔符,都能正确读取年、月、日的值了。

scanf 函数为我们从键盘获取输入提供了灵活的方式,但在使用时要注意占位符的匹配、变量地址的传递以及各种特殊情况的处理,这样才能确保程序正确获取用户输入的数据。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、数据类型的介绍
    • 1.1 字符型
    • 1.2 整型
    • 1.3 浮点型
    • 1.4 布尔类型
  • 二、 数据类型大小
    • 2.1 sizeof操作符
    • 2.2 sizeof测试
    • 2.3 数据类型的取值范围
  • 三、变量
    • 3.1 变量的创建
    • 3.2 变量的初始化
    • 3.3 变量的分类
  • 四、算数操作符
    • 4.1 + 和 -:加法与减法
    • 4.2 *:乘法
    • 4.3 /:除法
    • 4.4 %:取模(求余)
  • 五、复制操作符
    • 5.1基本赋值操作符 =
    • 5.2 复合赋值操作符
  • 六、单目操作符
    • 6.1 自增(++)与自减(--)操作符
    • 6.2 正号(+)与负号(-)操作符
  • 七、强制类型转换
    • 7.1 强制类型转换的语法形式
    • 7.2 为什么需要强制类型转换?
    • 7.3 强制类型转换的使用示例
    • 7.4 使用建议:非必要不使用
  • 八、printf的使用
    • 8.1 基本用法
    • 8.2 占位符的使用
    • 8.3 占位符列举
    • 8.4 输出格式控制
  • 九、scanf的使用
    • 9.1 基本用法
    • 9.2 scanf 的返回值
    • 9.3 常用占位符
    • 9.4 赋值忽略符
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档