C语言提供了许多丰富的数据类型来描述我们生活中的各种数据,比如用int,short,long int表示整数,float,double表示小数,char表示字符,string表示字符串……
所谓类型,就是相似的数据所拥有的相同特征,只有编译器知道了数据的数据类型才能操作这个数据。
我们主要讲解内置类型
#include
int main(){
char x = 'a';
signed char y = 'b';
unsigned char z = 'c';
return 0;
}
int main() {
//短整型
short int a = 1;
signed int b = 1;
unsigned int c = 1;
//整型
int d = 1;
signed int e = 1;
unsigned int f = 1;
//长整型
long int g = 1;
signed long int h = 1;
unsigned long int i = 1;
//更长的整型(c99引入)
long long int j = 1;
signed long long int k = 1;
unsigned long long int l = 1;
return 0;
}
int main() {
float a = 1.0;
double b = 2.33;
long double c = 3.3333;
return 0;
}
float是单精度浮点型
double是双精度浮点型
long double是精度更高的浮点型
精度的区别在于占用字节的大小,而编译器不管是float还是double都是保留6为小数。
C语言原来没有为布尔值单独设置一个类型,而是使用整数0表示假,非零值表示真。
#include
#include
int main() {
_Bool type = true;
if (type) {
printf("hello\n");
}
return 0;
}
布尔类型的值只有两个:true和false
使用布尔类型必须包含头文件:#include
在编写代码时可以将_Bool写为bool
#include
#include
int main() {
bool type = true;
if (type) {
printf("hello\n");
}
return 0;
}
这是因为在库函数中编译了如下语句:
#define bool _Bool
#define false 0
#define true 1
在各大数据类型中,加signed和不加效果一样的,默认为有符号的数据类型
每一种数据类型都有自己的长度,使用不同的数据类型,能够创建出长度不同的变量,变量长度的不同,存储的数据范围就有所差距。
sizeof(类型)
sizeof 表达式
sizeof操作数是表达式时可以省略括号
sizeof后面的表达式不参与真实的运算,根据表达式的类型来确定大小
sizeof的计算结果是sizeof_t类型,sizeof_t是一种无符号整数,这种无符号整数在打印的时候用%zd作为占位符
int main() {
int a = 10;
printf("%zd\n", sizeof(a));
printf("%zd\n", sizeof(int));
printf("%zd\n", sizeof(2.3 + 3));
return 0;
}
在这串代码中a被赋值为10,而sizeof打印的是4,这是因为sizeof打印的是数据类型的字节数。而2.3+3的结果是小数,默认是double类型,所以字节数为8。下面是各大数据类型的类型长度。
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中的表达式不计算,这是因为sizeof的计算在运行前就已经确定,所以不管表达式为何值,只看先前的数据类型
int main() {
int a = 4;//int类型有4个字节
printf("%zd\n", sizeof(a = 3.14 + 1));
return 0;
}
3.14+1的结果为double类型的值,而输出的是4,这是因为sizeof没有运行表达式
sizeof运算符在运行表达式之前就已经被确定为int类型的a的字节数了
C语言使用signed和unsigned关键字修饰字符型和整型类型的。
signed关键字,表示一个类型带正负号,包含负值
unsigned关键字,标志一个类型不带正负号,只能表示0和正整数
对于int来说,该类型自动包含正负号,即signed int = int,signed一般省略,但如果要int类型只标志非负整数,就必须使用关键字unsigned。
unsigned int a=-1;
为什么要使用unsigned,这是因为同样长度的内存可以表示两倍的最大整数值。
比如16位的signed short 的取值范围是:-32768~32767,而unsigned short 的取值范围是:0~65535
注意
C语言中的char类型默认是否带有正负号,有当前系统决定。
也就是说 char不等同于signed char,它可能是signed char也可能是 unsigned char。
与int不同,int就等同于signed int
每一种数据类型都有自己的取值范围,可以在不同的场景选取不同的数据类型
limits.h文件说明了整型类型的取值范围
float.h文件说明了浮点数的取值范围
类型的使命就是创建变量,而变量就相当于是一个容器,用来存储信息。
C语言把经常变化的值称为变量,把不变的值称为常量
int main() {
int a;
double b;
char c = 'a';
return 0;
}
int a就相当于创建变量,给变量赋值称为初始化
int x = 10;
int main() {
int y = 1;
printf("%d\n", x);
printf("%d\n", y);
return 0;
}
在这段代码中,x为int泪下地全局变量,可以在全局使用,而y是局部变量,只能在大括号内使用。那如果局部变量和全局变量的名称相同,应该打印谁呢?
int x = 10;
int main() {
int x = 1;
printf("%d\n", x);
printf("%d\n", x);
return 0;
}
可以看到两个都打印的是局部变量,这是因为当全局变量和局部变量同名时,局部变量优先使用 。
当我们的程序涉及到计算时就需要用到一组操作符,叫做:算数操作符(运算符),分别是:+,-,*,/,%,这些操作符都是双目运算符。
+和-用来完成加法和减法
#include
int main(){
int x = 2+1;
int y = 2-1;
return 0;
}
运算符*用来完成乘法
#include
int main(){
int x = 2*1;
return 0;
}
运算符/用来完成除法
#include
int main() {
double x = 6 / 4;
int y = 6 / 4;
printf("%f\n", x);//输出1.000000
printf("%d\n", y);//输出1
return 0;
}
x是浮点数,结果却是1.000000,而不是1.5,这是因为C语言中整数除法是整数,只返回整数部分,丢弃小数部分,要想输出浮点数,则必须保证至少除号两边有一个是小数,这时就会变为浮点数的计算。
#include
int main() {
double x = 6.0 / 4;
int y = 6 / 4;
printf("%f\n", x);//输出1.5
printf("%d\n", y);//输出1
return 0;
}
运算符%用来完成求余(取模)
%运算符只能用于整数,不能用于浮点数
#include
int main(){
int x = 6 % 4;//2
return 0;
}
负数求模的规则是:结果的正负号由第一个运算数的正负号决定
#include
int main(){
printf("%d\n",6 % 4);//2
printf("%d\n",6 % -4);//2
printf("%d\n",-6 % 4);//-2
printf("%d\n",-6 % -4);//-2
return 0;
}
在变量创建的时候给一个初始值叫做初始化,在变量创建好后,再给一个值,叫做赋值
int a = 100;//初始化
a = 10;//赋值
赋值操作符可以连续赋值
int a=3;
int b=4;
int c=5;
c=b=a+3;//从右向左依次赋值
写代码时经常会用到自增自减的操作,可以写如下代码
int a = a + 1;
int b+ = 1;
两条语句的作用都是自增一
其他的复合赋值符为:+=,-=,*=,/=,%=,>>=,<<=,&=,|=,^=
++是一种自增的操作,和+=效果相同,它又分为前置++和后置++
–是一种自减的操作,和-=效果相同,它又分为前置- -和后置- -
前置++计算口诀:先加1,再使用
#include
int main() {
int a = 2;
int b = ++a;
printf("%d", b);//3
return 0;
}
后置++计算口诀:先使用,再加1
#include
int main() {
int a = 2;
int b = a++;
printf("%d", b);//2
return 0;
}
前置- -计算口诀:先减1,再使用
#include
int main() {
int a = 2;
int b = --a;
printf("%d", b);//1
return 0;
}
后置- -计算口诀:先使用,再减1
#include
int main() {
int a = 2;
int b = a--;
printf("%d", b);//2
return 0;
}
在操作符中还有一种特殊的操作符是强制类型转换
如果编译:
int a = 3.14;
改代码会报错,3.14是double类型的变量,两边变量的类型不匹配。
如果编译:
int a = (int)3.14;//值为3
这段代码不会报错,强制将double转换为int,小数部分被丢弃。
printf函数的作用是将参数文本输出到屏幕上。它名字中的print意思是“打印”,f的意思是format(格式化),表示可以定制输出文本的格式。
printf函数不会在末尾自动换行,必须在末尾加上换行符“\n”
printf可以在输出文本中指定占位符
所谓占位符,就是这个位置的值可以由其他值代入
#include
int main() {
int year = 22;
printf("I am %d years old", year);//输出I am 22 years old
}
因为如果在复杂代码中改值不方便,所以使用占位符用以方便改变值
printf中参数与占位符是一一对应的关系,如果有n个占位符,那么就有n+1个参数(n个占位符加上1个双引号引起来的参数)
所有的占位符:
printf()函数 允许限定占位符的最小宽度
#include
int main() {
printf("%5d", 123);//输出“ 123”
return 0;
}
%5d表示最少宽度为5,超过5则按原样打印,如果未超过5,就用空格向左补齐,上述代码输出两个空格和123.
如果想要右对齐,则只需在占位符中间加符号
#include
int main() {
printf("%-5d", 123);//输出“123 ”
return 0;
}
上述代码在123后面补了两个空格
对于浮点数,这个限定符会限制所有数字的最小显示宽度
#include
int main() {
printf("%12f", 123.45);//输出“ 123.450000”
return 0;
}
上述结果向左补齐了12位,因为浮点数默认带六位小数
还可以在占位符中间加上正号用以一直显示正数
#include
int main() {
printf("%+d", 123);//输出“+123”
printf("%+d", -123);//输出“-123”
return 0;
}
还可以限制小数的位数:只需要在占位符中间加上"."+保留的位数
#include
int main() {
printf("%.2f", 123.4);//输出“123.40”
return 0;
}
还可以和限定宽度的占位符结合使用
#include
int main() {
printf("%9.2f", 123.4);//输出“ 123.40”
return 0;
}
%9.2f中9表示向左补位,总共占9格,.2f表示保留两位小数
这两个值可以用*代替,通过传参来使用
#include
int main() {
printf("%*.*f",9,2,123.4);//输出“ 123.40”
return 0;
}
当我们有了变量,我们需要自定义变量的值就可以使用scanf函数
scanf函数用于读取用户的键盘输入,程序运行到这条语句时,会停下来等待用户输入,用户输入数据后,按下回车键,scanf就会处理输入,将数据存入变量中。
scanf必须知道变量的类型,才能处理数据 变量前面必须加上&运算符(指针变量除外),因为scanf()传递的不是值,而是地址。
scanf()处理占位符时,会自动过滤空白字符,包括空格,制表符,换行符等
int main() {
int a = 0;//初始化
scanf("%d", &a);//输入值
printf("number is %d\n", a);
return 0;
}
scanf()的返回值是一个整数,表示成功读取的变量个数
如果没有读取到任何项,或者匹配失败,则返回0,如果在成功读取之前,发生了读取错误或者读取到文件末尾,则返回常量EOF
int main() {
int a = 0;
int b = 0;
double c = 0.0;
int r = scanf("%d%d%lf", &a, &b, &c);
printf("a=%d b=%d c=%lf\n", a, b, c);
printf("%d\n", r);
return 0;
}
这里成功读取到三个变量
在scanf()的占位符中,除了 %c以外,都会自动忽略起首的空白字符
%c不忽略空白字符,总是返回第一个字符,无论是否为空格
如果要强制跳过空格,则可以写为scanf(" %c",%ch),即%c前面加上一个空格,表示跳过0个或多个空白字符。
int main() {
char ch;
scanf("%c", &ch);
printf("%c", ch);
return 0;
}
当在输入时前面加上空格也会读取到空格
%s不能简单等同于字符串,它的规则是:从当前第一个非空字符读取,碰到空白字符就停止
scanf()函数在遇到%s时会在末尾添加一个空字符\0表示结束
int main() {
char ch[10] = { 0 };
scanf("%9s", &ch);//防止溢出
printf("%s", ch);
return 0;
}
上述结果说明scanf跳过了前面的空格而遇到f后面的空格时停止读取。%9的作用是防止溢出,如果不添加限制,程序会崩溃,这是scanf函数不安全的地方。
scanf()函数的占位符与占位符之间最好不要添加其他字符,如果添加了其他字符,就必须在键盘上按照这个格式输入相同的格式才行,比如scanf(“%d-%d”,&a,&b);就必须输入比如2-3这种格式才行,如果要输入不符合预定的格式,我们可以使用赋值忽略符
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,就不会返回值。
在vs中scanf函数被认为是不安全的函数,必须使用scanf_s函数才能正常使用输入功能,至于为什么scanf函数不安全还有在vs中如何使用scanf函数,可以查看:
这儿