目录
数据类型
内置类型就是平时日常生活中用的多的类型,比如数学中的整数、小数,英语中的字母。
一个或多个内置类型放在一起,就构成了自定义类型。举个例子,一个人的姓名ZhangSan,就是自定义类型中的数组(多个字符组成的集合)。自定义类型也可以嵌套(自定义类型跟内置类型组成一个新的自定义类型),以后再讲。
整型
整型有很多,这里先介绍int。int是整形,就是整数,具体点呢就是有符号的整数,它的全称是signed int。为了简便,signed可写可不写
#include<stdio.h>
int main()
{
int a = -5;
printf("%d\n", a);
signed int b = -99;
printf("%d\n", b);
return 0;
}
运行结果:
int是有符号的,可以表示正数、负数、0,那有没有无符号的呢?答案是有,unsigned int
#include<stdio.h>
int main()
{
unsigned int a = -5;
printf("%u\n", a); //打印 int 用 %d,打印 unsigned int 要用 %u
return 0;
}
运行结果:
我把-5存在了unsigned int类型中,结果却没有打印-5,而是一个很大的数字。至于为啥是这个数,要学了原码、反码、补码才能给你解释。
如果你把上面代码中的%u换成%d,结果会打印出-5。至于为啥,enmm,就 不 告 诉 你~~
算了直接看下面这段代码:
#include<stdio.h>
int main()
{
int a = -5;
printf("a以d的形式打印:%d\n", a);
printf("a以u的形式打印:%u\n", a);
unsigned int b = -5;
printf("b以d的形式打印:%d\n", b);
printf("a以u的形式打印:%u\n", b);
return 0;
}
运行结果:
哎嘿!结果竟然出奇的一致!可是我明明一个用int类型储存-5、一个用unsigned int类型储存-5啊!心机之蛙一直摸你肚子——这两个是同一个东西!在底层,变量是以二进制形式储存在计算机中,代码中的a和b在内存中储存的是同一个二进制序列!%d与%u只是将同一个二进制序列以不同的形式打印出来!可恶,绕了半天结果告诉我int跟unsigned int存的是“同一个东西”。
我对这两者之间的区别理解的并不是很深,只能告诉你一个是有符号,一个无符号,int的取值范围是-2^31 ~ 2^31 - 1,unsigned int的取值范围是0~2^31。如果你有更深刻的见解,欢迎在评论区交流!
对了,取值范围不用刻意去记,直接用代码就行,换成全大写的int再加上下划线再加上全大写的max就表示int的最大值,不过要包含下头文件limits.h
#include<stdio.h>
#include <limits.h>
int main()
{
int a = INT_MAX;
unsigned int b = UINT_MAX;
printf("%d\n", a); //2147483647
printf("%u\n", b); //4294967295
return 0;
}
好了,接下来我们该跟整型家族暧昧一下了。打印下面各种整型的占位符(%d还是%ld啊)无需刻意去记,不知道的就百度一下就行了。
短整型 | 整型 | 长整型 | 长长整型(C99引入) |
short [int] | int | long [int] | long long [int] |
[signed] short [int] | [signed] int | [signed] long [int] | [signed] long long [int] |
unsigned short [int] | unsigned int | unsigned long [int] | unsigned long long [int] |
哇!这么多的吗?实际上用的基本上都是int,打比赛或者刷题数据可能会溢出,换成long long就差不多了,别的类型用的不多。上面的表格中[ ]表示里面的东西可写可不写。比如long [int],可以写成long,也可以写成long int,这两者代表的是一个意思。上面四种,前面没加unsigned的,都是有符号整型,加了unsigned的,都是无符号整型。至于这四者之间的区别,上节已经讲过了,表中从左往右能够储存的数据越来越大,比如short存不下20亿,int就行。
#include<stdio.h>
int main()
{
short a = 2000000000;
printf("%d\n", a);
int b = 2000000000;
printf("%d\n", b);
return 0;
}
运行结果:
浮点型、布尔型
float | double | long double |
浮点型不象整型那个繁多,没有unsigned float、unsigned double等。
注:打印浮点型别错用了占位符
#include<stdio.h>
int main()
{
float b = 3.14;
printf("%d\n", b);
printf("%f\n", b);
return 0;
}
运行结果:
%d是打印整数的,我原本以为用%d打印小数只会保留小数的整数部分,结果却不是的,后面章节会讲为啥是这样。
布尔型只有两种状态:非0表示真true, 0表示假false
通过监视窗口我们就能看出,a跟b都是非0,所以是true,c是0,对应false。细心的通知发现我在前面多包涵了stdbool.h的头文件,bool类型是在C99时引入的,专门表示真假,要用到bool类型就要包含一下头文件哈。
监视是啥?我怎么打开这个东东?在文章最后有讲解。
下面介绍一个简单的语句 -- if( )
if语句就是:括号内是判断条件,判断条件为真,就会输出紧接着的下一个语句,如果有多条语句要输出,就用花括号{ }括起来,当然只有一条语句要输出 也可以用花括号(如第二个if语句)。
#include<stdio.h>
#include<stdbool.h>
int main()
{
bool a = true;
if(a)
printf("C语言是世界上最好的语言!.cpp\n");
bool b = false;
if(b)
{
printf("C++是世界上最好的语言!.py\n");
}
bool c = true;
if(c)
{
printf("C跟C++都是世界上最好的语言!\n");
printf("代码和我总有一个是能跑的!\n");
}
return 0;
}
运行结果:
第一个if语句中a是真,就会执行下面紧接着的printf;第二个if中b是假,就不执行,所以只会打印第一个printf里的内容。
新手我建议统一用花括号{ }。比如看下面的代码:
#include<stdio.h>
#include<stdbool.h>
int main()
{
bool a = false;
if(a)
printf("C语言是世界上最好的语言!.cpp\n");
printf("C++是世界上最好的语言!.py\n");
return 0;
}
运行结果:
明明if括号里的判断条件是假,为什么还会有输出呢?那是因为if只管一条语句。
字符型
char | [signed] char | unsigned char |
字符型记住要用单引号。下面这段代码我想创建啊一个char类型变量储存字符a,可是我用的是双引号,导致我打印变量c时啥都没有输出。
变量
C语言中把经常变化的值称为变量,不变的值称为常量。变量创建的语法形式是这样的:
data_type name;
| |
数据类型 变量名
变量在创建的时候就给⼀个初始值,就叫初始化。如下:
int age = 18;
char ch = 'w';
double weight = 48.5;
unsigned int height = 100;
变量的分类
- 全局变量。全局变量的使用范围更广,整个⼯程中想使用,都是有办法使⽤的。
- 局部变量。局部变量的使用范围是比较局限,只能在自己所在的局部范围内使用的。
#include<stdio.h>
int a = 100; //全局变量
int main()
{
int b = 10; //局部变量
printf("a: %d, b: %d\n", a, b); //全局变量在局部可以使用
return 0''
}
但下面这段代码就有问题,变量c是在if语句内创建的,却在语句外使用,会直接编译错误
全局变量跟局部变量名字相同时,优先使用局部变量
但是不能在同一个作用域下创建两个名字相同的变量,会直接报错
变量的存储位置
其实内存区域的划分会更加细致,以后在操作系统的相关知识的时候会介绍。
sizeof关键字
这家伙是用来计算数据所占内存的大小,单位是字节Byte,简称B(大写字母B)。(还有个单位是比特bit,简称b(小写字母b)。1字节=8比特,这两种不要混淆了)。sizeof的用法,看代码:
#include<stdio.h>
int main()
{
int a = 10;
float b = 3.14;
double b1 = 3.52;
char c = 'a';
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(b));
printf("%d\n", sizeof(b1));
printf("%d\n", sizeof(c));
return 0;
}
运行结果:
也就是说,a跟b分别占了4个字节,b1占了8个字节,c占1个字节
上面的代码也可以这样写,括号里面放数据类型,运行结果是一样的
#include<stdio.h>
int main()
{
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
printf("%d\n", sizeof(char));
return 0;
}
再奇葩一点,我甚至可以放一个式子!不过这种情况很少遇见,只需了解。
#include<stdio.h>
int main()
{
int a = 10;
char c = '1';
long long d = 99;
printf("%d\n", sizeof (a + c)); // 4
printf("%d\n", sizeof (a + d)); // 8
printf("%d\n", sizeof (c + d)); // 8
printf("%d\n", sizeof (10 + 11)); // 4
printf("%d\n", sizeof (a = 22)); // 4
printf("%d\n", a); // 10
return 0;
}
方便起见,我把对应的运行结果用注释写上了。
sizeof( )计算式子的结果取决于占内存空间最大的那个变量(谁最大谁有理)。举个例子,sizeof(a+c)中,a是int类型 占4个字节,c是char类型 占1个字节,占空间最大的是a,所以输出4;sizeof (a + d))中,a占4字节,d占8字节,占空间最大的是d,所以输出8。
最后一行呢?为什么输出10?我在上一行明明写了a=22啊(这条语句意思是把22赋值给a),因为sizeof后面的表达式是不参与计算的,sizeof只计算你占内存多大,压根不管你里面的式子。
上面的代码我都是直接打印结果,我能不能sizeof计算的结果保存在一个变量中呢?要用什么类型的变量储存呢?int吗?int有负数,变量占的内存能为负数吗?那是不是就要用unsigned int呢?
下面这段是详细的解释,你可以直接看黑体字部分
sizeof 运算符的返回值,C语⾔只规定是⽆符号整数,并没有规定具体的类型,⽽是留给系统⾃⼰去决定,sizeof 到底返回什么类型。不同的系统中,返回值的类型有可能是unsigned int ,也有可能是unsigned long ,甚⾄是unsigned long long ,对应的printf() 占位符分别是%u 、%lu 和%llu。这样不利于程序的可移植性。C语⾔提供了⼀个解决⽅法,创造了⼀个类型别名size_t ,⽤来统⼀表示sizeof 的返回值类型。对应当前系统的sizeof 的返回值类型,可能是unsigned int ,也可能是
unsigned long long 。
#include<stdio.h>
int main()
{
int a = 10;
size_t sz = sizeof(a);
printf("%zd\n", sz); //运行结果:4
//注:size_t类型的占位符是%zd
return 0;
}
总结一下
- sizeof( )括号里可以放变量名,也可以放数据类型,还可以放表达式,表达式的括号还可以省略。如:sizeof a + c (这个留给读者验证)!
- sizeof后面的表达式是不参与计算的
- sizeof 的计算结果是size_t 类型的
算数操作符
+、-、*、/、%
前四个就是数学运算,这里先介绍 / 。除号两边都是整数,则结果为整数,有一个为小数,则结果为小数。
#include<stdio.h>
int main()
{
int a = 22;
int b = 3;
float c = 2.2;
printf("%d\n", a / b);
printf("%f\n", a / c);
return 0;
}
运行结果:
第一个结果为7?22除以3的结果是7.33...,由于a跟b都是整数,所以结果只保留了整数部分。
再看一个例子:
#include <stdio.h>
int main()
{
int a = 5;
int b = (a / 20) * 100;
printf("%d\n", b);
return 0;
}
按数学逻辑,b的值应该是25,但结果却是0,因为5 / 20的整数部分是0,再乘100还是0
% 表示求模运算,即返回两个整数相除的余数。这个运算符只能用于整数,不能用于浮点数。
#include <stdio.h>
int main()
{
int x = 6 % 4;
printf("%d\n", x); // 2
return 0;
}
负数求模的规则是,结果的正负号由第⼀个运算数的正负号决定。
#include <stdio.h>
int main()
{
printf("%d\n", 11 % -5); // 1
printf("%d\n",-11 % -5); // -1
printf("%d\n",-11 % 5); // -1
return 0;
}
你可以这样计算:把负数全部变成正数 然后求余数,再根据第一个运算数的符号写出最终结果
赋值操作符=(从右往左)
#include <stdio.h>
int main()
{
int a = 10; //初始化,把10赋值给a
a = 200; //又把200赋值给a,会把a之前储存的10覆盖掉
printf("%d\n", a); //输出: 200
return 0;
}
连续赋值:
#include <stdio.h>
int main()
{
int a = 10;
int b = 5, c = 7;
c = b = a + 1; //先把a+1赋值给b,即b=11,再把b赋值给c,即c=11。是从右往左
printf("a: %d\n", a); // 10
printf("b: %d\n", b); // 11
printf("c: %d\n", c); // 11
return 0;
}
复合赋值:再代码中,我们可能会对一个变量进行自增、自减操作
#include <stdio.h>
int main()
{
int a = 10;
a = a + 30; //把a+30赋值给a.相当于a又增加了30
printf("a: %d\n", a); //40
return 0;
}
C语言提供了更简便的写法:+=,注意这个整体是一个操作符,中间不可以有空格
#include <stdio.h>
int main()
{
int a = 10;
a += 30;
//a + = 30; 不可以有空格
printf("a: %d\n", a);
return 0;
}
a += 30 <==> a = a + 30
a -= 30 <==> a = a - 30
a *= 30 <==> a = a * 30
a /= 30 <==> a = a / 30
a %= 30 <==> a = a % 30
单目操作符++、--
啊?啥是单目?先介绍一下双目操作符吧,上面介绍的加减乘除取模,操作符两边都有一个数比如a+b,加号左边是a 右边是b,有两个变量就是双目操作符。那单目操作符就是只有一个变量呗
++是⼀种⾃增的操作符,⼜分为前置++和后置++,--是⼀种⾃减的操作符,也分为前置--和后置--.
前置++:先+1,后使用
#include <stdio.h>
int main()
{
int a = 10;
int b = ++a; //++的操作数是a,是放在a的前面的,就是前置++
printf("a=%d b=%d\n", a, b);
return 0;
}
运行结果:
后置++:先使用,后+1
#include <stdio.h>
int main()
{
int a = 10;
int b = a++; //++的操作数是a,是放在a的后面的,就是后置++
printf("a=%d b=%d\n", a, b);
return 0;
}
运行结果:
前置--:先-1,后使用。后置--:先使用,后-1。这个就留个读者验证了~
强制类型转换
直接看代码,a是整型,我偏偏想用%f打印成小数,就在a前面加上(float),表示把a强制转换成float类型。俗话说,强扭的⽠不甜,我们使⽤强制类型转换都是万不得已的时候使⽤,如果不需要强制类型转化就能实现代码,这样⾃然更好的。
printf()制定占位符的输出格式
上一节提到打印小数时我只想保留一位小数就要加上.1
printf("%.1f", 3.14); //输出:3.1
当然,打印字符串也有类似用法。比如我想打印字符串的前5个字符:
printf("%.5s\n", "hello world"); //输出:hello
其实打印整数也可以限定
第二个printf的占位符是%5d,意思是至少打印5位数,位数不够就在前面添空格。右对齐。
上面的代码用的是%-5d,意思是至少打印5位数,位数不够就在后面添空格。左对齐。
scanf
如果我想写个代码计算99*99,可能会这样:
#include <stdio.h>
int main()
{
printf("%d\n", 99 * 99); // 9801
return 0;
}
那如果计算别的乘法呢?比如我想任意输入两个数,让代码帮我算。。。
这就要用到scanf()函数了,它用于读取用户输入,在头文件stdio.h中。程序运行到这个语句时,会停下来等待用户输入
#include <stdio.h>
int main()
{
int a, b;
printf("请输入两个整数,用空格隔开\n");
scanf("%d %d", &a, &b); //输入:123 56
//scanf("%d %d\n", &a, &b); 这行是错误写法,不要在最后加上\n
printf("%d\n", a * b);
return 0;
}
scanf跟printf不同的是,它在变量前面加上了&(取地址符),scanf从屏幕读取数据后要存放哪里?你告诉它地址 它才能通过地址找到对应的变量进而把数据存放在里面。
注意:
-
scanf双引号里面的东西要跟用户输入的内容严格一致
上面的代码为啥算的结果是99呢?仔细看scanf双引号里面是%d,%d,想得到正确结果中间应当是逗号,而不是空格。而且代码中是英文逗号,你就要输入英文逗号;是中文符号,你就要输入中文符号。要严格一致。
这样输入才对嘛!
- scanf会自动过滤空白字符,包括空格、制表符、换行符
我先输入99,敲下回车键后在输入88,也能得到答案。甚至可以像下面这样输入:
虽然中间隔了好多空格,但scanf会过滤掉它们。
但有个例外,%c不会忽略空白符:
我输入a时不小心按了一下空格,导致并没有打印出字母a,而是打印了空格。
如果要强制跳过字符前的空白字符,可以写成scanf(" %c", &ch) ,即%c 前加上⼀个空格,表
示跳过零个或多个空白字符。如下面的代码
要特别说明一下%s,它的规则是,从当前第⼀个非空白字符开始读起,直到遇到空白字符(即空格、换⾏符、制表符等)为止
#include <stdio.h>
int main()
{
//因为字符串是数组,所以要用数组储存。
char c[11]; //这行代码表示我创建了一个字符数组c,最多存储11个字符
scanf("%s", c); //输入:hello world
printf("%s\n", c); //输出:hello
return 0;
}
第一份非空白字符是h,往后第一个空白字符是空格,所以只读取了hello
- scanf的返回值:返回值就是成功读取的数据个数
上面的代码成功读取两个数,所以返回值是2
上面的代码我输入99,然后想提前终止,就输入Ctrl+Z,这样scanf就只读取了一个数据,所以返回值是1
好了,以上就是本节全部内容了!有疑问的话欢迎在评论区留言哈!