基础定义
比特(bit)和字节(Byte)
1.bit(比特)位
全称:bit → 中文叫 比特
含义:最小的数据单位
只有两个值:0 或 1
简写:b(小写)
- Byte(字节)
全称:Byte → 中文叫 字节
关系:1 Byte = 8 bit
简写:B(大写)
1 字节 = 8 比特
1 Byte(B) = 8 b(bit)
进制转换
其他进制 => 十进制
按位展开 × 基数ⁿ 相加
二进制转十进制:×2ⁿ
八进制转十进制:×8ⁿ
十六进制转十进制:×16ⁿ
例:
二进制 101 →1×2² + 0×2¹ + 1×2⁰ = 5
8421转换法(十进制 => 二进制)
四位二进制对应:
8 4 2 1
想转二进制,就把数字拆成 8+4+2+1
例 1:十进制 10
10 = 8 + 2
对应:
8 4 2 1
1 0 1 0
所以:10 → 1010
例 2:十进制 5
5 = 4 + 1
8 4 2 1
0 1 0 1
所以:5 → 0101
十进制转换其他进制
除基取余,倒着读
转二进制:÷2
转八进制:÷8
转十六进制:÷16
步骤:
1.不断除以目标进制
2.记录余数
3.最后从下往上读就是结果
十进制 10 → 二进制 1010
数据类型
1.可以使用 sizeof(数据类型)来查看当前数据类型在当前系统占用大小,单位:字节
printf("%zu",sizeof(int)); //4
2.只有整数有无符号类型,浮点型没有
整形
简化写法
(windows)
short 2字节 printf("%d",a);
int 4字节 printf("%d",a);
long 4字节 printf("%ld",a);
long long 8字节 printf("%lld",a);
(Linux):
long 32位4字节
64位8字节
完整写法
(windows)
short int 2字节 printf("%d",a);
signed int 4字节 printf("%d",a);
long int 4字节 printf("%ld",a);
long long int 8字节 printf("%lld",a);
(Linux):
long 32位4字节
64位8字节
无符号整形(不常用)
不表示正负,只表示非负数
unsigned int printf("%u",a);
unsigned long int printf("%lu",a);
unsigned long long int printf("%llu",a);
浮点型
(小数默认是 double 类型 )
单精度浮点型 float 4字节
双精度浮点型 double 8字节
长双精度 long double printf("%zu",sizeof(long double));
float a = 3.14F;
printf("%.2f",a);
double b = 1.78;
printf("%.2lf",b);
long double c = 3.14158L;
printf("%Lf",c);
字符型
char 1字节
char c1 = 'a';
printf("%c",c1);
char c2 = '1';
printf("%c",c2);
char c3 = '.';
printf("%c",c3);
命名要求
1.由数字,字母 下划线_ 组成
2.不可以数字开头
3.标识符不能是关键字
自增自减运算符
放前面:先变再用
放后面:先用再变
a++ 先用再自增
++a 先自增再用
a-- 先用再自减
--a 先自减再用
① 前置自增 ++a(先加,再用)
int a = 5;
int b = ++a;
等价于:
a = a + 1;
b = a;
结果:a = 6,b = 6
② 后置自增 a++(先用,再加)
int a = 5;
int b = a++;
等价于:
b = a;
a = a + 1;
结果:a = 6,b = 5
隐式转换和强制转换
隐式转换:
1.不同数据类型进行计算,赋值会触发隐式转换
2.char short 类型运算都会先提升为 int 再进行计算
3.取值小的 转换 取值大的
short b1 = 10;
short b2 = 10;
数据类型 res = b1+b2; //res是 int 类型
int i = 10;
long n = 100L;
double d = 20.0;
数据类型 res = i + n + d; //res是 double 类型
short b = 10;
long s = 20L;
long long n = 100L;
数据类型 res = b + s + n; //res是 long long类型
[数据类型 从大到小排]
double
float
long long
long
int
short
char
强制转换
取值范围大 赋值 给取值范围小 时使用(精度损失)
int b = 10;
short i = (short)b;
字符相加
字符对照 => ASCII码
'a' => 97
'A' => 65
a-z 97-122
A-Z 65-90
char c = 'a';
int i = c + 1;
printf("%c",i);//b
printf("%d",i);//98
常用头文件
stdio.h标准输出输出头文件
#include<stdio.h>
prinft()//标准输出语句
printf("直接字符串输出");
float a =1.2789;
printf("占位符输出 保留两位小数%.2f",a); //1.28
scanf()//标准输入语句
int a,b;
printf("请输入两个数\你");
scanf(" %d %d",&a,&b);
printf("输入的数 a=%d\n输入的数 b=%d",a,b);
/*
输入的数 a=1
输入的数 b=2
*/
putchar(c)//输出单独字符
getchar() //获取单个字符
char a,b;
prinft("请输入两个个字符\你");
a = getchar();
b = getchar();
getchar();
putchar(a);
putchar(b);
math.h C语言数学库
#include<math.h>
常用函数:
sqrt(x) //开平方
double a = sqrt(16);
printf("%.2lf",a); //4.00
pow(2,3) //次方根 二的三次方
double b = pow(2,3);
printf("%.2lf",b);//2.00
round(3.145) //四舍五入
double c = round(3.145);
printf("%.2lf",c);//3.00
double c1 = round(3.545);
printf("%.2lf",c);//4.00
abs(-1); //整数绝对值
int a = abs(-1);
printf("%d",a); // 1
fabs(-1.258); //浮点数绝对值
double d = fabs(-1.258);
printf("%.3lf",d); //1.258
floor(1.258); //向下取整
double e = floor(1.258);
printf("%.3lf",e); //1.000
ceil(1.258); //向上取整
double f = ceil(1.258);
printf("%.3f",f);//2.000
常量PI M_PI
printf("%lf",M_PI);
stdlib.h C语言标准库
#include<stdlib.h>
malloc():动态分配内存
int* arr = (int*)malloc(4 * sizeof(int)); //分配四个 int 单位的内存
free(arr);
calloc():分配已清零的内存
realloc():调整已分配内存大小
free():释放动态内存(防止内存泄露)
string.h 字符
#include <string.h>常用函数: 详见下方 ### 常用的字符串函数
strlen
strcpy
strcmp
strcat
strchr
strstr
选择控制结构:
if 单分支语句
int age = 0;
scanf(" %d",&age);
if(age <18)
{
printf("未成年");
}
if-else if双分支语句
int age = 0;
scanf(" %d",&age);
if(age<18)
{
printf("未成年");
}
else
{
printf("成年人");
}
if-else-if-else if多分支语句
int age = 0;
scanf(" %d",&age);
if(age>=70)
{
printf("老年人");
}else if(age>=40)
{
printf("中年人");
}else if(age>=18)
{
printf("年轻人");
}else{
printf("未成年");
}
switch多分支语句
数字控制案例
int selector = 1;
while(selector != 0){
printf("欢迎使用本系统,请输入操作指令续序号:\n 1:添加用户\n 2:删除用户 \n 0:退出系统\n");
scanf(" %d",&selector);
switch(selector){
case 1:
printf("已执行添加用户\n");
break;
case 2:
printf("已执行删除用户\n");
break;
case 0:
printf("感谢使用!下次见\n");
break;
default:
printf("操作指令错误,请重新输入\n");
}
}
字符控制案例
char selector = 'a';
while(selector != 'q'){
printf("欢迎使用本系统,请输入操作指令续序号:\n a:添加用户\n b:删除用户 \n q:退出系统\n");
selector = getchar();
while(getchar() != '\n');
switch(selector){
case 'a':
printf("已执行 添加用户!\n");
break;
case 'b':
printf("已执行 删除用户\n");
break;
case 'q':
printf("感谢使用,下次见!\n");
break;
default:
printf("指令输入有误,请重新输入\n");
}
}
循环控制语句
for循环 满足条件才循环
for(int i = 1;i<=5;i++){
printf("第%d次",i);
}
continue
结束本次循环(跳过 continue 后面的代码进入下一轮循环【如果有】)
for(int i =0;i<=5;i++){
if(i == 3){
continue;
}
printf("%d",i);//0,1,2,4,5
}
break
终止循环
for(int i =0;i<=5;i++){
if(i == 3){
break;
}
printf("%d\n",i); //0,1,2}
while循环 满足条件才循环
int counter = 1;
while(counter<=5){
printf("第%d次",counter); //1,2,3,4,5
counter++;
}
do-while循环 不关条件是否满足,先执行一次循环 - 满足条件才循环
int counter = 1;
do{
printf("第 %d 次",counter); //1,2,3,4,5
counter++;
}while(counter<=5);
函数
函数的定义
无返回值函数
void sayHello(){
printf("hello\n");
}
带返回值函数
int add(int a,int b){
int sum = a+b;
return sum;
}
递归函数
栈的概念
入栈push -> 出栈pop
先入后出
例子:累加和
求 1-num 的累加和
int sum(int num){
if(num == 1){
return 1; //程序出口 退出条件
}
return num + sum(num-1); //自己调用自己
}
指针:
指向内存地址
间接寻址运算符 *
根据地址找到里面存的值
取地址运算符 &
拿到某变量在内存的地址
int a = 20;
int *p = &a; //此处*表示声明指针
//以下都是一样的意思 (都是修改 a 的值为 10)
a = 10;
printf("%d\n",a); // 10
*p = 10;
printf("%d\n",a); // 10
交换变量例子
全局变量:放置在所有函数的外侧最上方 程序运行结束才销毁
局部变量:放置在单独函数内部包括 main 函数 随之函数运行完成结束而销毁,仅在函数内部可使用
注意 :函数要先声明方可使用,程序是顺序执行 必须先在程序最顶部定义,程序运行时根据顺序执行先读入自定义函数到内存。才可以被主函数查找并调用!
#include <stdio.h>
// 1. 函数声明(写在最顶部!) 或者直接在这里定义好相关的功能
void swap(int *a, int *b);
int main() {
swap(&a, &b); // 2. main 调用,能找到!
return 0;
}
// 3. 函数定义(可以放在 main 后面!)
void swap(int *a, int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
交换变量函数:
放到主函数的外面的上面
void swap(int *a,int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
主函数内:
int a =0;
int b =0;
scanf(" %d %d",&a,&b);
printf("未交换前的 a=%d;b=%d\n",a,b);
swap(&a,&b);
printf("交换后的 a=%d,b=%d\n",a,b);
指针数组
int a=10,b=20,c=30;
//定义指针数组 存储数的指针地址
int *arr[] = {&a,&b,&c};
//使用间接寻址操作符* 取对应数组索引下指针地址的值
printf("%d\n%d\n%d",*arr[0],*arr[1],*arr[2]); //10 20 30
函数指针
//普通函数
int add(int a,int b){
return a+b;
}
//函数指针
int (*add_ptr)(int,int) = add;
//以下写法代表的是一样的执行
printf("%d\n",add(1,2));
printf("%d\n",add_ptr(1,2));函数指针的应用
回调函数
//mian(外部)函数上面
double calc(double a,double b,double (*func)(double,double)) //double(*func)(double,double)代表一个名为 func 的函数指针
{
return func(a,b);//返回 func 函数的处理结果
}
double div1(double a,double b){
return a/b;
}
double add(double a,double b){
return a+b;
}
//main 函数内
double a = 0.0;
double b = 0.0;
double res = 0.0;
char operation = ' '; //注意字符不能为空 此处使用空格初始化
printf("请输入两个数:\n");
scanf(" %lf %lf",&a,&b);
printf("请输入进行的运算:\n + \n /\n");
scanf(" %c",&operation);
if(operation == '+'){
res = calc(a,b,add); //使用+运算函数处理
printf("%lf",res);
}else if(operation == '/'){
res = calc(a,b,div1); //使用/运算函数处理
printf("%lf",res);
}else{
printf("不支持的操作");
}
动态内存 malloc + 字符指针
固定数组大小
char str[20];动态分配
char *str_ptr = (char*)malloc(20 * sizeof(char));// 开辟 20 个字符长度的 str 指针
char *str1_ptr = (char*)malloc(20);
判断是否申请成功
if(str_ptr == NULL){
return -1;
}
使用:
strcpy(str_ptr,"hello 小胡");
printf("%s",str_ptr);
数组
一维数组
数组的定义
int arr[]={1,2,3,4,5}
数组访问
数组索引 0(第一个元素)设置为 100
arr[0] = 100;
数组指针 指向数组第一个元素(索引 0)的地址
int *arr_ptr = arr;
向函数传递一维数组
void func1(int arr[],int len){};
指针写法
void func2(int *arr,int len){};
带数组大小
void func3(int arr[5]){};
基于数组的常用算法和应用
1.遍历数组
int arr[] = {1,2,3,4,5};
int len =5;
for(int i =0;i<len;i++){
printf("%d",arr[i]);
}
2.求数组最大值
int arr[] = {1,2,3,4,5};
int max = arr[0];//设置第一位为默认最大值
for(int i =0;i<5;i++){
//如果当前索引下的数大于默认值 就把他设置为最大值
if(arr[i]>max){
max = arr[i];
}
}
3.求数组最小值
int arr[] = {1,2,3,4,5};
int min = arr[0];//设置第一位为默认最小值
for(int i=0;i<5;i++){
//如果当前索引下的数小鱼默认值 就把他设置为最小值
if(arr[i]<min){
min = arr[i];
}
}4. 数组求和
int arr[] = {1,2,3,4,5};
int sum = 0;
for(int i =0;i<5;i++){
sum+=arr[i];
}
5.数组反序
int arr[] = {1,2,3,4,5};
int len = 5;
//仅需要循环一半
for(int i =0;i<len/2;i++){
//交换变量 最左边的交换到最右边
int temp = arr[i];
//最右边即 数组长度-1(因为下标从0开始)-i(减去当前下标 可算出跟右侧下标为几的元素交换)
//即 len-1-i 就是右边要交换的元素索引
arr[i] = arr[len-1-i];
arr[len-1-i] = temp;
}
6.查找元素
int arr[] = {1,2,3,4,5};
int len = sizeof(arr)/siziof(int);
int target = 4;
for(int i = 0;i<len;i++){
if(arr[i] == target){
printf("找到啦 你要找的数字%d 的target,位于索引%d",target,i);
}
}
7.冒泡排序
升序版:
int arr[] = {2,1,4,9,3};
int len = sizeof(arr)/sizeof(int);
升序:从小到大排 (升:越来越大)
降序:从大到小排 (降:越来越小)
//无需进行最后一次比较(最后一位会自己排好) 故len-1
for(int i = 0 ;i<len-1;i++)
{
//每次都将本索引位置的元素 跟其他几位索引的元素进行比较
//减去当前索引 i 的位置可以算出需要剩余比较的次数(每一轮少比 i 次)
for(int j = 0;j<len-i-1;j++){
//两两比较 大的后移
if(arr[j]>arr[j+1]){
//交换 arr[j]和 arr[j+1]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
//输出验证
for(int i = 0;i<len;i++){
printf("%d \t",arr[i]);
}
降序版:
int arr[] = {2,1,6,7,9,8};
int len = sizeof(arr)/sizeof(int);
for(int i = 0;i<len-1;i++){
for(int j = 0;j<len - i -1;j++){
//两两比较小的放后面
// 比较条件 j+1>j j+1 大 j小 j+1 和 j 互换位置
//升序降序仅和 if 的条件判断有关!
if(arr[j+1]>arr[j]){
int temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
//输出验证
for(int i = 0;i<len;i++){
printf("%d \t",arr[i]);
}
二维数组
二维数组的定义
二维数组是多行多列
arr[ 行 ] [ 列 ]
// 3行 4 列 的二维数组
int arr3={
{1,2,3,4},
{2,3,4,5},
{3,4,5,6},
};
[0][1][2][3]
[索引 0]第一行: 1, 2, 3, 4
[索引 1]第二行: 2, 3, 4, 5
[索引 2]第三行: 3, 4, 5, 6
二维数组的访问
访问第一行第二列的元素
int res = arr1;
注意:数组从 0 开始!3索引只有
行0,1,2
列0,1,2,3
printf("%d",res);//4
向函数传递二维数组
int func(int arr[][4]){}
必须写列数,因为二维数组在内存中连续存放 必须知道一行有多少个才能找到下一行
int arr3 = {{1,2,3,4},{2,3,4,5,},{3,4,5,6}};
即:1,2,3,4,2,3,4,5,3,4,5,6
知道列则可以切分出行
[第一行] [第二行] [第三行]
即:1,2,3,4 | 2,3,4,5 | 3,4,5,6
主函数外(上面)
//打印二维数组函数 必传列 且不可为变量
void printArray(int arr[][4],int row){
//外层循环遍历每一行
for(int i =0;i<row;i++){
//内层循环遍历每一列 即每一行的每一项元素
for(int j=0;j<4;j++){
printf("%d\t",arr[i][j]);
}
printf("\n");//打印完每一行后换行
}
}
主函数内
int arr[3][4] = {{1,2,3,4},{2,3,4,5,},{3,4,5,6}};
int row = 3;
printArray(arr,row);
c语言字符串
字符串定义与使用
char str[] = "hello";
char str1[] = "你好";
\0 是表示字符数组终止(结束符)
str: h e l l o \0
str1: 你 好 \0
printf("%d",sizeof(str)); //6 一个英文字符占1
printf("%d",sizeof(str1)); //7 一个中文字符占3
常用的字符串函数
strcpy:字符串复制
strlen:字符串长度
char str2 = "你好";
printf("%d",strlen(str2));//6
strcmp:字符串比较
char str3[] = "你好";
char str4[] = "你好";
printf("%d",strcmp(str3,str4)); //0 表示字符串长度相等
char str5[] = "你";
char str6[] = "你好";
printf("%d",strcmp(str5,str6)); //-229 小于0 表示串 2 大
printf("%d",strcmp(str6,str5)); //229 大于0 表示串 1 大
strcat:字符串拼接
char str6[20] = "hello ";
strcat(str6,"world");
printf("%s",str6);
strchr:字符串查找
char str7[] = "hello";
char *str_ptr = strchr(str7,'h');
if(str_ptr != NULL){
printf("找到啦");
}else{
printf("没找到");
}
strstr:查找子串
char str7[] = "hello world";
char *str7_ptr = strstr(str7,"world");
if(str7_ptr){
printf("找到啦");
}else{
printf("未找到");
}
sizeof 和 strlen的区别
sizeof 是字符数组大小
strlen 是字符串真实长度
例子:
char str[] = "你好";
printf("%d",strlen(str));//6
printf("%d",sizeof(str));//7 多了终止符\0
结构体
结构体基本结构定义
struct Student{
char name[20];
int age;
float score;
};
创建结构体实例
struct Student stu; //创建名为 stu 的 Student 结构体
结构体赋值
1.逐个赋值:
strcpy(stu.name,"张三");
stu.age = 18;
stu.score = 99.5;
2.一次赋值
struct Student stu1 = {"Ethan",20,99.9};
访问结构体成员
printf("姓名:%s\n",stu.name);
printf("年龄:%d\n",stu.age);
printf("成绩:%f\n",stu.score);
printf("姓名:%s\n",stu1.name);
printf("年龄%d\n",stu1.age);
printf("成绩:%f\n",stu1.score);
指向结构体的指针
struct Student stu2; //创建 stu2 Student结构体实例
struct Student *stu_ptr = &stu2; //创建 stu_ptr 结构体指针,并赋 stu2 结构体实例 地址给 stu_ptr
strcpy(stu_ptr->name,"小胡");
stu_ptr->age = 21; //通过结构体指针设置 age
stu_ptr->score = 99.8;
printf("%s \t %d \t %f",stu2.name,stu2.age,stu2.score);
结构体指针的应用
1.通过函数修改结构体实例
普通函数变量传参 函数里面变了实际结构体实例为改变(拷贝)
void changeStu(struct Student *stu_ptr,char name[],int age,float score){
strcpy(stu_ptr->name,name);
stu_ptr->age = age;//修改的是实例的参数
stu_ptr->score = score;
}
int main() {
struct Student stu; //创建stu结构体实例
struct Student *stu_ptr = &stu;
//调用 changeStu函数修改结构体实例
changeStu(stu_ptr,"小胡",19,99.6);
printf("%s\t %d\t %f",stu_ptr->name,stu_ptr->age,stu_ptr->score);
}
2.节省内存
复杂结构体传参
太大了 使用结构体指针仅需传一个地址
// 调用
changeStu(&stu); // 传地址
否则
需要传入整个结构体
// 调用
changeStu(stu);
3.数据结构 链表,树
struct Node{
int data;
struct Node *next;
};
4.动态内存创建结构体
//创建 Student 结构体指针 stu_ptr 并将分配好的内存地址给到结构指针
struct Student *stu_ptr = (struct Student *)malloc(sizeof(struct Student));
if(stu_ptr == NULL){
printf("内存分配失败!");
return -1;
}
strcpy(stu_ptr->name,"小胡");
stu_ptr->age = 20;
stu_ptr->score = 90.0;
printf("%s\t %d \t %.2f",stu_ptr->name,stu_ptr->age,stu_ptr->score);
free(stu_ptr);//使用完成后释放内存
stu_ptr = NULL; //指针回空
结构体数组
结构体的定义
struct Student{
char name[20];
int age;
float score;
};
结构体直接赋值
struct Student stu[3]={
{"小胡",18,98.5},
{"小明",20,99.2},
{"小李",21,92.1}
};
结构体单独赋值
strcpy(stu[0].name,"Ethan");
stu[0].age = 22;
stu[0].score = 100;
遍历结构体数组
for(int i =0; i<3;i++){
printf("姓名:%s 年龄:%d 成绩:%f\t\t",stu[i].name,stu[i].age,stu[i].score);
}
单链表
动态分配单链表
单链表的定义
typedef struct Node{
int data; //数据域:存储数据
struct Node *next; //指针域:指向下一个节点
}Node, *LinkList; //LinkList是struct Node* 类型的(链表-头指针)
初始化空链表
LinkList InitList(){
//动态分配头节点内存(头节点不存数据 方便操作)
Node *head = (Node*)malloc(sizeof(Node));
if(head == NULL){
printf("内存分配失败!");
return NULL;
}
head->next = NULL;//空链表:头节点置空
return head;
}
单链表的插入
头插法
void InsertHead(LinkList head,int val){
Node *newNode = malloc(sizeof(Node));
newNode->data = val;
//新节点指向第一个节点
newNode->next = head->next;
//头节点指向新节点
head->next = newNode;
printf("头插成功!%d",val);
}
尾插法
void InsertTail(LinkList head,int val){
Node *newNode = malloc(sizeof(Node));
newNode->data = val;
newNode->next = NULL;
//找到链表最后一个节点p
Node *p = head;
while(p->next != NULL){
p = p->next;
}
//新节点挂在最后一个节点的后面
p->next = newNode;
printf("尾插成功!%d",val);
}
单链表的遍历
void TraverseList(LinkList head){
if(head->next == NULL){
printf("链表为空!");
return;
}
Node *p = head->next;
printf("链表元素:\n");
while(p != NULL){
printf("%d\t",p->data);
p = p->next;
}
printf("\n");}
单链表的删除
int DelNode(LinkList head,int val){
//p:待删除节点的前驱节点
Node *p = head;
//q:指向待删除的节点
Node *q;
//找到待删除节点的前驱
while(p->next != NULL && p->next->data != val){
p = p->next;
}
//判断是否找到了指定的节点
if(p->next == NULL){
printf("未找到%d节点,删除失败",val);
return 0;
}
q = p->next;
p->next = q->next;
free(q);
printf("节点%d删除成功!",val);
return 1;
}
单链表的查找
Node* SearchList(LinkList head,int val){
Node *p = head->next;
while(p != NULL && p->data != val){
p = p-next;
}
return p;}
常见查找算法
顺序查找
二分查找
常用排序算法(数组)
一维数组插入 删除 查找
冒泡排序
选择排序
例题
99 乘法表
斐波那契数列
阶乘
最大最小值
1.键盘录入一个三位数,将其拆分个十百位
个位: 数值%10
十位: 数值/10%10
百位: 数值/100%10
千位: 数值/1000%10