数据类型
为什么对数据进行分类:节约存储空间,提高运行速度。
整型
类型 | 字节数 | 范围 |
---|---|---|
(signed) char | 1 | -128~127 |
(signed) short | 2 | -32678~32767 |
(signed) int | 4 | 正负20亿 |
(signed) long | 4/8(电脑位数决定) | 正负20亿/9开头19位整数 |
(signed) long long | 8 | 9开头19位整数 |
unsigned char | 1 | 0~255 |
unsigned short | 2 | 0~65535 |
unsigned int | 4 | 0~40亿 |
unsigned long | 4/8 | 0~40亿/1开头20位整数 |
unsigned long long | 8 | 1开头20位整数 |
浮点
类型 | 字节 |
---|---|
float | 4 |
double | 8 |
long double | 12/16 |
注意:采用科学计算法,二进制与真实数据之间需要翻译,因此计算整数要比整型要慢。小数点后六位有效。
模拟
char
字符其实就是符号或图案,在内存中存储的是整数,需要显示时会根据ASCII表中的对应关系显示出相应的符号或图案。
‘\0’ 0 特殊字符,字符串的结束标志。
‘0’ 48
‘A’ 65
‘a’ 97
bool
布尔 先有了C语言之后才有 bool 类型,所有C语言不可能有真正布尔类型,stdbool.h头文件对布尔类型做的模拟。
可以使用sizeof计算类型的字节数。
变量
程序运行期间数值可以变化的叫变量,相当于存储数据的盒子。
定义
类型 变量名;
int num;
取名规则:
1、由字母、数字、下划线组成
2、不能以数字开头
3、不能与关键字重名。
4、见名知义(功能、类型、范围)
注意:变量的默认值是随机,为了安全要给初始化,一般初始化为0。
使用
赋值:num = 10;
参与运算:num * 100;
输入、输出
int printf(const char *format, ...);
功能:输出数据
format:双引号包含的格式信息(提示信息+占位符)
…:变量列表
返回值:输出字符个数
int scanf(const char *format, ...);
功能:输入数据
format:双引号包含的格式信息(占位符)
…:变量地址列表
返回值:成功输入变量的个数
注意:scanf需要的是变量类型和变量地址,变量地址=&变量名
类型占位符
C语言通过占位方式来传递变量的类型。
%hhu %hu %u %lu %llu
%hhd %hd %d %ld %lld
%f %lf %LF
常量
程序运行期间数值不能变化的叫常量
常数 | 类型 |
---|---|
100 | 默认int |
100l | long |
100ll | long long |
100u | unsigned int |
100lu | unsigned long |
100llu | unsigned long long |
3.14 | 默认double类型 |
3.14f | float |
3.14l | long double |
格式化输入输出
%nd 显示n个字符宽度,不够则补空格,右对齐
%-nd 显示n个字符宽度,不够则补空格,左对齐
%0nd 显示n个字符宽度,不够则补0
%n.mf 显示n个字符宽度(小数点算一位),不够则补空格,m表示小数点后的位数(四舍五入)
%g 不显示小数点后多余的0
运算符
自变运算符
++/-- 使变量的值自动加1或减1
前自变:立即有效
后自变:下一条语句才有效
注意:不要一行代码中多次使用自变运算符;
算术运算符
±* / %
整数/整数结果没有小数点。
/ % 除数不能为零,否则会产生浮点数例外,核心转储。
关系运算符
> < >= <= == !=
比较的结果是 0 或 1 比较结果还能继续参与运算。
10 < n < 100 结果永远为真,与数学中的运算规则不同。
逻辑运算符
&& || !
会先把运算对象转换逻辑值,0转换成假,非0转换为真。
A && B 一假即假
A || B 一真即真
!A 求反
&& || 具有短路特性,当左边的值可以确定表达式结果,右边不同计算。
三目运算符
运算对象有三个部分
A?B:C; 判断A的值如果为真执行B,如果为假执行C。
该语句不能使用流程控制语句,因为它必须要有运算结果。
赋值运算符
+= *= /= …
a += b; a = a+b;
a = b; a = ab;
位运算符
& | ~ ^ >> <<
类型转换
自动类型转换
只有相同类型的数据才能进行运算,不同类型数据转换成相同类型再进行云计算。
转换规则(以不丢失数据为基础,适当牺牲一些空间):
1、字少的向字节多的转。
2、有符号向无符号转。
3、整型向浮点型转。
强制类型转换
(类型)数据 有丢失数据的风险,慎重使用。
分支语句
if(表达式)//单分支
{
表达式值为真,执行此处代码。
}
if(表达式)//双分支
{
表达式值为真,执行此处代码。
}
else
{
表达式值为假,执行此处代码。
}
if(表达式1)//多分支
{
表达式1值为真,执行此处代码。
}
else if(表达式2)
{
表达式2值为真,执行此处代码。
}
else
{
表达式1和2都为假,执行此处代码。
}
开关语句
switch(n)//运算结果必须是整型
{
case val: // val必须是整常量 ,如果val等于n,则打开执行开关
...
break; // 关闭执行开关
注意:如果每个cast后都有break,就形成了分支结构。
default:// 如果所有的case都没有匹配成功则打开执行开关
}
case a … b: 判断一个范围,此为GNU编译器独有的语法,不建议使用。
eg:
输入一个月份,判断季节。
123 春天
456 夏天
789 秋天
10 11 12 冬天
other 输入的月份有误
#include<stdio.h>
int main()
{
char month = 9;
printf("输入一个月份:");
scanf("%hhd",&month);
switch(month)
{
case 1 : printf("春天\n"); break;
case 2 : printf("春天\n");break;
case 3 : printf("春天\n");break;
case 4 : printf("夏天\n");break;
case 5 : printf("夏天\n");break;
case 6 : printf("夏天\n");break;
case 7 : printf("秋天\n");break;
case 8 : printf("秋天\n");break;
case 9 : printf("秋天\n");break;
case 10 : printf("冬天\n");break;
case 11 : printf("冬天\n");break;
case 12 : printf("冬天\n");break;
default : printf("error");
}
return 0;
}
循环语句
循环就是让一段代码反复执行,达到你想要的结果。
在介绍循环之前,我们先来了解下列两个语法。
break
跳出循环,但只能跳一层。
continue
结束本次循环,进入下次循环。
for
for一种比较灵活且危险(与其它循环相比)的一种循环。
一般使用一个变量来引导它的运行,这个变量就叫作循环变量。
for([1];[2];[3])
{
[4];
}
1、给循环变量赋初值,C99标准才可以定义循环变量。
此处定义的循环变量,只能for循环内使用。
2、判断循环变量的是否到达边界。
4、被反复执行的代码,也叫循环体。
3、改变循环变量,防止变成死循环,一般对循环变量自加或自减。
eg
输入一个正整数,判断是否是素数
#include<stdio.h>
#include<stdint.h>
int main()
{
uint32_t num = 0;
printf("请输入一个正整数:");
scanf("%d",&num);
for(int i = 2; i < num; i++)
{
if(0 == num%i)
{
printf("NO\n");
return 0;
}
}
printf("YES\n");
return 0 ;
}
while
while(条件) // 当条件为真执行循环体,为假时结束
{
// 循环体
}
while循环相当于for循环的精简版本。
for循环负责解决明确知道循环次数的问题。
while负责解决只知道结束条件而不确定循环次数的问题。
do while
do{
循环体
}while(条件);
先执行循环体,再判断循环条件,该循环至少执行一次。
循环嵌套
循环语句中包含循环语句。
外层循环执行一次,内存循环执行遍。
跳转语句
goto 可以在函数内任意跳转。
它可能会破坏已经设计好的分支或循环语句,因此绝大多数公司禁止使用。
但它在驱动编程时特别适合处理异常。
标签: // 位置
goto 标签;
eg
不使用循环计算N的阶乘
#include<stdio.h>
int main()
{
int i = 1, num = 0;
printf("请输入一个整数:");
scanf("%d",&num);
loop:
if(num)
{
i *= num;
num--;
goto loop;
}
else
{
printf("%d",i);
}
}
数组
什么是数组
变量的组合,是一种批量定义变量的方式。
一维数组
定义
类型 数组名[数量];
int arr[5];
使用
数组名[下标];
下标
从零开始,范围:0~数量-1。
遍历
与for循环配合,使用循环变量i当作数组的下标。
初始化
类型 数组名[数量] = {1,2,3,4,5,…};
1、数组与普通变量一样默认值是随机的,为了安全要对进行初始化。
2、这种初始化语法只能在定义数组时使用,而且必须使用常量初始化。
3、初始化数据过多,编译器会丢弃并产生警告。
4、初始化数据不够,编译器则会补0。
5、初始化数组时长度可以省略,编译器会自动统计数据的个数然后告诉数组。
计算数组长度:sizeof(arr)/sizeof(arr[0]) = 数组的长度
越界
为了程序的运算效率是不会检查数组的下标。
数组越界的后果:
1、一切正常。
2、段错误
3、脏数据
eg
定义一个长度为10的数组并初始化,找出数组中第二个大的值
#include<stdio.h>
int main()
{
int arr[10]={56,43,5,2,0,10,2,5,23,54};
int m[2]={arr[0] , arr[1]} , t = arr[0];
if(m[0] < m[1])
{
t=m[0];
m[0]=m[1];
m[1]=t;
}
for(int i=0; i<10; i++)
{
if(arr[i] > m[0])
{
m[1] = m[0];
m[0] = arr[i];
}
else if(arr[i] > m[1] && arr[i] < m[0])
{
m[1] = arr[i];
}
}
printf("%d\n",m[1]);
}
二维数组
定义
类型 数组名[行数][列数];
int arr[3][5];
对应的下标:
[0,0][0,1][0,2][0,3][0,4]
[1,0][1,1][1,2][1,3][1,4]
[2,0][2,1][2,2][2,3][2,4]
使用
数组名[行下标][列下标]
行下标:0 ~ 行数-1
列下标:0 ~ 列数-1
遍历
需要与双层for循环配合,外层循环负责遍历行,内层循环负责遍历列。
for(int i=0; i<3; i++)
{
for(int j=0; j<5; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
初始化
类型 数组名[行数][列数] = {{第一行},{第二行},{第三行}};
eg
定义一个5*5的数组,找出其中的最小值下标,计算周围数字的和是多少。
#include<stdio.h>
int main()
{
int sum=0;
int arr[5][5] = {
{1,2,3,4,5},
{4,5,6,7,8},
{1,8,4,2,10},
{0,2,5,4,6},
{12,2,3,6,5}
};
int min = arr[0][0] , min_i = 0,min_j = 0;
//找出最小值的下标
for(int i=0;i<5; i++)
{
for(int j=0; j<5; j++)
{
if(arr[i][j] < min)
{
min = arr[i][j];
min_i = i;
min_j = j;
}
}
}
//利用循环将最小值周边的值相加
for(int i=min_i-1; i <= min_i+1; i++)
{
for(int j=min_j-1; j <= min_j+1; j++)
{
if(i<0 || j<0 || i>4 || j>4)
{
continue;
}
else
{
sum += arr[i][j];
}
}
}
sum -= arr[min_i][min_j];
printf("%d",sum);
}
变长数组
定义数组时使用变量当作它长度,在代码编译期间数组的度是不确定的,当执行到数组的定义语句时它的长度才家有确定下来,一旦确定就无法更改了。
int a [n][n];
优点:可以根据实际情况来确定数组长度达到节约内存目的。
缺点:不可以初始化
位运算符
A & B 按位相与
01011010 0x5A
11000011 0xC3
--------------------
01000010 0x42
A | B 按位或
01011010 0x5A
11000011 0xC3
--------------------
11011011 0xDB
~A 按位求反
01011010 0x5A
10100101 0xA5
A ^ B
01011010 0x5A
11000011 0xC3
--------------------
10011001 0x99
A << n 把A补码左移n位,左边的丢弃,右边补0
01011010 << 4
10100000
A >> n 把A补码右移n位,右边的丢弃,左边补符号位
11000011 >> 4
11111100