C语言数据类型和变量

# C语言数据类型和变量

# 数据类型介绍

        C语⾔提供了丰富的数据类型来描述⽣活中的各种数据。使⽤整型类型来描述整数,使⽤字符类型来描述字符,使⽤浮点型类型来描述⼩数。所谓“类型”,就是相似的数据所拥有的共同特征,编译器只有知道了数据的类型,才知道怎么操作数据。

        下面盘点了C语言提供的各种数据类型,本次主要探讨内置数据类型。

1.1  字符型

1 char     //charactor

2 [signed] char     //有符号的

3 unsigned char   //无符号的

1.2 整型

1 //短整型

2 short  [int]

3 [signed] short [int]

4 unsigned short [int]

5

6/ /整型

7 int
8 [ signed ] int
9 unsigned int
10 // ⻓整型
11 long [ int ]
12 [ signed ] long [ int ]
13 unsigned long [ int ]
14 // 更⻓的整型
15  //C99 中引⼊
16 long long [ int ]
17 [ signed ] long long [ int ]
18 unsigned long long [ int ]

1.3 浮点型

1 float   //单精度浮点数

2 double   //双精度浮点数

3 long double    //更长的双精度浮点数    c99

1.4  布尔类型

        C语言原来并没有为布尔类型单独设置一个类型,而是使用整数0表示假,非零表示真。

在C99中也引入了布尔类型,是专门表示真假的。

1 _Bool

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

        布尔类型变量的取值是:true 或者 false。

2 #define bool _Bool

3 #define false 0

4 #define true 1

代码展示为:

#include<stdio.h>
#include<stdbool.h>
int main()
{_Bool flag = true;if (flag)printf("i like C\n");return 0;
}

1.5 各种数据类型的长度

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

1.5.1  sizeof操作符

        sizeof 是⼀个关键字,也是操作符,专⻔是⽤来计算sizeof的操作符数的类型⻓度的,单位是字节。
        sizeof 操作符的操作数可以是类型,也可是变量或者表达式。
        sizeof 的操作数如果不是类型,是表达式的时候,可以省略掉后边的括号的。 如:
sizeof(类型)或  sizeof 表达式
        sizeof 后边的表达式是不真实参与运算的,根据表达式的类型来得出大小。
        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

例如:

下图是在VS2022 X64配置下的输出:

另外补充一点: sizeof 中表达式不计算

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

# signed和unsigned

          C 语⾔使⽤ signed 和 unsigned 关键字修饰字符型和整型类型的。signed 关键字,表⽰⼀个类型带有正负号,包含负值;unsigned 关键字,表⽰该类型不带有正负号,只能表⽰零和正整数。对于 int 类型,默认是带有正负号的,也就是说 int 等同于 signed int 。由于这是默认情况,关键字 signed ⼀般都省略不写,但是写了也不算错。int 类型也可以不带正负号,只表⽰⾮负整数。这时就必须使⽤关键字 unsigned 声明变量。
signed int a;//等同于 int a;
unsigned int a;
        整数变量声明为 unsigned 的好处是,同样⻓度的内存能够表⽰的最⼤整数值,增⼤了⼀倍。⽐如,16位的 signed short int 的取值范围是:-32768~32767,最⼤是32767;⽽unsigned short int 的取值范围是:0~65535,最⼤值增⼤到了65,535。32位的 signed int 的取值范围可以参看 limits.h 中给出的定义。下⾯的定义是VS2022环境中,limits.h中相关定义。
1 # define SHRT_MIN (-32768) // 有符号 16 位整型的最⼩值
2 # define SHRT_MAX 32767 // 有符号 16 位整型的最⼤值
3 # define USHRT_MAX 0xffff // ⽆符号 16 位整型的最⼤值
4 # define INT_MIN (-2147483647 - 1) // 有符号整型的最⼩值
5 # define INT_MAX 2147483647 // 有符号整型的最⼤值

 unsigned int ⾥⾯的 int 可以省略,所以上⾯的变量声明也可以写成下⾯这样。

unsigned a;

字符类型 char 也可以设置 signed 和 unsigned
1 signed char c; // 范围为 -128 127
2 unsigned char c; // 范围为 0 255
注意,C 语⾔规定 char 类型默认是否带有正负号,由当前系统决定。这就是说, char 不等同于 signed char ,它有可能是 signed char ,也有可能是 unsigned char 。这⼀点与 int 不同, int 就是等同于 signed int

# 数据类型的取值范围

        上述的数据类型很多,尤其数整型类型就有short、int、long、long long 四种,为什么呢?其实每⼀种数据类型有⾃⼰的取值范围,也就是存储的数值的最⼤值和最⼩值的区间,有了丰富的类
型,我们就可以在适当的场景下去选择适合的类型。如果要查看当前系统上不同数据类型的极限值:
        limits.h ⽂件中说明了整型类型的取值范围。
        float.h 这个头⽂件中说明浮点型类型的取值范围。
为了代码的可移植性,需要知道某种整数类型的极限值时,应该尽量使⽤这些常量。
SCHAR_MIN SCHAR_MAX :signed char 的最⼩值和最⼤值。
SHRT_MIN SHRT_MAX :short 的最⼩值和最⼤值。
INT_MIN INT_MAX :int 的最⼩值和最⼤值。
LONG_MIN LONG_MAX :long 的最⼩值和最⼤值。
LLONG_MIN LLONG_MAX :long long 的最⼩值和最⼤值。
UCHAR_MAX :unsigned char 的最⼤值。
USHRT_MAX :unsigned short 的最⼤值。
UINT_MAX :unsigned int 的最⼤值。
ULONG_MAX :unsigned long 的最⼤值。
ULLONG_MAX :unsigned long long 的最⼤值。

# 变量 

1.变量的创建

        了解清楚了类型,我们使⽤类型做什么呢?类型是⽤来创建变量的。什么是变量呢?C语⾔中把经常变化的值称为变量,不变的值称为常量。变量创建的语法形式是这样的:
data_type name;
        |         |
        |         |
数据类型 变量名
int age; // 整型变量
char ch; // 字符变量
double weight; // 浮点型变量

 变量在创建的时候就给⼀个初始值,就叫初始化。

#include<stdio.h>
int main()
{int age = 18;char ch = 'w';double weight = 48.0;unsigned int height = 100;return 0;
}

2.变量的分类

        • 全局变量:在⼤括号外部定义的变量就是全局变量
        全局变量的使⽤范围更⼴,整个⼯程中想使⽤,都是有办法使⽤的。
        • 局部变量:在⼤括号内部定义的变量就是局部变量
        局部变量的使⽤范围是⽐较局限,只能在⾃⼰所在的局部范围内使⽤的。
#include<stdio.h>int global = 2023;//全局变量int main()
{int local = 2018;//局部变量printf("%d\n",local);printf("%d\n",global);return 0;
}
        如果局部和全局变量,名字相同呢?其实当局部变量和全局变量同名的时候,局部变量优先使⽤.这里不过多赘述了。
        全局变量和局部变量在内存中存储在哪⾥呢?⼀般我们在学习C/C++语⾔的时候,我们会关注
内存中的三个区域:栈区、堆区、静态区。
        1. 局部变量是放在内存的栈区
        2. 全局变量是放在内存的静态区
        3. 堆区是⽤来动态内存管理的(后期会介绍)
        其实内存区域的划分会更加细致,以后在操作系统的相关知识的时候会介绍。

# 算数操作符

        在写代码时候,⼀定会涉及到计算。C语⾔中为了⽅便运算,提供了⼀系列操作符,其中有⼀组操作符叫:算术操作符。分别是: + - * / % ,这些操作符都是双⽬操作符。
        注: 操作符也被叫做:运算符,是不同的翻译,意思是⼀样的。

1.+ 和 -

        + 和 - ⽤来完成加法和减法。+ - 都是有2个操作数的,位于操作符两端的就是它们的操作数,这种操作符也叫双⽬操作符。使用方法如下:
#include <stdio.h>
int main()
{int x = 4 + 22;int y = 61 - 23;printf("%d\n", x);printf("%d\n", y);return 0;
}

2. *

        运算符 * ⽤来完成乘法。

#include <stdio.h>
int main()
{int num = 5;printf("%d\n", num * num); // 输出 25return 0;
}

3. /

        运算符 / ⽤来完成除法。除号的两端如果是整数,执⾏的是整数除法,得到的结果也是整数。
#include <stdio.h>
int main()
{float x = 6 / 4;int y = 6 / 4;printf("%f\n", x); // 输出 1.000000printf("%d\n", y); // 输出 1return 0;
}
        上⾯⽰例中,尽管变量 x 的类型是 float (浮点数),但是 6 / 4 得到的结果是 1.0 ,⽽不是
1.5 。原因就在于 C 语⾔⾥⾯的整数除法是整除,只会返回整数部分,丢弃⼩数部分。
        如果希望得到浮点数的结果,两个运算数必须⾄少有⼀个浮点数,这时 C 语⾔就会进⾏浮点数除法。
#include <stdio.h>
int main()
{float x = 6.0 / 4; // 或者写成 6 / 4.0printf("%f\n", x); // 输出 1.500000return 0;
}
        上⾯⽰例中, 6.0 / 4 表⽰进⾏浮点数除法,得到的结果就是 1.500000 
再看⼀个例⼦:
#include <stdio.h>
int main()
{int score = 5;score = (score / 20) * 100;return 0;
}
        上⾯的代码,你可能觉得经过运算, score 会等于 25 ,但是实际上 score 等于 0 。这是因为
score / 20 是整除,会得到⼀个整数值 0 ,所以乘以 100 后得到的也是 0 。为了得到预想的结果,可以将除数 20 改成 20.0 ,让整除变成浮点数除法。

4.%

        运算符 % 表⽰求模运算,即返回两个整数相除的余值。这个运算符只能⽤于整数,不能⽤于浮点数。

#include <stdio.h>
int main()
{int x = 6 % 4; // 2return 0;
}
        负数求模的规则是,结果的正负号由第⼀个运算数的正负号决定。
#include <stdio.h>
int main()
{printf("%d\n", 11 % -5); // 1printf("%d\n",-11 % -5); // -1printf("%d\n",-11 % 5); // -1return 0;
}

# 赋值操作符:=和复合赋值

        在变量创建的时候给⼀个初始值叫初始化,在变量创建好后,再给⼀个值,这叫赋值。 赋值操作符 = 是⼀个随时可以给变量赋值的操作符。

int a = 100 ; // 初始化
a = 200 ; // 赋值,这⾥使⽤的就是赋值操作符

 1.连续赋值

        赋值操作符也可以连续赋值,如:

int a = 3 ;
int b = 5 ;
int c = 0 ;
c = b = a+ 3 ; // 连续赋值,从右向左依次赋值的。
        C语⾔虽然⽀持这种连续赋值,但是写出的代码不容易理解,建议还是拆开来写,这样在需要调试的时候⽅便观察代码的执⾏细节。
int a = 3 ;
int b = 5 ;
int c = 0 ;
b = a+ 3 ;
c = b;      //效果与上面一致

2.复合赋值符 

在写代码时,我们经常可能对⼀个数进⾏⾃增、⾃减的操作,如下代码:
int a = 10 ;
a = a+ 3 ;
a = a -2 ;

这样代码C语⾔给提供了更加⽅便的写法:

int a = 10 ;
a += 3 ;
a -= 2 ;

 C语⾔中提供了复合赋值符,⽅便我们编写代码,这些赋值符有:

+=        -=
*=         /=         %=
// 下⾯的操作符后期讲解
>>=         <<=
&=         |=         ^=

单⽬操作符:++、--、+、-  

        C语⾔中还有⼀些操作符只有⼀个操作数,被称为单⽬操作符。 ++ -- +( ) -( ) 就是单⽬操作符的。

1 ++和--

++是⼀种⾃增的操作符,⼜分为前置++和后置++,--是⼀种⾃减的操作符,也分为前置--和后置--.

前置++

int a = 10 ;
int b = ++a; //++ 的操作数是 a ,是放在 a 的前⾯的,就是前置 ++
printf ( "a=%d b=%d\n" ,a , b);
计算⼝诀:先+1,后使⽤;
a原来是10,先+1,后a变成了11,再使⽤就是赋值给b,b得到的也是11,所以计算技术后,a和b都是11,相当于这样的代码:
int a = 10 ;
a = a+ 1 ;
b = a;
printf ( "a=%d b=%d\n" ,a , b);

 后置++

int a = 10 ;
int b = a++; //++ 的操作数是 a ,是放在 a 的后⾯的,就是后置 ++
printf ( "a=%d b=%d\n" ,a , b);
计算⼝诀:先使⽤,后+1
a原来是10,先使⽤,就是先赋值给b,b得到了10,然后再+1,然后a变成了11,所以直接结束后a是11,b是10,相当于这样的代码:
int a = 10 ;
int b = a;
a = a+ 1 ;
printf ( "a=%d b=%d\n" ,a , b);

前置--

如果你听懂了前置++,那前置--是同理的,只是把加1,换成了减1;
计算⼝诀:先-1,后使⽤
int a = 10 ;
int b = --a; //-- 的操作数是 a ,是放在 a 的前⾯的,就是前置 --
printf ( "a=%d b=%d\n" ,a , b); // 输出的结果是: 9 9
后置--
同理后置--类似于后置++,只是把加⼀换成了减⼀
计算⼝诀:先使⽤,后-1
int a = 10 ;
int b = a--; //-- 的操作数是 a ,是放在 a 的后⾯的,就是后置 --
printf ( "a=%d b=%d\n" ,a , b); // 输出的结果是: 9 10

2 + 和 -  

        这⾥的+是正号,-是负号,都是单⽬操作符。运算符 + 对正负值没有影响,是⼀个完全可以省略的运算符,但是写了也不会报错。 运算符 - ⽤来改变⼀个值的正负号,负数的前⾯加上 - 就会得到正数,正数的前⾯加上 - 会得到负 数。
int a = + 10 ; 等价于 int a = 10 ;
 
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

强制类型转换

        在操作符中还有⼀种特殊的操作符是强制类型转换,语法形式很简单,形式为:(类型)

 int a = 3.14;

//a 的是 int 类型 , 3.14 double 类型,两边的类型不⼀致,编译器会报警告
为了消除这个警告,我们可以使⽤强制类型转换:
int a = ( int ) 3.14 ; // 意思是将 3.14 强制类型转换为 int 类型,这种强制类型转换只取整数部分

        俗话说,强扭的⽠不甜,我们使⽤强制类型转换都是万不得已的时候使⽤,如果不需要强制类型转化就能实现代码,这样⾃然更好的。 

# scanf和printf介绍

1.printf

基本⽤法

        printf() 的作⽤是将参数⽂本输出到屏幕。它名字⾥⾯的 f 代表 format (格式化),表⽰可以定制输出⽂本的格式。
# include <stdio.h>
int main ( void )
{
        printf ( "Hello World" );
        return 0 ;
}
        上⾯命令会在屏幕上输出⼀⾏⽂字“Hello World”。
        printf() 不会在⾏尾⾃动添加换⾏符,运⾏结束后,光标就停留在输出结束的地⽅,不会⾃动换⾏。为了让光标移到下⼀⾏的开头,可以在输出⽂本的结尾,添加⼀个换⾏符 \n 。如下:
# include <stdio.h>
int main ( void )
{
        printf ( "Hello World\n" );
        return 0 ;
}
如果⽂本内部有换⾏,也是通过插⼊换⾏符来实现,如下⽅代码:
# include <stdio.h>
int main ( void )
{
        printf ( "Hello\nWorld\n" );
        printf ( "Hello\n" );
        printf ( "World\n" );
return 0 ;
}
        printf() 是在标准库的头⽂件 stdio.h 定义的。使⽤这个函数之前,必须在源码⽂件头部引⼊这个头⽂件。

占位符 

     printf() 可以在输出⽂本中指定占位符。所谓 “占位符”,就是这个位置可以⽤其他值代⼊。
// 输出 There are 3 apples
# 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 。因此printf()参数与占位符是一一对应关系,如果有n个占位符,printf()的参数就应该有n+1个,务必牢记。
        常⽤的占位符除了 %d ,还有 %s 表⽰代⼊的是字符串。
#include <stdio.h>
int main()
{printf("%s will come tonight\n", "zhangsan");return 0;
}
        上⾯⽰例中, %s 表⽰代⼊的是⼀个字符串,所以 printf() 的第⼆个参数就必须是字符串,这个例⼦是 zhangsan 。执⾏后的输出就是 zhangsan will come tonight
        输出⽂本⾥⾯可以使⽤多个占位符。    
#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() 可能会输出内存中的任意值。

占位符列举

        printf() 的占位符有许多种类,与 C 语⾔的数据类型相对应。下⾯按照字⺟顺序,列出常⽤的占位符,⽅便查找,具体含义在后⾯章节介绍。
%a :⼗六进制浮点数,字⺟输出为⼩写。
%A :⼗六进制浮点数,字⺟输出为⼤写。
%c :字符。
%d :⼗进制整数。
%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 类型。
%hu :unsigned short int 类型。
%ld :⼗进制 long int 类型。
%lo :⼋进制 long int 类型。
%lx :⼗六进制 long int 类型。
%lu :unsigned long int 类型。
%lld :⼗进制 long long int 类型。
%llo :⼋进制 long long int 类型。
%llx :⼗六进制 long long int 类型。
%llu :unsigned long long int 类型。
%Le :科学计数法表⽰的 long double 类型浮点数。
%Lf :long double 类型浮点数。
%n :已输出的字符串数量。该占位符本⾝不输出,只将值存储在指定变量之中。
%o :⼋进制整数。
%p :指针。
%s :字符串。
%u :⽆符号整数(unsigned int)。
%x :⼗六进制整数。
%zd size_t 类型。
%% :输出⼀个百分号。

 输出格式

        printf() 可以定制占位符的输出格式。
 限定宽度

        printf() 允许限定占位符的最⼩宽度。

#include <stdio.h>
int main()
{printf("%5d\n", 123); // 输出为 " 123"return 0;
}
        上⾯⽰例中, %5d 表⽰这个占位符的宽度⾄少为5位。如果不满5位,对应的值的前⾯会添加空格。输出的值默认是右对⻬,即输出内容前⾯会有空格;如果希望改成左对⻬,在输出内容后⾯添加空格,可以在占位符的 % 的后⾯插⼊⼀个 - 号。
#include <stdio.h>
int main()
{
printf("%-5d\n", 123); // 输出为 "123 "
return 0;
}
        上⾯⽰例中,输出内容 123 的后⾯添加了空格。
        而对于⼩数,这个限定符会限制所有数字的最⼩显⽰宽度。
// 输出 " 123.450000"
#include <stdio.h>
int main()
{printf("%12f\n", 123.45);return 0;
}
        上⾯⽰例中, %12f 表⽰输出的浮点数最少要占据12位。由于⼩数的默认显⽰精度是⼩数点后6位,所以 123.45 输出结果的头部会添加2个空格。
总是显⽰正负号
        默认情况下, printf() 不对正数显⽰ + 号,只对负数显⽰ - 号。如果想让正数也输出 + 号,可以在占位符的 % 后⾯加⼀个 +
#include <stdio.h>
int main()
{printf("%+d\n", 12); // 输出 +12printf("%+d\n", -12); // 输出 -12return 0;
}
        上⾯⽰例中, %+d 可以确保输出的数值,总是带有正负号。
限定⼩数位数
        输出⼩数时,有时希望限定⼩数的位数。举例来说,希望⼩数点后⾯只保留两位,占位符可以写成 %.2f
// 输出 Number is 0.50
#include <stdio.h>
int main()
{printf("Number is %.2f\n", 0.5);return 0;
}
        如果希望⼩数点后⾯输出3位( 0.500 ),占位符就要写成 %.3f
同时 这种写法可以与限定宽度占位符,结合使⽤。
// 输出为 " 0.50"
#include <stdio.h>
int main()
{printf("%6.2f\n", 0.5);return 0;
}
        上⾯⽰例中, %6.2f 表⽰输出字符串最⼩宽度为6,⼩数位数为2。所以,输出字符串的头部有两个空格。
        最⼩宽度和⼩数位数这两个限定值,都可以⽤ * 代替,通过 printf() 的参数传⼊。如下:
#include <stdio.h>
int main()
{printf("%*.*f\n", 6, 2, 0.5);return 0;
}
// 等同于printf("%6.2f\n", 0.5);
        上⾯⽰例中, %*.*f 的两个星号通过 printf() 的两个参数 6 2 传⼊。
输出部分字符串
        %s 占位符⽤来输出字符串,默认是全部输出。如果只想输出开头的部分,可以⽤ %.[m]s 指定输出的⻓度,其中 [m] 代表⼀个数字,表⽰所要输出的⻓度。
// 输出 hello
#include <stdio.h>
int main()
{printf("%.5s\n", "hello world");return 0;
}
        上⾯⽰例中,占位符 %.5s 表⽰只输出字符串“hello world”的前5个字符,即“hello”。

 2 scanf

        当我们有了变量,我们需要给变量输⼊值就可以使⽤ scanf 函数,如果需要将变量的值输出在屏幕上的时候可以使⽤ prinf 函数,下⾯看⼀个例⼦:
#include <stdio.h>
int main()
{int score = 0;printf("请输⼊成绩:");scanf("%d", &score);printf("成绩是:%d\n", score);return 0;
}

基本用法

        scanf() 函数⽤于读取⽤⼾的键盘输⼊。
        程序运⾏到这个语句时,会停下来,等待⽤⼾从键盘输⼊。⽤⼾输⼊数据、按下回⻋键后, scanf() 就会处理⽤⼾的输⼊,将其存⼊变量。
        它的原型也定义在头⽂件 stdio.h ,并且 scanf() 的语法跟 printf() 类似。
   例:scanf ( "%d" , &i);
        
        它的第⼀个参数是⼀个格式字符串,⾥⾯会放置占位符(与 printf() 的占位符基本⼀致),告诉编译器如何解读⽤⼾的输⼊,需要提取的数据是什么类型。 这是因为 C 语⾔的数据都是有类型的, scanf() 必须提前知道⽤⼾输⼊的数据类型,才能处理数 据。
        它的其余参数就是存放⽤⼾输⼊的变量,格式字符串⾥⾯有多少个占位符,就有多少个变量。 上⾯⽰例中, scanf() 的第⼀个参数 %d ,表⽰⽤⼾输⼊的应该是⼀个整数。 %d 就是⼀个占位 符, % 是占位符的标志, d 表⽰整数。第⼆个参数 &i 表⽰,将⽤⼾从键盘输⼊的整数存⼊变量 i
        注意:变量前⾯必须加上 & 运算符(指针变量除外),因为 scanf() 传递的不是值,⽽是地址,即将变量 i 的地址指向⽤⼾输⼊的值。如果这⾥的变量是指针变量(⽐如字符串变量),那就不⽤加 & 运算符。
        下⾯是⼀次将键盘输⼊读⼊多个变量的例⼦:
        scanf ( "%d%d%f%f" , &i, &j, &x, &y);
        上⾯⽰例中,格式字符串 %d%d%f%f ,表⽰⽤⼾输⼊的前两个是整数,后两个是浮点数,⽐如 1 -20 3.4 -4.0e3 。这四个值依次放⼊ i j x y 四个变量。
        scanf() 处理数值占位符时,会⾃动过滤空⽩字符,包括空格、制表符、换⾏符等。所以,⽤⼾输⼊的数据之间,有⼀个或多个空格不影响 scanf() 解读数据。另外,⽤⼾使⽤回⻋键,将输⼊分成⼏⾏,也不影响解读。
        ⽤⼾分成四⾏输⼊,得到的结果与⼀⾏输⼊是完全⼀样的。每次按下回⻋键以后,scanf() 就会开始解读,如果第⼀⾏匹配第⼀个占位符,那么下次按下回⻋键时,就会从第⼆个占位符开始解读。scanf() 处理⽤⼾输⼊的原理是,⽤⼾的输⼊先放⼊缓存,等到按下回⻋键后,按照占位符对缓存进⾏解读。 解读⽤⼾输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条 件的字符为⽌。
#include <stdio.h>
int main()
{int x;float y;// ⽤⼾输⼊ " -13.45e12# 0"scanf("%d", &x);printf("%d\n", x);scanf("%f", &y);printf("%f\n", y);return 0;
}
        上⾯⽰例中, scanf() 读取⽤⼾输⼊时, %d 占位符会忽略起⾸的空格,从 - 处开始获取数据,读取到 -13 停下来,因为后⾯的 . 不属于整数的有效字符。这就是说,占位符 %d 会读到 -13 。 第⼆次调⽤ scanf() 时,就会从上⼀次停⽌解读的地⽅,继续往下读取。这⼀次读取的⾸字符 是 . ,由于对应的占位符是 %f ,会读取到 .45e12 ,这是采⽤科学计数法的浮点数格式。后⾯的 # 不属于浮点数的有效字符,所以会停在这⾥。
        由于 scanf() 可以连续处理多个占位符,所以上⾯的例⼦也可以写成下⾯这样。
#include <stdio.h>
int main()
{int x;float y;// ⽤⼾输⼊ " -13.45e12# 0"scanf("%d%f", &x, &y);return 0;
}

 scanf的返回值

        scanf() 的返回值是⼀个整数,表⽰成功读取的变量个数。如果没有读取任何项,或者匹配失败,则返回 0 。如果在成功读取任何数据之前,发⽣了读取错误或 者遇到读取到⽂件结尾,则返回常量 EOF。
#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;
}

输入输出测试:

如果输⼊2个数后,按 ctrl+z ,提前结束输⼊:
        在VS环境中按3次 ctrl+z ,才结束了输⼊,我们可以看到r是2,表⽰正确读取了2个数值。如果⼀个数字都不输⼊,直接按3次 ctrl+z ,输出的r是-1,也就是EOF

占位符 

scanf() 常⽤的占位符如下,与 printf() 的占位符基本⼀致。
%c :字符。
%d :整数。
%f float 类型浮点数。
%lf double 类型浮点数。
%Lf long double 类型浮点数。
%s :字符串。
%[] :在⽅括号中指定⼀组匹配的字符(⽐如 %[0-9] ),遇到不在集合之中的字符,匹配将会停⽌。
            上⾯所有占位符之中,除了 %c 以外,都会⾃动忽略起⾸的空⽩字符。 %c 不忽略空⽩字符,总是返回当前第⼀个字符,⽆论该字符是否为空格。 如果要强制跳过字符前的空⽩字符,可以写成 scanf(" %c", &ch) ,即 %c 前加上⼀个空格,表 ⽰跳过零个或多个空⽩字符。
        下⾯要特别说⼀下占位符 %s ,它其实不能简单地等同于字符串。它的规则是,从当前第⼀个⾮空⽩字符开始读起,直到遇到空⽩字符(即空格、换⾏符、制表符等)为⽌。因为 %s 不会包含空⽩字符,所以⽆法⽤来读取多个单词,除⾮多个 %s ⼀起使⽤。这也意味着,
scanf() 不适合读取可能包含空格的字符串,⽐如书名或歌曲名。另外, scanf() 遇到 %s 占位符,会在字符串变量末尾存储⼀个空字符 \0 。 scanf() 将字符串读⼊字符数组时,不会检测字符串是否超过了数组⻓度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防⽌这种情况,使⽤ %s 占位符时,应该指定读⼊字符串的最⻓⻓度,即写成 %[m]s ,其中的 [m] 是⼀个整数,表⽰读取字符串的最⼤⻓度,后⾯的字符将被丢弃。
#include <stdio.h>
int main()
{char name[11];scanf("%10s", name);return 0;
}
        上⾯⽰例中, name 是⼀个⻓度为11的字符数组, scanf() 的占位符 %10s 表⽰最多读取⽤⼾输⼊的10个字符,后⾯的字符将被丢弃,这样就不会有数组溢出的⻛险了。

 赋值忽略符

有时,⽤⼾的输⼊可能不符合预定的格式。

#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 ,这种情况下, scanf() 解析数据就会失败。 为了避免这种情况, scanf() 提供了⼀个赋值忽略符(assignment suppression character) * 。只要把 * 加在任何占位符的百分号后⾯,该占位符就不会返回值,解析后将被丢弃。
#include <stdio.h>
int main()
{int year = 0;int month = 0;int day = 0;scanf("%d%*c%d%*c%d", &year, &month, &day);return 0;
}
        上⾯⽰例中, %*c 就是在占位符的百分号后⾯,加⼊了赋值忽略符 * ,表⽰这个占位符没有对应的变量,解读后不必返回。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/172238.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[ruby on rails] array、jsonb字段

一、jsonb # 新增 add_column :shi_tis, :setting, :jsonb, default: {}# string转jsonb def changechange_column :users, :setting, :jsonb, using: setting::jsonb, default: {} end# 加索引 add_index :users, :setting, using: :gin # 这样就为setting jsonb字段创建了一…

TypeScript中的枚举是什么?

在TypeScript中&#xff0c;枚举&#xff08;Enum&#xff09;是一种用于定义一组有命名的常量值的数据类型。它们可以提供更具可读性和可维护性的代码。 枚举的作用是为一组相关的值提供一个易于理解和使用的命名空间。它们可以用于代表一系列可能的选项、状态或标志&#xf…

CMake Error:No targets specified and no makefile found

在适用cmake构建项目的时候&#xff0c;突然遇到了这个报错 Make Error at CMakeLists.txt:1 (project): VERSION not allowed unless CMP0048 is set to NEW – Configuring incomplete, errors occurred! make: *** No targets specified and no makefile found. Stop. CMake…

MySQL进阶知识

目录 MySQL的Linux安装 存储引擎 MySQL的体系结构 存储引擎简介 存储引擎特点 InnoDB 逻辑存储结构 MyISAM Memory 对比 存储引擎选择 索引 介绍 索引结构 BTree索引 Hash索引 索引分类 索引语法 SQL性能分析 SQL执行频率 慢查询日志 profile详情 expla…

CRC 循环冗余检测

目录 一、基础知识1.异或运算xor2.模2算术&#xff08;1&#xff09;模2加法和减法&#xff08;2&#xff09;模2乘法&#xff08;3&#xff09;模2除法 二、CRC循环冗余检测1.背景2.原理3.求R 一、基础知识 1.异或运算xor 异或&#xff0c;顾名思义&#xff0c;只有当两个数…

算法基础一

两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 解题思路&#xff1a;这道题最优的做法时间复杂度是O(n)&#xff0c;顺序扫描数组&#xff0c;对每一个元素在…

Aibote4j java封装版本的

Aibote4j 交流Q群&#xff1a;496086899 开源地址&#xff1a;https://github.com/1341191074/aibote4j 更新yolo和ocr相关新增的能力&#xff0c;比较强劲了 下面是使用的案例 public class WebBotTest extends WebBot {public static void main(String[] args) {WebBotSer…

最新AI创作系统ChatGPT网站运营源码、支持GPT-4-Turbo模型,图片对话识图理解,支持DALL-E3文生图

一、AI创作系统 SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&#xff01;本系统使用NestjsVueTypescript框架技术&#xff0c;持续集成AI能力到本系统。支持OpenAI DALL-E3文生图&#xff0c;…

事件委派+自定义属性+编程式导航实现路由跳转及传参

当我们页面中有许多a标签需要实现点击跳转到同一个页面并携带不同的参数时&#xff0c;我们就可以使用事件委派自定义属性编程式导航 的方式&#xff0c;用最小的内存实现路由跳转的最大效率。 为什么我们不用router-link 进行跳转&#xff1f; 要知道&#xff0c;我们页面中…

【单调栈】子数组的最小值之和

import java.util.Deque; import java.util.LinkedList;/** 参考链接&#xff1a;https://leetcode.cn/problems/sum-of-subarray-minimums/solutions/1930857/gong-xian-fa-dan-diao-zhan-san-chong-shi-gxa5/* https://leetcode.cn/problems/sum-of-subarray-minim…

Docker 的基本概念和优势,以及在应用程序开发中的实际应用。

Docker 是一种容器化技术&#xff0c;它将一个应用程序及其所有依赖项打包在一起&#xff0c;形成一个独立的、可移植的容器。这个容器可以在任何支持 Docker 的操作系统上运行&#xff0c;而且具有很好的可移植性和可扩展性。以下是 Docker 的基本概念和优势&#xff1a; 镜像…

论文公式和代码对应

NGCF 论文地址 NGCF模型全部代码 import torch import torch.nn as nn import torch.nn.functional as F class NGCF(nn.Module):def __init__(self, n_user, n_item, norm_adj, args):super(NGCF, self).__init__()self.n_user n_userself.n_item n_itemself.device args…

数据结构与算法(Java)-树形DP题单

树形DP&#xff08;灵神笔记&#xff09; 543 二叉树的直径 543. 二叉树的直径 - 力扣&#xff08;LeetCode&#xff09; 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根…

使用 Java 客户端通过 HTTPS 连接到 Easysearch

Easysearch 一直致力于提高易用性&#xff0c;这也是我们的核心宗旨&#xff0c;然而之前一直没有官方的 Java 客户端&#xff0c;也对用户使用造成了一些困扰&#xff0c;现在&#xff0c;我们正式发布了第一个 Java 客户端 Easysearch-client:1.0.1。 这一里程碑式的更新为开…

成为AI产品经理——TPR、FPR、ROC、AUC

目录 一、PR图、BEP 1.PR图 2.BEP 二、灵敏度、特异度 1.灵敏度 2.特异度 三、真正率、假正率 1.真正率 2.假正率 三、ROC、AUC 1.ROC 2.AUC 四、KS值 一、PR图、BEP 1.PR图 二分类问题模型通常输出的是一个概率值&#xff0c;我们需要设定一个阈值&#xff…

Android aidl的简单使用

一.服务端 1.创建aidl文件&#xff0c;然后记得build下生成java文件 package com.example.aidlservice31;// Declare any non-default types here with import statementsinterface IMyAidlServer {// 接收一个字符串参数void setData(String value);// 返回一个字符串String …

数据结构与算法之贪心: LeetCode 122. 买卖股票的最佳时机II (Typescript版)

买卖股票的最佳时机II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能获得的 …

mybatis配置文件中配置类型别名的方式

在MyBatis配置文件&#xff08;通常是mybatis-config.xml&#xff09;中&#xff0c;可以通过以下方式配置类型别名&#xff1a; 1. 使用typeAliases元素配置全局类型别名 <configuration> <typeAliases> <typeAlias alias"YourAlias" type"…

简单订单和支付业务的相关流程

1、订单创建、支付及订单处理流程图 2、创建HTTP客户端工具类 Slf4j public class HttpclientUtil {//类中定义了一个私有静态成员变量instance&#xff0c;并且将其初始化为HttpclientUtil类的一个实例&#xff0c;用于实现单例模式。private static HttpclientUtil instance…

单片机学习6——定时器/计数功能的概念

在8051单片机中有两个定时器/计数器&#xff0c;分别是定时器/计数器0和定时器/计数器1。 T/C0: 定时器/计数器0 T/C1: 定时器/计数器1 T0: 定时器0 T1: 定时器1 C0: 计数器0 C1: 计数器1 如果是对内部振荡源12分频的脉冲信号进行计数&#xff0c;对每个机器周期计数&am…