C 语言学习历程:(第二章)数据类型和变量・程序的 “数据骨架”
第二章:数据类型和变量
文章目录
1. 数据类型及长度和取值范围
- signed 关键字,表示⼀个类型带有正负号,包含负值
- unsigned 关键字,表示该类型不带有正负号,只能表示零和正整数。好处是同样长度的内存能够表示的最大整数值增大一倍。
计算机存储的是二进制 0 和 1,存放一个 0 或者 1 需要一个比特位,一个字节为八个比特位
1.1 字符型
- char:1个字节
- [signed] char
- unsigned char
C 语言规定
char类型默认是否带有正负号,由当前系统决定。
一般默认char等同于signed char
1.2 整形
短整型:
- short [int]:2个字节
- [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>// 标准输入输出的头文件
int main()
{
// sizeof 不是类型的时候可以省略括号
int a = 99;
printf("%zd\n", sizeof(a));// sizeof 打印应该用 %zd 格式,sizeof函数的返回值类型是 size_t
printf("%zd\n", sizeof a);
printf("%zd\n", sizeof(int));
printf("%zd\n", sizeof(2 + 5));// sizeof的表达式不实际运算,而是根据类型进行推断
// 验证 sizeof表达式不实际计算
short b = 3;
printf("%zd\n", sizeof ( b = 10 + b));// 输出 2,类型是由 b 决定
printf("b = %d\n", b);// b = 3,由此可见 sizeof 表达式不进行计算
printf("%zd\n", sizeof 2 + 5);// 表达式省略括号后,先计算 2 的长度是 4 ,接着 + 5 = 9
// 数据类型长度
printf("%zd\n", sizeof(char));// 字符型 1
printf("%zd\n", sizeof(bool));// 布尔型 1
printf("%zd\n", sizeof(short));// 短整型 2
printf("%zd\n", sizeof(int));// 整型 4
printf("%zd\n", sizeof(long));// 长整型 4 C 语言标准规定 long >= int
printf("%zd\n", sizeof(long long));// 长长整形 8
printf("%zd\n", sizeof(float));// 单精度浮点型 4
printf("%zd\n", sizeof(double));// 双精度浮点型 8
printf("%zd\n", sizeof(long double));// 长双精度浮点型 8
return 0;
}
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>// 标准输入输出的头文件
// 全局变量,整个工程都可以使用
int public;
float local = 99.9f;//定义变量时给初始值叫初始化
int main()
{
//局部变量:代码块内声明,只能在本代码块使用
float local = 23.6f;// 小数默认为 double ,所以末尾加 f 表明 float 类型
printf("%f\n", local);// 全局变量与局部变量同名,优先局部变量
return 0;
}
全局变量和局部变量在内存中如何存储呢?
学习 C 语言的过程中,需要用到内存中的三个区域:栈区,堆区,静态区
- 栈区:局部变量和函数参数
- 堆区:动态内存管理
- 静态区:动态内存管理
为什么变量要初始化呢?
- 局部变量不初始化,他的值是随机的
- 全局变量不初始化,他的值会默认初始化为0
3. 操作符
3.1 算数操作符
+ - * / % 这些操作符都需要两个操作数,也被称为双目操作符。
注意:
/:两端若是整数,执行的是整除,得到的也是整数。如果希望执行浮点数除法,两个运算数必须至少一个为浮点数。
%:只能用于整数,不能用于浮点数,负数求模结果的正负号则有第一个运算数的正负号决定。
实操:
#include <stdio.h>// 标准输入输出的头文件
int main()
{
printf("%d\n", 6 + 4 );
printf("%d\n", 4 - 6 );
printf("%d\n", 6 * 4 );
int x = 6 / 4;// 两端整数执行的是整除,得到的也是整数,丢掉小数部分
printf("%d\n", x);// 1
float y = 6.0 / 4;// 只要一端为小数,执行的便是浮点数除法
printf("%f\n", y);// 1.500000
// 求模只能用于整数,不能有一方是浮点数
printf("%d\n", 6 % 4);
//负数求模,结果的正负号由第一个运算数的正负号决定
printf("%d\n", -6 % 4);// -2
printf("%d\n", 6 % -4);//2
printf("%d\n", -6 % -4);//-2
return 0;
}
3.2 赋值操作符
- 初始化:创建变量时给值
- 赋值:变量创建好后再给一个值
复合赋值符:
+=-=*=/=%=>>=<<=&=|=^=
实操:
#include <stdio.h>// 标准输入输出的头文件
int main()
{
//复合赋值符
int a1 = 10;
printf("%d\n",a1 += 3);// a1 = a1 + 3 13
printf("%d\n", a1 -= 3);// a1 = a1 -3 10
printf("%d\n", a1 *= 3);// a1 = a1 * 3 30
printf("%d\n", a1 /= 3);// a1 = a1 / 3 10
printf("%d\n", a1 %= 3);// a1 = a1 % 3 1
return 0;
}
3.3 单目操作符
顾名思义只有一个操作数:
-
++:自增- ++ 前置:先+1,后使用
- 后置 ++:先使用,再 +1
-
--:自减- – 前置:先 -1,再使用
- 后置 --:先使用,再 -1
-
+:正号 -
-:负号
实操:
#include <stdio.h>// 标准输入输出的头文件
int main()
{
int c1 = 10;
int d1 = ++c1;//前置++,先自增,再使用 :c1 = c1 + 1 d1 = c1
printf("c1:%d\n", c1);// 11
printf("d1:%d\n", d1);// 11
int c2 = 10;
int d2 = c2++;//后置++,先使用,再自增 :d2 = c2 c2 = c2 + 1
printf("c2:%d\n", c2);// 11
printf("d2:%d\n", d2);// 10
int c3 = 10;
int d3 = --c3;//前置--,先自减,再使用 :c3 = c3 - 1 d3 = c3
printf("c3:%d\n", c3);// 9
printf("d3:%d\n", d3);// 9
int c4 = 10;
int d4 = c4++;//后置--,先使用,再自减 :d4 = c4 c4 = c4 - 1
printf("c4:%d\n", c4);// 9
printf("d4:%d\n", d4);// 10
int c5 = 10;
int d5 = -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>// 标准输入输出的头文件
int main()
{
float x1 =(float) (6 / 4);// int 强转为 float ,小数点后补 6 个 0
//或者 float x1 =6 / 4; 底层默认就会转换
printf("%f\n", x1);// 1.000000
printf("%f\n", 6 / 4);// 不能通过占位符强转,int 类型使用 %f 占位符结果为 0.000000
int y2 = (int) (6.0 / 4);// double 强转为 int,丢掉小数部分
printf("%d\n", y2);// 1
printf("%d\n", 99.9 / 3);// 不能通过占位符强转,float 类型使用 %d 占位符结果为随机值
return 0;
}
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> // 标准输入输出的头文件
int main()
{
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);//多个占位符时,后面内容需要与占位符一一对应
return 0;
}
5.1.2 限定最小宽度:
- %[m]占位符:限定数据的最小宽度
#include <stdio.h>// 标准输入输出的头文件
int main()
{
printf("%d\n", 123);
printf("%+d\n", +123);//显示正负号号,正号默认不显示,%+d 显示
printf("%+d\n", -123);
// 指定整数最小宽度,默认右对齐(前面补零)
printf("%5d\n", 123);
printf("%-5d", 123);// 左对齐(后面补零)
printf("hehe\n");// 可以看到左对齐的效果
return 0;
}
5.1.3 限定小数位数:
- %.[m]f:指定小数部分位数
#include <stdio.h>// 标准输入输出的头文件
int main()
{
// 指定小数最小宽度
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 传给第二个*
return 0;
}
5.1.4 输出部分字符串:
%.[m]s:指定输出长度
#include <stdio.h>// 标准输入输出的头文件
int main()
{
//输出部分字符串
printf("%.5s\n", "hello world");// 输出前五个字符
printf("%[hello]", "hewordllo");//在方括号中指定⼀组匹配的字符,遇到不在集合之中的字符,匹配将会停止
return 0;
}
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 文件中添加。
*/
int main()
{
int age = 0;
int year = 0;
printf("请输入你的年龄和出生年份:");
//scanf 传递的不是值,而是地址,即将变量 age 和 year 的地址分别指向用户输入的值
//%d 接受输入数据的类型,& 取变量的地址值
scanf("%d/%d", &age, &year);//注意,此时第一个 %d 后有一个 / 说明键盘在两个整数之间也要输入 /
//例如输入: 18/ 23
// 第一个数据前面空格没有影响,但必须紧接着分隔符
printf("您的年龄是:%d\n", age);
printf("您的出生年份是:%d\n", year);
return 0;
}
scanf 处理用户输入的原理:
用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。 解读用户输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为止。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>// 标准输入输出的头文件
int main()
{
printf("%s", "请输入 x3 和 y3 的值:");
int x3 =0;
float y3 = 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 次方)
//因为 # 不属于小数,所以会停在这里,同时浮点数在内存中有可能无法精确保存导致结果有误差
return 0;
}
5.2.2 返回值
scanf() 的返回值是⼀个整数,表示从左到右成功读取的变量个数。 如果没有读取任何项,或者匹配失败,则返回 0 。 如果在成功读取任何数据之前,发生了读取错误或者遇到读取到文件结尾,则返回常量EOF(-1)。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>// 标准输入输出的头文件
int main()
{
int a4 = 0;
int b4 = 0;
float f4 = 0.0f;
int r4 = scanf("%d %d %f", &a4, &b4, &f4);
printf("a=%d b=%d f=%f\n", a4, b4, f4);
printf("r = %d\n", r4);
return 0;
}
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>// 标准输入输出的头文件
int main()
{
//在 scanf 中,%c 不忽略前面的空格,如果忽略要在前面加上空格
char a5 = 0;
scanf(" %c", &a5);
printf("%c----------\n", a5);
// scanf 接受字符串
// 其底层原理无法接受多个单词,假设单词之间有空白字符(空格,换行,制表符)识别到后便会自动加上 \0,除非多个 %s
// 字符串:字符数组
char array1[11];
char array2[11];
//在读取字符串不会检测数组的长度,所以需要指定长度确保安全
scanf("%10s", array1);//数组不需要取地址,因为数组名就是地址,格式 %[m]s ,指定长度为10,剩下的一个留给\0
scanf("%[^\n]s", array2);//此写法可以读取多个单词,遇到 \n 才会停止,但此时便不忽略前面的空格
//scanf("%10[^\n]s", array2);//读取多个单词的同时,限制读取数组的长度
printf("%s\n", array1);
printf("%s\n", array2);
return 0;
}
5.2.4 赋值忽略符
为了避免解析失败这种情况,可以使用赋值忽略符,把 * 加在任何占位符的百分号后⾯,便可忽略掉此占位符。该占位符不会返回值,解析后将被丢弃,同时 scanf 的返回值不包括被忽略的值。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>// 标准输入输出的头文件
int main()
{
// scanf 赋值忽略符
int year = 0;
int month = 0;
int day = 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 的返回值即不被忽略值的数量
int num=scanf("%d%*c%d%*c%d", &year,&month,&day);//此写法可以不管数字中间输入什么单个字符都可以被接受
printf("%d %d %d \n",year,month,day);
printf("%d", num);
return 0;
}
5.2.5 printf 和 scanf 差异
- 在
printf函数中%f可以表示小数float和doubale,但是在scanf函数中,double必须使用%lf printf函数中,%[m]s,其中 m 是一个整数,代表最小宽度,而在scanf函数中表示读取字符串的最大长度
第三章:分支与循环
声明
本文是在鹏哥 C 语言集训营学习过程中所记录的学习笔记,梳理了核心知识点,同时也记录了本人实操验证的代码案例,供后续学习复盘使用。
更多推荐



所有评论(0)