#include <stdio.h>
#include <math.h>
#include<malloc.h>
/*机器语言 01010100
汇编语言 ADD AX,BX 高级编程语言 a+b
面向对象语言 java C++ */
/*c优点:代码量小 速度快 功能强大
缺点:危险性高 开发周期比较长 可移植性不强*/
/*c语言的应用领域:
系统软件开发,驱动程序,数据库
应用软件开发:wps,ps,
嵌入式软件开发:智能手机,掌上电脑
游戏开发:cs*/
/*有史以来最重要语言,一名合格黑客必须要掌握的语言
C语言最重要的是指针,学好C语言的指针就能很好理解java的引用*/
/*
1.一个字节占8位
一个汉字占2个字节
2.基本数据类型:
整形:int 4个字节
短整型:short int 2个字节
长整形:long int 8个字节
浮点型:
单精度浮点数 float 4个字节
双精度浮点数 double 8个字节
字符:char 1个字节
3.复合基本类型
结构体
枚举
*/
/*
4.变量:本质就是内存中一段存储空间
int i;
操作系统会在内存条中找到一个空闲的存储单元与i相关联
i=3; 把3放到存储单元里面
3放在内存中,程序终止之后所占的空间被释放
5.变量为什么要初始化?初始化就是赋值的意思
内存条是硬件设备,高电平是1,低电平是0。里面存的是1/0
如果变量没有被初始化,会被默认添加一个数值
当软件运行完毕,操作系统会回收该内存空间,操作系统不清空该内容空间中遗留下的数据
6.如何定义变量
数据类型 变量名=变量值
等价于 数据类型 变量名;变量名=变量值;
int i=2; int i; i=2;
int i,j;int i=3,j=5;
7.什么是进制
十进制:逢10进1
0 1 2 3 4 5 6 7 8 9
二进制:逢2进1
8.
%d表示以10进制输出
%x或者%X表示以16进制输出
%o表示以8进制输出
9.十六进制:前面加0x
8进制:前面加0
科学计数法:1.235*e3=1235
123.12e-2=1.2312
10.什么是字节
字节就是存储数据的单位,并且是硬件访问的最小单位
1K=1024字节
1M=1024K
1G=1024M
11
ASCII码:不是一个值,规定了不同的字符用哪个整数值表示
A 65 a 97
B 66 b 98
C 67 c 99
12
printf("输出控制符",输出参数);
printf("%d%d",i,j) ;
13
scanf:通过键盘将数据输入到变量中
将从键盘输入的数据转化为按照输入控制符所规定格式的数据 ,然后存到以输入参数
的值为地址的变量中。
scanf("输入控制符",输入参数);
scanf("%d",&i);
scanf中尽量不要输入非控制输入符例如\n
14
四则运算
16/5==3被除数除数都是整数商也是整数
16/5.0==3.20000 除数是小数商也是小数
逻辑运算符
&&左边的表达式为假右边的表达式就不执行
||左边是真右边不执行
if默认智能控制一个语句的执行,如果要控制多个语句的执行,必须要括起来
素数 1 5 7 11 13
15 循环:某些代码会被重复执行
for while do...while
16 强制类型转换
int i=100;
float sum=(float)i;
17 浮点数存储带来的问题
float 和 Double都不能保证精确的存储小数
举例:有一个浮点型的变量x,怎样判断x的值是0
|x-0.0000001|<=0.0000001
18 多层for之间的嵌套使用
19 什么叫n进制
把2进制转化成10进制
把10进制转化为2进制
不同进制所代表的数值之间的关系
10进制转2进制 把余数倒过来
20
++i 先+1,再参与运算
i++ 先参与运算,再加一
21 break continue
break不能用于if
break终止最近的循环
22
一维数组:
为N个变量连续存储空间
所有变量的数据类型相同
所有变量所占的字节大小相等
有关一维数组的操作:
初始化
完全初始化
int a[5]={1,11,12,2,3} ;
不完全初始化,没有初始化的值为0
int a[5]={1,12,3};
不初始化,所有值为垃圾直
int a[5];
错误写法:
int int a[5];
a[5]={1,12,3};
把a数组中的值全部复制给b数组
错误的写法:b=a;
正确的写法 ;
for(int i=0;i<5;i++){
b[i]=a[i];
}
scanf("%d",&a[0]);
排序
求最大最小值
倒置
插入
删除
二维数组
int a[3][4]可以当做3行4列看待
23
函数
为什么需要函数
如何定义函数
函数的分类
注意的问题
24 指针
内存是8个0或者8个1 一个编号
*/
//int * p; p是变量的名字 ,(int *)表示p变量存放的是int类型变量的地址
//int * p,不表示定义了一个名字自叫做*p的变量 。而是p是变量名,p变量的数据类型是int *类型、
//所谓int *类型,就是存放int变量地址的类型
//int i=3; p=i;error p只能存放int类型变量的地址
/* p=&i;
p保存了i的地址,因此p指向i.
p不是i,i也不是p:修改p的值不会影响i的值,修改i的值不会影响p的值
如果一个指针变量指向了一个普通变量,则 *指针变量就完全等同于 普通变量
例子:
如果p是一个指针变量。并且p存放普通变量的地i的地址
*p 就完全等同于 i
*p以p的内容为地址的变量
或者说:在所有出现*p的地方都可以替换成i
在所有出现i的地方都可以替换成*p
1.指针就是地址,地址就是指针
2.地址就是内存单元的编号
3.指针变量是存放地址的变量,指针变量存放指针,指针只是一个值
4.指针变量和指针是两个不同的概念
5.但是要注意:通常我们叙述时会把指针变量简称为指针,实际他们含义不一样
指针:
表示一些复杂的数据结构
快速的传递数据
它使函数返回一个以上的值
能直接访问硬件
能够方便的处理字符串
它也是理解面向对象语言中应用的基础
总结:它是c语言的灵魂
指针的定义
地址:内存单元的编号 ,是一个从零开始的非负整数。
指针:
指针就是地址,地址就是指针
指针变量是存放地址的变量,指针变量存放指针,指针只是一个值
指针变量和指针是两个不同的概念
指针的本质是操作受限的非负整数
但是要注意:通常我们叙述时会把指针变量简称为指针,实际他们含义不一样
指针的分类
1.基本类型指针
2.指针和数组
3.指针和函数
4.指针和结构体
5.多级指针
CPU直接处理内存条数据
CPU-内存:控制线、数据线、地址线
指针常见错误:
1 int i=5;
int *p;
*p=i;
2 int i=5;
int *p;
int *q;
*q=p;//不能把int *类型转换为int 类型
3 int i=5;
int *p;
int *q;
p=&i;
p=q; q是垃圾值,q赋给p,p也是垃圾直
printf("%d",*q);
q空间属于本程序,本程序可以读q的内容
但是q内部是垃圾直,本程序不能读*q的内容
经典指针程序: 互换数字
1
void huhuan(int a,int b){
int t;
t=a;
a=b;
b=t;
}
main(){
int a=3;
int b=5;
huhuan(a,b);//只是改变形参的值,不会改变实参的值
}
2
void huhuan(int *p,int *q){
p指向a的地址,*p就是a
q指向b的地址,*q就是b
int t;
t=*p;
*p=*q;
*q=t;
}
int main(void){
int a=3;
int b=5;
huhuan(&a,&b);//只是改变形参的值,不会改变实参的值
printf("%d %d",a,b);
return 0;
}
附注:
*的含义
1:乘法
2:定义指针变量
指针使函数返回一个以上的值:
void huhuan(int *p,int *q){
*p=1;
*q=2;
}
int main(void){
int a=3;
int b=5;
huhuan(&a,&b);//只是改变形参的值,不会改变实参的值
printf("%d %d",a,b);
return 0;
}
如何通过被调函数修改主调函普通变量的值
1:实参必须为普通变量的地址
2:形参必须为指针变量
3:在被调函数中通过*形参名=。。。就可以修改主调函数相关变量的值
指针和数组:
指针和一维数组的关系
数组名:第一个元素的地址
int a[5] a是数组名,5是数组元素个数,元素就是变量 a[0] a[1]
一位数组名是个指针(常量),他存放的是一维数组第一个元素的地址
int a[5];
int b[5];
a=b;//error a是常量
一位数组第一个元素的地址:&a[0]
下标和指针的关系
如果p是个指针变量,则p[i]永远等价于*(p+i)
如果一个 函数要处理一个一维数组,则需要接受该数组的哪些信息?
数组名
数组的长度
例1:
void f(int *prr,int len){
for(int i=0;i<len;i++){
printf("%d",*(prr+i));
}
}
int main(void){
int a[5]={1,2,3,4,5};
f(a,5);
}
例2:
void f(int *prr,int len){
prr[3]=88;//prr[3]等价于*(prr+3) 等价于a[3]
}
int main(void){
int a[5]={1,2,3,4,5};
f(a,6);
a[3]=6;
}
指针变量的运算:
不能相加、不能相乘、也不能相除
如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量才可以相减
一个指针变量到底占几个字节:
sizeof :该数据类型占的字节数 sizeof(int)=4
int i=5;
double j=4.4;
int * p=&i;
double * q=&j;
sizeof(p)=sizeof(q)=4
动态内存分配:
传统数组的特点
1:数组长度必须事先制定,必须是常整数,不能是变量
int a[10]; ok
2:传统形式定义的数组,该数组的内存程序员无法手动释放
数组一旦定义,系统为该数组分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放
3:数组的长度不能在函数运行的过程中动态地扩充或者缩小
4:A函数定义的数组,在A函数运行期间可以被其他函数利用 。但是在A函数运行完毕之后,不能被其他函数使用
为什么要需要动态分配内存?
动态数组很好的解决了传统数组的4个缺陷。
malloc 是memory内存 allocate分配
1.要使用malloc这个函数 ,必须添加malloc.h这个头文件
2.malloc只有 一个形参,并且形参是整形
3.malloc(4)表示请求系统为本程序分配4个字节
4. malloc函数只能返回第一个字节的地址
5.p本身所占的内存是静态分配的,p所指向的内存是动态分配的
int i=5;//分配了4个字节,静态分配
int *p=(int *)malloc(4) ;
free(p);//表示把p所指向的内存给释放掉,p本身的内存是静态的
*p=5;//*p代表的就是一个int变量
动态内存分配举例——动态数组的构造
int main(){
int a[5];//如果int占4个字节的话,则本数组总共占20个字节。每四个字节被当做了一个int变量来使用
int len;
int * pArr;
printf("请输入你要存放的元素的个数:\n") ;
scanf("%d",&len);
pArr=(int *)malloc(len*sizeof(int)) ;
//pArr存放第一个字节地址,但是因为pArr是int *类型的。
//所以他指向了前4个字节。parr+1指向后4个字节地址,pArr在这里就相当于数组名
//对以为数组进行赋值
for(int i=0;i<len;i++){
scanf("%d",&pArr[i]);
}
//输出
for(int i=0;i<len;i++){
printf("%d ",pArr[i]);
}
free(pArr);//释放掉动态分配的数组
return 0;
}
静态内存和动态内存的比较
静态内存由系统自动分配,由系统自动释放
静态内存是在栈分配的
动态内存是由程序员手动分配,手动分配。
动态内存是在堆分配的
跨函数使用内存问题
例: void f(int ** q){
int i=5;
//*q等价于p
//*q=i; error 因为*q=i等价于p=i,这样写是错误的
*q=&i;
}
int main(){
int *p;
f(&p);//f函数已经终止了,f函数里面的静态变量 i和q不能跨函数使用
printf("%d",*p);//本句语法没有问题,但是逻辑上有问题
}
动态内存可以跨函数使用内存
例: void f(int ** q){
//*q等价于p
//*q=i; error 因为*q=i等价于p=i,这样写是错误的
*q=(int *)malloc(sizeof(int));//等价于p=(int *)malloc(sizeof(int));
**q=5; //*p=5等价于i=5
}
int main(){
int *p;
f(&p);/
printf("%d",*p);
}
多级指针(保存指针变量地址的指针变量)
int i=10;
int * p=&i;
int ** q=&p;
int *** r=&q;
//r=&p; error 因为r是int ***类型,所以r只能放int **类型变量的地址
----------------------------------------------------------------------------------------
结构体:
struct Student{
int age;
float score;
char sex;
};
int main(){
struct Student st={80,66.6,'f'};
}
为什么需要结构体?
为了表示一些复杂的事物,普通的基本类型无法实现
什么叫结构体?
把一些基本数据类型组合在一起形成的一个新的数据类型
如何定义一个结构体?
推荐:这只是定义了一个新的数据类型,没有定义变量
struct Student{
int age;
float score;
char sex;
};
怎么使用结构体变量?
赋值和初始化
struct Student st={80,66.6,'f'}; //初始化 定义的同时赋值
struct Student st2;
st2.age=10;
st2.score=88;
st2.sex='f';
如何取出结构体变量中的每一个成员
结构体变量名.成员名
指针变量名->成员名 //在计算机内部会被转化成(*指针变量名).成员名 的方式来指向
struct Student st={80,66.6,'f'};
struct Student *pst=&st;
printf("%d",pst->age) ;
pst->age在计算机内部会被转化成(*pst).age 也等价于st.age
我们之所以知道pst->age等价于st.age,是因为pst->age被转化成了(*pst).age 执行
pst->age的含义:
pst所指向的结构体变量中age的这个成员
结构体变量的运算
结构体变量不能进行加减乘除操作,但是可以相互赋值
结构体变量和结构体指针作为参数传递的问题:推荐使用结构体指针变量作为参数传递
例:不能修改st的地址
struct Student{
int age;//
!!!!!!!!!!
age占4个字节,一个字节8位,8位一个编号(地址),一个字节一个地址,地址只用首字节地址表示
(int *p 表示)
float score;
char name[100];
};
void InputStudent(struct Student * ps){//指针变量无论他指向的数据类型占几个字节,ps本身只占4个字节
ps->age=10;
ps->score=44.4;
strcpy(ps->name,"张三");//不能写成stu.name
}
int main(void){
struct Student st;
InputStudent(&st);
outStudent(st);//对结构体输出可以发送st的地址,也可以发送st的内容
printf("%d %f %s\n",st.age,st.score,st.name) ;
return 0;
}
冒泡排序:
void sort(int *a,int len){
int i,j,t;//如果有i个数字,总共需要比i-1轮,然后每轮里面两个两个比
for(int i=0;i<len-1;i++){//整体比较
for(int j=0;j<len-1-i;j++){
if(a[j]>a[j+1]){
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
}
}
int main(void){
int array[6]={3,5,2,17,9,4};
sort(array,6);
for(int i=0;i<6;i++){
printf("%d ",array[i]);
}
return 0;
}
动态构造存放学生信息的结构体数组,然后按照分数排序输出
struct Student{
char name[100];
int age;
float score;
};
int main(){
int len;
printf("请输入学生个数:\n");
scanf("%d",&len);
struct Student t;
// 动态给学生分配几个内存
struct Student *pst;
pst=(struct Student *)malloc(len*sizeof(struct Student));
//输入
for(int i=0;i<len;i++){
printf("请输入第%d个学生信息:\n",i+1) ;
printf("age=");
scanf("%d",&pst[i].age);
printf("name=");
scanf("%s",pst[i].name);//name是数组名,本身就是数组首元素的地址,不能改成取地址
printf("score=");
scanf("%f",&pst[i].score);
}
for(int i=0;i<len-1;i++){//整体比较
for(int j=0;j<len-1-i;j++){
if(pst[j].score>pst[j+1].score){
t=pst[j];
pst[j]=pst[j+1];
pst[j+1]=t;
}
}
}
printf("=============学生的信息===========\n");
//输出
for(int i=0;i<len;i++){
printf("第%d个学生信息是\n",i+1) ;
printf("age=%d\n",pst[i].age);
printf("name=%s\n",pst[i].name);
printf("score=%f\n",pst[i].score);
}
return 0;
}
枚举:
把一个事物所有可能的取值一一列举出来
enum WeekDay{
Monday,Friday,Sundy
};
int main(void){
enum WeekDay day=Monday;
printf("%d",Sundy);//2
return 0;
}
进制转换
(32)5=3*5+2*1=(17)10;
链表
算法:
通俗的定义:
解题的方法和步骤
狭义定义:
对存储数据的操作
广义定义:
广义的算法也叫泛型
无论数据是如何存储的,对该数据的操作都是一样的
我们至少可以通过两个结构来存储数据
数组
优点:存取速度快
缺点:插入和删除速度慢
链表
特点:插入删除快,查询慢
专业术语
头结点
头结点的数据类型和首节点的类型是一模一样的
头结点是首节前面的那个节点
头结点并不存放有效数据
设置头结点的目的是为了方便对链表的操作
头指针
存放头结点地址的指针
首节点
存放第一个有效数据的节点
尾结点
存放最后一个有效数据的节点
指针域为空
确定一个链表需要一个参数:头指针
*/