第二章:数据类型和变量
文章目录
- 第二章:数据类型和变量
- 1. 数据类型及长度和取值范围
- 1.1 字符型
- 1.2 整形
- 1.3 浮点型
- 1.4 布尔类型
- 1.5 sizeof
- 1.6 取值范围
- 2.变量
- 2.1 变量命名规则:
- 2.2 变量分类
- 3. 操作符
- 3.1 算数操作符
- 3.2 赋值操作符
- 3.3 单目操作符
- 4. 强制类型转换
- 5. printf 和 scanf
- 5.1 printf
- 5.1.1 占位符
- 5.1.2 限定最小宽度:
- 5.1.3 限定小数位数:
- 5.1.4 输出部分字符串:
- 5.2. scanf
- 5.2.1 基本用法
- 5.2.2 返回值
- 5.2.3 占位符
- 5.2.4 赋值忽略符
- 5.2.5 printf 和 scanf 差异
- 声明
1. 数据类型及长度和取值范围
- signed 关键字,表示⼀个类型带有正负号,包含负值
- unsigned 关键字,表示该类型不带有正负号,只能表示零和正整数。好处是同样长度的内存能够表示的最大整数值增大一倍。
计算机存储的是二进制 0 和 1,存放一个 0 或者 1 需要一个比特位,一个字节为八个比特位
1.1 字符型
- char:1个字节
- [signed] char
- unsigned char
C 语言规定
char类型默认是否带有正负号,由当前系统决定。
一般默认char等同于signed char
1.2 整形
短整型:
- short [int]:两个字节
- [signed] short [int]
- unsigned short [int]
整形:
- int:4个字节
- [signed] int
- unsigned [int]
长整形:
- long [int]:4个字节
- [signed] long [int]
- unsigned long [int]
长长整形(C99引入)
- long long [int]:8个字节
- [signed] long long [int]
- unsigned long long [int]
1.3 浮点型
- float:4个字节
- double:8个字节
- long double:在 vs 上是8个字节,gcc 是16个字节
1.4 布尔类型
在 C 语言中 0 表示假,非零即为真。布尔类型的变量取值是:true或false
bool或者_Bool
1.5 sizeof
sizeof操作符专门用来计算类型长度,单位是字节,计算结果是size_t类型。sizeof操作符的操作数可以是类型,也可以是变量或者表达式(表达式不参与计算)。
#include<stdio.h>// 标准输入输出的头文件intmain(){// sizeof 不是类型的时候可以省略括号inta=99;printf("%zd\n",sizeof(a));// sizeof 打印应该用 %zd 格式,sizeof函数的返回值类型是 size_tprintf("%zd\n",sizeofa);printf("%zd\n",sizeof(int));printf("%zd\n",sizeof(2+5));// sizeof的表达式不实际运算,而是根据类型进行推断// 验证 sizeof表达式不实际计算shortb=3;printf("%zd\n",sizeof(b=10+b));// 输出 2,类型是由 b 决定printf("b = %d\n",b);// b = 3,由此可见 sizeof 表达式不进行计算printf("%zd\n",sizeof2+5);// 表达式省略括号后,先计算 2 的长度是 4 ,接着 + 5 = 9// 数据类型长度printf("%zd\n",sizeof(char));// 字符型 1printf("%zd\n",sizeof(bool));// 布尔型 1printf("%zd\n",sizeof(short));// 短整型 2printf("%zd\n",sizeof(int));// 整型 4printf("%zd\n",sizeof(long));// 长整型 4 C 语言标准规定 long >= intprintf("%zd\n",sizeof(longlong));// 长长整形 8printf("%zd\n",sizeof(float));// 单精度浮点型 4printf("%zd\n",sizeof(double));// 双精度浮点型 8printf("%zd\n",sizeof(longdouble));// 长双精度浮点型 8return0;}1.6 取值范围
每一种数据类型都有自己的取值范围,也就是存储数值的最小值和最大值的区间。
CHAR_MIN,CHAR_MAX:char 的最小值和最大值SHRT_MIN,SHRT_MAX:shot 的最小值和最大值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的最大值
2.变量
- 数据类型 变量名
变化的值称为变量,不变的值称为常量。创建变量的本质就是向内存中申请一块空间,并将变量的值存储在空间中。
2.1 变量命名规则:
- 只能由字母(大小写)、数字和下划线 _ 组成
- 不能以数字开头
- 长度不超过63个字符
- 变量名区分大小写
- 不能使用关键字
2.2 变量分类
- 全局变量:大括号外部定义的变量,在整个工程中都可以使用
- 局部变量:在大括号内部定义的变量,只能在自己所在的局部范围使用
#include<stdio.h>// 标准输入输出的头文件// 全局变量,整个工程都可以使用intpublic;floatlocal=99.9f;//定义变量时给初始值叫初始化intmain(){//局部变量:代码块内声明,只能在本代码块使用floatlocal=23.6f;// 小数默认为 double ,所以末尾加 f 表明 float 类型printf("%f\n",local);// 全局变量与局部变量同名,优先局部变量return0;}全局变量和局部变量在内存中如何存储呢?
学习 C 语言的过程中,需要用到内存中的三个区域:栈区,堆区,静态区
- 栈区:局部变量和函数参数
- 堆区:动态内存管理
- 静态区:动态内存管理
为什么变量要初始化呢?
- 局部变量不初始化,他的值是随机的
- 全局变量不初始化,他的值会默认初始化为0
3. 操作符
3.1 算数操作符
+ - * / %这些操作符都需要两个操作数,也被称为双目操作符。
注意:
/:两端若是整数,执行的是整除,得到的也是整数。如果希望执行浮点数除法,两个运算数必须至少一个为浮点数。
%:只能用于整数,不能用于浮点数,负数求模结果的正负号则有第一个运算数的正负号决定。
实操:
#include<stdio.h>// 标准输入输出的头文件intmain(){printf("%d\n",6+4);printf("%d\n",4-6);printf("%d\n",6*4);intx=6/4;// 两端整数执行的是整除,得到的也是整数,丢掉小数部分printf("%d\n",x);// 1floaty=6.0/4;// 只要一端为小数,执行的便是浮点数除法printf("%f\n",y);// 1.500000// 求模只能用于整数,不能有一方是浮点数printf("%d\n",6%4);//负数求模,结果的正负号由第一个运算数的正负号决定printf("%d\n",-6%4);// -2printf("%d\n",6%-4);//2printf("%d\n",-6%-4);//-2return0;}3.2 赋值操作符
- 初始化:创建变量时给值
- 赋值:变量创建好后再给一个值
复合赋值符:
+=-=*=/=%=>>=<<=&=|=^=
实操:
#include<stdio.h>// 标准输入输出的头文件intmain(){//复合赋值符inta1=10;printf("%d\n",a1+=3);// a1 = a1 + 3 13printf("%d\n",a1-=3);// a1 = a1 -3 10printf("%d\n",a1*=3);// a1 = a1 * 3 30printf("%d\n",a1/=3);// a1 = a1 / 3 10printf("%d\n",a1%=3);// a1 = a1 % 3 1return0;}3.3 单目操作符
顾名思义只有一个操作数:
++:自增- ++ 前置:先+1,后使用
- 后置 ++:先使用,再 +1
--:自减- – 前置:先 -1,再使用
- 后置 --:先使用,再 -1
+:正号-:负号
实操:
#include<stdio.h>// 标准输入输出的头文件intmain(){intc1=10;intd1=++c1;//前置++,先自增,再使用 :c1 = c1 + 1 d1 = c1printf("c1:%d\n",c1);// 11printf("d1:%d\n",d1);// 11intc2=10;intd2=c2++;//后置++,先使用,再自增 :d2 = c2 c2 = c2 + 1printf("c2:%d\n",c2);// 11printf("d2:%d\n",d2);// 10intc3=10;intd3=--c3;//前置--,先自减,再使用 :c3 = c3 - 1 d3 = c3printf("c3:%d\n",c3);// 9printf("d3:%d\n",d3);// 9intc4=10;intd4=c4++;//后置--,先使用,再自减 :d4 = c4 c4 = c4 - 1printf("c4:%d\n",c4);// 9printf("d4:%d\n",d4);// 10intc5=10;intd5=-c5;printf("c5:%d d5:%d\n",c5,d5);//多个占位符输出,后面内容与占位符一一对应,如果少于则会输出内存中的任意值//int ch = getchar();// 此函数仅接受字符,输入 2 的时候是字符 2 ,函数返回的是 ASCALL 码值//printf("%d\n", ch);//putchar(ch);// putchar输出字符 = printf("%c"),函数接受字符或者 ASCALL码}4. 强制类型转换
(类型)
#include<stdio.h>// 标准输入输出的头文件intmain(){floatx1=(float)(6/4);// int 强转为 float ,小数点后补 6 个 0//或者 float x1 =6 / 4; 底层默认就会转换printf("%f\n",x1);// 1.000000printf("%f\n",6/4);// 不能通过占位符强转,int 类型使用 %f 占位符结果为 0.000000inty2=(int)(6.0/4);// double 强转为 int,丢掉小数部分printf("%d\n",y2);// 1printf("%d\n",99.9/3);// 不能通过占位符强转,float 类型使用 %d 占位符结果为随机值return0;}5. printf 和 scanf
5.1 printf
5.1.1 占位符
函数里的f代表format(格式化),表示可以定制输出文本的格式,所以就要用到占位符。
常用占位符列举:
%c:char%f: float 和 double%lf:double%Lf:long double%d:int%u:unsigned int%hd:short int%hu:unsigned short int%ld:long int%lu:unsigned long int%lld:long long int%p:指针%s:字符串%x:十六进制整数%zd:size_t
占位符会被后边的值所替换。多个占位符时,后面内容需要与占位符一一对应,如果少于则会输出内存中的任意值。
#include<stdio.h>// 标准输入输出的头文件intmain(){printf("hello world\n");// \n 换行符,printf("%s\n","hello world");//%s 字符串占位符printf("%c\n",'a');// %c:字符占位符,'':字符,只能包含一个字符printf("%d\n",123);// %d:整数占位符printf("%f\n",3.14);// %f:float 单精度浮点型和 double 双精度浮点型占位符,默认小数点后 6 位,不足补零printf("%lf\n",3.14);// %lf:double 双精度浮点型占位符,默认小数点后 6 位,不足补零printf("%Lf\n",3.14);// %Lf:long double 长双精度浮点型占位符,默认小数点后 6 位,不足补零printf("%s %d\n","hello world",99);//多个占位符时,后面内容需要与占位符一一对应return0;}5.1.2 限定最小宽度:
- %[m]占位符:限定数据的最小宽度
#include<stdio.h>// 标准输入输出的头文件intmain(){printf("%d\n",123);printf("%+d\n",+123);//显示正负号号,正号默认不显示,%+d 显示printf("%+d\n",-123);// 指定整数最小宽度,默认右对齐(前面补零)printf("%5d\n",123);printf("%-5d",123);// 左对齐(后面补零)printf("hehe\n");// 可以看到左对齐的效果return0;}5.1.3 限定小数位数:
- %.[m]f:指定小数部分位数
#include<stdio.h>// 标准输入输出的头文件intmain(){// 指定小数最小宽度printf("%12f\n",123.45);// 包含小数整体和小数点,最小12位,不足补零,默认右对齐//限制小数点后位数(会四舍五入)printf("%.2f\n",123.4567);//最小宽度和限制小数点位数结合printf("%10.2f\n",123.4567);//通过参数控制printf("%*.*f\n",10,2,123.4567);// 10 传给第一个*,2 传给第二个*return0;}5.1.4 输出部分字符串:
%.[m]s:指定输出长度
#include<stdio.h>// 标准输入输出的头文件intmain(){//输出部分字符串printf("%.5s\n","hello world");// 输出前五个字符printf("%[hello]","hewordllo");//在方括号中指定⼀组匹配的字符,遇到不在集合之中的字符,匹配将会停止return0;}5.2. scanf
5.2.1 基本用法
接受用户输入的数据传递给变量。原型定义在头文件stdio.h。第一个参数是占位符,用于接受什么类型的数据,告诉编译器如何解读用户的输入。其余参数则是存放用户输入的变量。有多少个占位符,就有多少个变量。
scanf()处理数值占位符时,会自动过滤空白字符,包括空格、制表符、换行符等。所以,用户输入的数据之间,有⼀个或多个空格不影响scanf()解读数据。另外,用户使用回车键,将输入分成几行,也不影响解读。
经过测试,如果scanf函数中包含了除占位符之外的字符,scanf会理解为这是两个数据之间的分割符,同时用户也要在键盘输入这个字符。在这种情况下,第一个数据只能在前边可以有多个空格,后边必须紧接着就是分隔符,不能有空格,也不能进行回车,否则二个数据将不会被读取。
注意:
变量前面必须加上&运算符,因为scanf传递的不是值,而是地址,即将变量的地址指向用户输入的值。另外 scanf 遇到输入类型不匹配时并不会自动丢弃,而是将错误的输入留在输入缓冲区中。
测试:
#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件/* 在运行 scanf()函数会报错此函数可能不安全,要么替换为 scan_s()(vs自己提供的函数) 要么在源文件中加入 #define _CRT_SECURE_NO_WARNINGS 当我们在源文件中加入上述的 #define _CRT_SECURE_NO_WARNINGS 时,新建源文件使用 scanf 时还是需要加入。 在vs中创建 .c/.cpp 文件的时候,其实是拷贝了一个叫 newc++file.cpp的文件。 所以我们可以在 newc++file.cpp 文件中添加。 */intmain(){intage=0;intyear=0;printf("请输入你的年龄和出生年份:");//scanf 传递的不是值,而是地址,即将变量 age 和 year 的地址分别指向用户输入的值//%d 接受输入数据的类型,& 取变量的地址值scanf("%d/%d",&age,&year);//注意,此时第一个 %d 后有一个 / 说明键盘在两个整数之间也要输入 ///例如输入: 18/ 23// 第一个数据前面空格没有影响,但必须紧接着分隔符printf("您的年龄是:%d\n",age);printf("您的出生年份是:%d\n",year);return0;}scanf处理用户输入的原理:
用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。 解读用户输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为止。
#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件intmain(){printf("%s","请输入 x3 和 y3 的值:");intx3=0;floaty3=0.0f;// ⽤⼾输⼊" -13.45e12# 0"scanf("%d%f",&x3,&y3);printf("%d\n",x3);// 此时 x3 接受的是整数类型,所以 -13.45e12#,只能读取到 -13,停到小数点这里printf("%f\n",y3);// 从上次解读遗留的第一个字符开始,读取到 .45e12(0.45 * 10 的12 次方)//因为 # 不属于小数,所以会停在这里,同时浮点数在内存中有可能无法精确保存导致结果有误差return0;}5.2.2 返回值
scanf()的返回值是⼀个整数,表示从左到右成功读取的变量个数。 如果没有读取任何项,或者匹配失败,则返回 0 。 如果在成功读取任何数据之前,发生了读取错误或者遇到读取到文件结尾,则返回常量EOF(-1)。
#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件intmain(){inta4=0;intb4=0;floatf4=0.0f;intr4=scanf("%d %d %f",&a4,&b4,&f4);printf("a=%d b=%d f=%f\n",a4,b4,f4);printf("r = %d\n",r4);return0;}5.2.3 占位符
scanf占位符与printf基本一致。
在scanf中除了%c以外,都会自动忽略起首的空白字符,%c不忽略。如果要想忽略起首的空白字符,需要在%c前加上一个空格即%c
注意:
scanf中的占位符%s:从当前第⼀个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。 因此%s不会包含空白字符,所以无法用来读取多个单词,除非多个%s⼀起使用。或者使用%[^\n]s此时也不忽略前面的空格。同时scanf遇到%s占位符时,会在字符串变量末尾存储⼀个空字符\0。
另外scanf将字符串读入字符数组时,不会检测字符串是否超过了数组长度。所以在储存字符串时,很可能会超出数组的边界,为了防止这种情况,使用%s时,应指定读入字符串的最长长度,即写成%[m]s,m = 字符数组长度 -1,因为还有一个\0
#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件intmain(){//在 scanf 中,%c 不忽略前面的空格,如果忽略要在前面加上空格chara5=0;scanf(" %c",&a5);printf("%c----------\n",a5);// scanf 接受字符串// 其底层原理无法接受多个单词,假设单词之间有空白字符(空格,换行,制表符)识别到后便会自动加上 \0,除非多个 %s// 字符串:字符数组chararray1[11];chararray2[11];//在读取字符串不会检测数组的长度,所以需要指定长度确保安全scanf("%10s",array1);//数组不需要取地址,因为数组名就是地址,格式 %[m]s ,指定长度为10,剩下的一个留给\0scanf("%[^\n]s",array2);//此写法可以读取多个单词,遇到 \n 才会停止,但此时便不忽略前面的空格//scanf("%10[^\n]s", array2);//读取多个单词的同时,限制读取数组的长度printf("%s\n",array1);printf("%s\n",array2);return0;}5.2.4 赋值忽略符
为了避免解析失败这种情况,可以使用赋值忽略符,把*加在任何占位符的百分号后⾯,便可忽略掉此占位符。该占位符不会返回值,解析后将被丢弃,同时 scanf 的返回值不包括被忽略的值。
#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件intmain(){// scanf 赋值忽略符intyear=0;intmonth=0;intday=0;printf("请输入年月日:");//scanf("%d%d%d",&year,&month,&day);// 此写法只能输入 年 月 日,经过实验 年-月-日 也可以,系统会当作负数处理不建议,年/月/日 则只会接受到年的数据//scanf("%d/%d/%d",& year, &month, &day)//此写法则只能读取 年/月/日 ,否则年后面的数据会解析失败,等同scanf("%d%c%d%c%d",&year,&ch1,&month,&ch2,&day);//为了避免解析失败这种情况,可以使用赋值忽略符,把 * 加在任何占位符的百分号后⾯,便可忽略掉此占位符// 该占位符就不会返回值,解析后将被丢弃。同时 scanf 的返回值即不被忽略值的数量intnum=scanf("%d%*c%d%*c%d",&year,&month,&day);//此写法可以不管数字中间输入什么单个字符都可以被接受printf("%d %d %d \n",year,month,day);printf("%d",num);return0;}5.2.5 printf 和 scanf 差异
- 在
printf函数中%f可以表示小数float和doubale,但是在scanf函数中,double必须使用%lf printf函数中,%[m]s,其中 m 是一个整数,代表最小宽度,而在scanf函数中表示读取字符串的最大长度
声明
本文是在鹏哥 C 语言集训营学习过程中所记录的学习笔记,梳理了核心知识点,同时也记录了本人实操验证的代码案例,供后续学习复盘使用。