文章目录
C语言笔记2:C语言数据类型和变量
一、数据类型介绍
C语言提供了不同的数据类型,编译器根据数据类型的不同,对一块数据进行不同的操作,比方说:对一个浮点数类型的数据进行加法运算和对一个整数类型的数据进行加法运算,其运算过程是肯定不一样的。

但是在我看来,其实内置类型只有整型和浮点类型两大类,字符型和布尔类型只不过是特殊的整型而已
1.字符型
c
//[]表示可写可不写
[signed] char //有符号的
unsigned char //⽆符号的
char和int等整型不同,char是否能表示负数是未定义的,这是由编译器决定。而int默认就是signed int。
2.整型
c
//短整型
[signed] short [int]
unsigned short [int]
//整型
[signed] int
unsigned int
//长整型
[signed] long [int]
unsigned long [int]
//更长的整型
[signed] long long [int]
unsigned long long [int]
为什么要有 long long 呢? 长整型和更长的整型区别在哪?
这其实和C语言标准制定有关
C语言标准:
| 类型 | 最小范围 | 对应位数 |
|---|---|---|
| char | -127到127 | 8位 |
| short | -32767到32767 | 16位 |
| int | -32767到32767 | 16位 |
| long | -2147483647到2147483647 | 32位 |
| long long | -9223372036854775807到9223372036854775807 | 64位 |
为什么是-127而不是-128呢?这是因为这是最小范围 ,早期有些机器使用原码或者反码来表示整数,这也就意味着需要有**+0,-0**。而现在大多数机器都是使用补码,所以现在实际范围大概率会是**-128到127**。
为什么C语言标准要规定最小范围,而不是直接定死一个固定的大小呢?设想一个场景,假设你的机器是16位的处理字长,此时int被编译器编译成最符合你机器的大小也就是16位,几年后,32位的机器出现了,你想把你写好的代码全部放在32位的机器上跑,可是32位机器上处理整型最快的大小应该是32位,所以为了提高程序运行速度,32位机器上的编译器把你的int数据定义成了32位也就是4个字节大小。这样,你的程序就完成了跨平台,并且在新平台上也能充分发挥新硬件的优势。
long long 的出现就是为了解决C语言long类型在某些平台上不够用的问题,由于long在windows操作系统上是4个字节大小,这导致了一些数据无法存储,但是又不能直接把long的大小改成8个字节,因为这会让编译器无法编译之前的代码,有些老程序不会轻易修改,但是新增某些模块还是需要的,所以干脆就定了一个新的数据类型long long 来解决问题。
3.浮点型
c
float
double
long double
float c = 0.0f;//f表示0.0是个float类型,0.0表示double类型,0.0L表示long double类型
4.布尔类型
C99才引入,包含头文件<stdbool.h>
5.各种数据类型的长度
sizeof操作符的返回值C语言标准没有规定,可能会返回
unsigned int,unsigned long,unsigned long long,是的,这又带来了可移植性问题,为了解决标准不严格规定的问题,C语言又提供了一个别名size_t类型,size_t实际类型因系统而异,但是却保证了程序在系统A上能跑,在系统B上也不会报警告。测试不同数据类型的大小(windows ,VS2022,x64):
c
#include <stdio.h>
#include <stdbool.h>
int main()
{
printf("%zd\n", sizeof(char));
printf("%zd\n", sizeof(bool));
printf("%zd\n", sizeof(short));
printf("%zd\n", sizeof(int));
printf("%zd\n", sizeof(long));
printf("%zd\n", sizeof(long long));
printf("%zd\n", sizeof(float));
printf("%zd\n", sizeof(double));
printf("%zd\n", sizeof(long double));
return 0;
}
结果:

sizeof中的表达式不计算
c
#include <stdio.h>
int main()
{
short s = 2;
int a = 3;
printf("%zd\n",sizeof(s = a + 1));
printf("%hd",s);
return 0;
}
结果:

二、数据类型的取值范围
limits.h:这个头文件说明了整型类型的取值范围
float.h:这个头文件说明了浮点类型的取值范围
三、变量
c
int age = 18;
char ch = 'w';
double weight = 48.0;
unsigned int height = 100;
在创建变量的时候赋值,叫做初始化,总是初始化变量是一个好的习惯,这会避免很多意想不到的错误
四、全局变量和局部变量
- 全局变量:定义在大括号外
- 局部变量:定义在大括号内
全局变量和局部变量命名冲突:
c#include <stdio.h> int n = 5; int main() { int n = 10; printf("%d\n",n); return 0; }结果:

说明:局部变量和全局变量冲突时,优先使用局部变量
变量的存储位置

五、算术操作符
+、-、*这些运算符没什么好介绍的。
除法
除法:/
规则1:整数相除只会得到整数,并且向0截断,丢弃小数部分。
c#include <stdio.h> int main() { float x = 6/4; int y = 6/4; printf("x=%f,y=%d\n",x,y); return 0; }结果:

规则2:两个运算数中只要有一个是浮点数,那么结果也是浮点数
c#include <stdio.h> int main() { float x = 6.0/4; printf("%f\n",x); return 0; }结果:

模运算
规则1:取模运算只能用于整数
规则2:取模运算结果的正负性由第一个运算数的正负号决定
c#include <stdio.h> int main() { printf("%d\n",6%4); //+ printf("%d\n",-6%4); //- printf("%d\n",6%-4); //+ printf("%d\n",-6%-4); //- return 0; }结果:

六、++ 和 - -
前置++:先++,再使用
c#include <stdio.h> int main() { int a = 1; printf("%d\n",++a); printf("%d\n",a); return 0; }结果:
后置++:先使用,再++
c#include <stdio.h> int main() { int a = 1; printf("%d\n",a++); printf("%d\n",a); return 0; }结果:
七、scanf 和 printf
常见占位符列举:
字符:
- %c:字符 //char
整型:
- %d:整型//int
- %hd:短整型//short [int]
- %ld:长整型//long [int]
- %lld:长长整型//long long [int]
- %u:无符号整型//unsigned int
- %hu:无符号短整型//unsigned short [int]
- %lu:无符号长整型//unsigned long [int]
- %llu:无符号长长整型//unsigned long long [int]
小数:
- %f:小数//float
- %lf:小数//double
- %Lf:long double类型浮点数
特殊:
- %p:指针
- %s:字符串
- %x:十六进制整型
- %o:八进制整型
- %zd:size_t类型
定制格式
设置宽度
整型:
c
#include <stdio.h>
int main()
{
printf("%5d,%5d\n",123,45);
return 0;
}
结果:

浮点型:
c
#include <stdio.h>
int main()
{
printf("%12f",123.45);
return 0;
}
结果:

设置对齐
c
#include <stdio.h>
int main()
{
printf("%-5d,%-5d\n",123,45);
return 0;
}
结果:

设置填充
c
#include <stdio.h>
int main()
{
printf("%05d,%05d\n",123,45);
return 0;
}
结果:

填充和设置左对齐没办法一起用,因为左对齐了之后,后面填充0岂不是平白增加位数。简记:开头可加0或-。
设置正号显示
c
#include <stdio.h>
int main()
{
printf("%+d\n",10);
return 0;
}
结果:显示+10
设置小数位数
c
#include <stdio.h>
int main()
{
printf("%012.2f",123.45);
return 0;
}
以
.为分界线,左边是宽度,后边是小数位数。
结果:

占位符参数
c
#include <stdio.h>
int main()
{
printf("%0*.*f",12,2,123.45);
return 0;
}
*表示要传递参数。
结果:

指定输出的字符串长度
c
#include <stdio.h>
int main()
{
char word[128] = "hello world";
printf("%.5s\n",word);
return 0;
}
结果:

不管传入多大的字符串,我最多只输出5个。
scanf
基本用法:
c
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
float k = 0.0f;
long double n = 0.0L;
scanf("%d%d%f%Lf",&i,&j,&k,&n);
printf("%d,%d,%f,%.7Lf\n",i,j,k,n);
return 0;
}
结果:

其实scanf的工作就是检查用户输入的字符串,提取其中内容填充到占位符中,比如用户输入" 1 2###3.14???6.666",从前向后要填充到%d,%d,%f,%.7Lf,中去,它会先扫描跳过前面的空白字符,然后读取1,然后读到空格,发现空格不属于整型类型的字符,于是第一个占位符就被填好了,依次类推。
scanf的返回值
c#include <stdio.h> int main() { int a = 0; int b = 0; float c = 0.0f;//f表示0.0是个float类型,0.0表示double类型,0.0L表示long double类型 int r = scanf("%d %d %f",&a,&b,&c); printf("a=%d,b=%d,c=%f\n,r=%d\n",a,b,c,r); return 0; }
结果1(只输入两个数据就退出):
结果2(什么都不输入直接退出):

结果3(输入了一堆无关紧要的值):

说明:scanf返回成功读取到的数据
返回-1(EOF)的情况:
- 什么都没读取到就退出
- 读到文件末尾
返回0的情况:
- 没有数据匹配得上,尽管读取到了数据,但是写不进占位符里


