C语言概述
-
为什么要学习C语言
1)C的起源和发展
2)C的特点 优点:代码量小(WPS与OFFICE)
速度快 Windows(C、C++) Unix(C) Linux(C)
Java没有指针,不能访问硬件,不能写操作系统,上面三个操作系统没有中国人开发
功能强大
缺点:危险性高
开发周期长(面向过程,写大项目时容易崩溃)
可移植性不强
3)C的应用
CS内核引擎用的是C写的,所以用很老的机子也能跑,不卡 -
怎么学习C语言
途径:多思考,多上机
目标:能看得懂程序,能调试程序,自学能力要强
-
学习的目标
-
常见问题答疑
-
学习java为什么建议先学C语言
-
没学过计算机专业课程能够学懂C语言嘛?
-
英语和数学不好能学好C语言嘛?
5.课程计划
6.举个例子:一元二次方程 -
C语言编程预备知识
1.CPU内存条 硬盘 显卡 主板 显示器 之间的关系
2.HelloWorld程序如何运行起来的
3.什么是数据类型
基本类型数据
整数
整型 --int ----4
短整型 --short int —2
长整型 --long int ----8
浮点数【实数】
单精度浮点数 --float —4
双精度浮点数 --double ----8
字符
char ----1
复合类型数据
结构体
枚举
4.什么是变量
请求操作系统,在内存条里找到空闲的存储空间,产生一一对应的关系
5.CPU内存条 vc++6.0 操作系统 之间的关系
6.变量为什么必须初始化
所谓初始化就是赋值的意思:
当变量没初始化时,变量将把之前操作系统在内存遗留下来的数据赋给变量,导致变量的赋值出错。
i=-858993460请按任意键继续. . .
7.如何定义变量
8.什么是进制
10进制(10个手指)
9.常量在C语言中是如何表示的
10.常量是以什么样的二进制代码存储在计算机中
整数是以补码的形式转化为二进制代码存储在计算机中
实数是以IEEE754标准转化为二进制代码存储在计算机中
字符的本质实际也是与整数的存储方式相同
11.代码规范化
可读性更强
12.什么是字节
存储数据的单位,硬件所能访问的最小单位
位 0001010101
1字节 = 8位 1k = 1024字节
CPU只能控制到字节,控制到位需要位运算符
一般卖电脑的按1000算的,实际上是1024 320G硬盘 实际只有300G
13.不同类型数据之间相互赋值的问题
14.什么是ASCII
一种规定,规定了不同字符用哪个整数值去表示
15字符的存储【字符本质上与整数的存储方式相同】
printf() —将变量的内容输出到显示器中
四种:1.printf(“字符”);
int i = 10;//将10以0101001二进制代码存入i中
printf("%d",i);//以十进制输出
为什么需要输出控制符?
- 01组成的代码可以表示数据,也可以表示指令
- 如果01组成的代码表示数据的话,那么同样的01代码以不同的格式输出格式会有不同的结果
scanf()通过键盘将数据输入到变量中
-
scanf(“输入控制符”,输入参数)
功能:将键盘输入的字符转化为输入控制符所规定的数据,然后存入以输入参数的值为地址的变量中
#include<stdio.h>
int main()
{
int i;
scanf("%d",&i);//&i 表示i的地址,&是一个取地址符
printf("i=%d\n",i);
return 0;
}
2.scanf(“非输入控制符 输入控制符”,输入参数)
功能:将键盘输入的字符转化为输入控制符所规定的数据,然后存入以输入参数的值为地址的变量中,非输入控制符必须原样输入
#include<stdio.h>
int main()
{
int i;
scanf("m%d",&i);//&i 表示i的地址,&是一个取地址符
printf("i=%d\n",i);
return 0;
}
如何使用scanf编写出高质量的代码
1.使用scanf之前最好先使用printf提示,提示用户用什么方式来输入
2.scanf中尽量不要使用非输入控制符,尤其不要用\n
3.应该编写代码对用户的非法输入做适当的处理
#include<stdio.h>
int main()
{
int i;
char ch;
scanf("%d",&i);//123m
printf("i=%d\n",i);
while((ch = getchar())!='\n')
continue;
int j;
scanf("%d\n",&j);
printf("j=%d\n",j);
return 0;
}
运算符
算数运算符: + - * /(除) %(取余数)
关系运算符: > >= < <= != ==
逻辑运算符: ! && ||
c语言对真假的处理:非零是真,零是假。真是1,假是0
#include<stdio.h>
int main()
{
int i=10;
int k =20;
int m;
m=(3>2) && (k=8);
printf("m=%d,k=%d\n",m,k);
return 0 ;
}
#include<stdio.h>
int main()
{
int i=10;
int k =20;
int m;
m=(1>2) && (k=8);//m=?,k=? 左边表达式为假,后面不在执行
printf("m=%d,k=%d\n",m,k);
return 0 ;
}
赋值运算符: = += *= /= -=
优先级别:
算术>关系>逻辑>赋值
流程控制
1.什么是流程控制
程序代码执行的顺序
2.流程控制的分类
顺序
选择
定义:某些代码可能执行,也可能不执行,有选择的执行某些代码
分类:if
1.if最简单的用法
if(表达式)
语句
2.if的范围问题
#include<stdio.h>
int main()
{
if(1>2)
printf("AAAA\n");
printf("BBBB\n");
return 0;
}
解释:if默认只能控制语句A的执行或者不执行,if无法控制语句B的执行或者不执行,语句B一定会执行
#include<stdio.h>
int main()
{
if(1>2)
{
printf("AAAA\n");
printf("BBBB\n");
}
return 0;
}
此时if可以控制语句A和语句B,if默认只能控制一个语句的执行或不执行,如果要控制多个语句就必须用{}括起来
3.if…else…的用法
#include<stdio.h>
int main()
{
if(1>2)
printf("AAAA\n");
else
printf("BBBB\n");
printf("CCCC\n");
return 0;
}
4.if…else if…else…的用法
if(1)
A;
else if(2)
B;
else if(3)
C;
else
D;
#include<stdio.h>
int main()
{
if(1>2)
printf("AAAA\n");
printf("AAAA\n"); //出错,if默认只能控制一条语句
else if(2)
printf("BBBB\n");
else if(1)
printf("CCCC\n");
else
printf("DDD\n");
return 0;
}
5.C语言对真假的处理
非零是真,零就是假。真用1表示,假用零表示
6.if举例—求分数的等级
90<=score<=100
#include<stdio.h>
int main()
{
float score;
printf("请输入你的考试成绩");
scanf("%f",&score);
if(score > 100)
printf("这是做梦\n");
else if(score >= 90 && score < 100)
printf("优秀\n");
else if(score >= 60 && score <= 90)
printf("良好\n");
else
printf("不及格\n");
return 0;
}
#include<stdio.h>
//互换两个数字
int main()
{
int i = 3;
int j = 5;
int t;//定义临时变量
//i=j; 这样无法完成i和j的互换
//j=i;
t=i;
i=j;
j=t;
printf("i = %d,j=%d\n",i,j);
return 0;
}
#include<stdio.h>
int main()
{
int a,b,c;
printf("请输入三个整数(中间以空格分隔)");
scanf("%d %d %d",&a,&b,&c);
//编写代码完成a是最大值 b是中间值 c是最小值
if(a<b)
{
t=a;
a=b;
b=t;
}
if(a<c)
{
t=a;
a=c;
c=t;
}
if(b<c)
{
t=b;
b=c;
c=t;
}
printf("%d %d %d\n",a,b,c);
return 0;
}
如何看懂一个程序,分三步:
1.流程
2.每个语句功能
3.试数
7. if常见问题解析
(1)空语句的问题
if(3>2);
等价于
if(3>2)
;//这是一个空语句s
(2)
if(3>2)//如果这里加分号,那么程序编译到else处就会出错,没有语句是esle开头的
printf("哈哈\n");
else
print("呵呵\n");
(3)
总结:
if(表达式1)
A;
else if(表达式2)
B;
else if(表达式3)
C;
else
D;
//即便表达式1和2都成立,也只会执行A语句
(4)
if(表达式1)
A;
else if(表达式2)
B;
else if(表达式3)
C;
//这样写语法不会出错,但逻辑上有漏洞
(5)
if(表达式1)
A;
else if(表达式2)
B;
else if(表达式3)
C;
else (表达式4)
D;//这样写是不对的,它把表达式4和后面的D看成是一个语句了
(6)
if(表达式1)
A;
else if(表达式2)
B;
else if(表达式3)
C;
else (表达式4);//无意义的语句
D;//语法上没错,但
循环
定义:某些代码会被重复执行
分类:for
#include<stdio.h>
int main()
{
int i;
int sum = 0;
for(i=1;i<=100;i++)
sum = sum + i;
printf("sum = %d\n",sum);
return 0;
}
3执行完了才表示一次循环结束
#include<stdio.h>
int main()
{
int i;
int sum = 0;
for(i=1;i<10;i+=2){
sum = sum + i;
//printf("今天我很开心了")
}
printf("i=%d\n",i);
printf("sum = %d\n",sum);
return 0;
}
执行了几次?i的值最终是多少?
5 i=11
for和if结合
//求1-100之间能被3整除的数字之和 (从小的推大的)
#include<stdio.h>
int main()
{
int i;
int sum = 0;
for(i=3;i<10;i++)
{
//如果i能被3整除
if(i%3==0)
sum = sum+i;
}
printf("sum=%d\n",sum);
}
如果我把printf移动进去,那么结果会是什么???
#include<stdio.h>
int main()
{
int i;
int sum = 0;
for(i=3;i<10;i++)
{
//如果i能被3整除
if(i%3==0)
sum = sum+i;
printf("sum=%d\n",sum);
}
}
思路:
i -> 3 4 5 6 7 8 9 10 11
3 3 3 9 9 9 18 18
求1到100之间的奇数之和
int i;
int sum = 0;
for(i=0;i<101;i++)
{
if(i%2 == 1)
sum += i;
}
printf("sum=%d\n",sum);
求1到100之间的奇数的个数
int i;
int cnt = 0;//个数一般用cnt表示
for(i=0;i<101;i++)
{
if(i%2 == 1)
++cnt;
}
printf("cnt=%d\n",cnt);
求1到100之间的奇数的平均值
int i;
int sum = 0;
int cnt = 0;//个数一般用cnt表示
for(i=0;i<101;i++)
{
if(i%2 == 1)
sum +=i;
++cnt;
}
printf("sum=%d\n",sum);
printf("cnt=%d\n",cnt);
------>加大括号
float avg;
avg= 1.0*sum /cnt;//1.0默认是double类型
求1到100之间的奇数之和,再求偶数之和
int i;
int sum1 = 0;
int sum2 = 0;
for(i=0;i<101;i++)
{
if(i%2 == 1)
sum1 +=i;
else
sum2 +=i;
}
printf("sum1=%d\n",sum1);
printf("sum2=%d\n",sum2);
求1+1/2+1/3+…+1/100的值
#include<stdio.h>
int main()
{
int i;
int sum = 0;
for(i=1;i<=100;i++)
{
sum = sum + 1/i;
}
printf("sum=%d\n",sum);
}
这样写对嘛?1/i等于什么?0
#include<stdio.h>
int main()
{
int i;
float sum = 0;
for(i=1;i<=100;i++)
{
sum = sum + 1/(float)i;
//sum = sum + (float)(1/i);这样写是不对的
//更简单的写法:sum = sum + 1.0/i; 推荐
}
printf("sum=%f\n",sum);
}
#include<stdio.h>
int main()
{
float i;
float sum = 0;
for(i=1.0;i<=100;i++)
{
sum = sum + 1/i;
}
printf("sum=%f\n",sum);
}
浮点数的存错所带来的问题:
float和double都不能保证可以精确存储一个小数
举例:
有一个浮点型变量x,如何判断x的值是否为0
if(0==x)-------> if(|x-0.000001|<=0.000001)
是
else
不是
为什么循坏中更新的变量不能定义成浮点型?
浮点型是非准确储存, 可能i = 100.0000000001
强制类型转化:
格式: (数据类型)(表达式)
功能: 把表达式的值强制转化为前面所执行的数据类型
例子: (int)(4.5+2.2) ------> 6
(float)(5) -------->5.000000
多个for循环的嵌套使用:
for(1;2;3)
for(4;5;6)
A;
B;
1–>2–>4–>5—>A—>6—>5—>A 6不成立执行3 2---->4 不成立执行B
进制:
-
什么叫进制
逢n进1
-
把r进制转成十进制
-
十进制转r进制
4.不同进制所代表的数值之间的关系
自增 自减 三目运算符 逗号运算符
自增:
前自增 ++i
后自增i++
异同:
相同:最终都是i的值加1
不同:前自增整体表达式的值是i加1之后的值
后自增整体表达式的值是i加1之前的值
#include<stdio.h>
int main()
{
int i;
int j;
int k;
int m;
i = j = 3;//等价于i=3;j=3;
k = i++;
m = ++j;
printf("i=%d,j=%d,k=%d,m=%d\n",i,j,k,m);
return 0;
}//i=4,j=4,k=3,m=4
为什么会出现自增? i+=1 等价于i= i+1;
1.代码更精炼
2.自增速度更快//i++是直接放在寄存器内部进行处理的,速度会更快
学习自增要明白几个问题。
1编程要尽量屏蔽前自增和后自增的区别
2i++和++i不要把它作为完成复合的语句使用 i++ + ++i +i;//不规范
顺序点: , () ;
i++ , ++i 前面的值会变 i=2
三目运算符
A?B:C 等价于
if(A)
B;
else
C;
逗号运算符
(A,B,C,D)
从左到右执行,最终表达式的值是最后一项的值
#include<stdio.h>
int main()
{
int i;
int j =2;
i=(j++,++j,j+2,j-3)
printf("%d\n",i);
return 0;
}//3
j++ —> j=3 ++j---->j=4 j+2这里j的值没有改变,j+2和j+=2不一样
#include<stdio.h>
int main()
{
int i,j;
int cnt = 0;
for(i=0;i<3;i++)
for(j=2;j<5;j++)
{
cnt ++;
printf("哈哈%d\n",cnt);
}
printf("嘻嘻\n");
return 0;
}
while
1.执行顺序
格式:while(表达式)
语句;
2.与for相互比较
for(1;2;3)
A;
等价于
1;
while(2)
{
A;
3;
}
#include<stdio.h>
int main()
{
int sum=0;
int i;
// for(i=1;i<101;i++)
// {
// sum = sum + i;
// }
i = 1;
while (i<101)
{
sum = sum +i;
++i;
}
printf("sum = %d\n",sum);
return 0;
}
for的逻辑性更强,更不容易出错
3.举例
判断一个数是不是回文数(正着写和倒着写一样,121,12321)
#include<stdio.h>
int main()
{
int val;//存放待判断的数字
int m;
int sum = 0
printf("请输入您需要判断的数字");
scanf("%d",&val);
m = val;
while(m)
{
sum = sum * 10 +m%10;
m/=10;
}
if(sum == val)
printf("Yes!\n");
else
printf("No!\n");
return 0;
}
//斐波那契序列1 2 3 5 8 13
#include<stdio.h>
int main()
{
int n;
int f1,f2,f3;
int i;
f1=1;
f2=2;
printf("请输入您需要求的序列:");
scanf("%d",&n);
if(1==n)
{
f3=1;
}
else if(2==n)
{
f3=2;
}
else
{
for(i=3;i<=n;++i)
{
f3=f1+f2;
f1=f2;
f2=f3;
}
}
printf("%d\n",f3);
return 0;
}
do…while
主要用于人机交互
do
{
…
}while(表达式);
一元二次方程
#include<stdio.h>
#include<math.h>
int main()
{
double a,b,c;
double delta;
double x1,x2;
char ch;
do
{
printf("请输入一元二次方程三个系数:\n");
printf("a = ");
scanf("%lf",&a);
printf("b = ");
scanf("%lf",&b);
printf("c = ");
scanf("%lf",&c);
delta = b*b-4*a*c;
if(delta > 0)
{
x1 = (-b + sqrt(delta))/(2*a);
x2 = (-b - sqrt(delta))/(2*a);
printf("有两个解,x1=%lf,x=%lf\n",x1,x2);
}
else if(0==delta)
{
x1=x2=(-b)/(2*a);
printf("有唯一解,x1=x2=%lf\n",x1,x2);
}
else
{
printf("无实数解!\n");
}
printf("您想继续嘛(Y/N):");
scanf(" %c",&ch);//%c前面必须加一个空格
}while('y' == ch || 'Y' == ch);
return 0;
}
switch
#include<stdio.h>
#include<math.h>
int main()
{
int val;
printf("请输入您要进入的楼层:");
scanf("%d",&val);
switch(val)
{
case 1:
printf("1层开\n");
break;
case 2:
printf("2层开\n");
break;
case 3:
printf("3层开\n");
break;
default:
printf("没有这一层");
break;
}
}
如果注释掉case1里面的break,那么结果是---->1层开!2层开
流程:case1,case2,case3,default这都是程序的入口,一旦找到程序的入口,这些case1,case2,case3,default都没有意义的了,就依次执行!
break和continue
break如果用于循坏用来终止循环
如果用于switch用来终止switch
不能直接用于if,除非if属于循环内部的一个子句
#include<stdio.h>
int main()
{
int i;
for(i=0;i<3;++i)
{
if(3>2)
break;//break虽然是if内部语句,但终止的却是外部的for循环
printf("嘿嘿!\n");
}
return 0;
}
在多层循环中,break只能终止就近的循环
#include<stdio.h>
int main()
{
int i,j;
for(i=0;i<3;++i)
{
for(j=1;j<4;++j)
break;
printf("嘿嘿!\n");
}
return 0;
}
多层switch嵌套中,终止的是就近的switch
continue
用于跳过本次循环余下的语句,转去判断是否需要执行下次循环
1.for(1;2;3)
{
A;
B;
continue;//执行这个,会去执行3,c被跳过
c;
}
2.while(表达式)
{
A;
B;
continue;//执行这个,会去执行表达式,c被跳过
c;
}
数组
#include<stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
//a是数组的名字,5表示数组元素的个数,并且这个5个元素用a[0]~a[4]表示
int i;
for(i=0;i<5;++i)
printf("%d\n",a[i]);
printf("%d\n",a[100]);
return 0;
}
为什么需要数组?
为了解决大量同类型数据的存储和使用问题
为了模拟现实世界
数组的分类
一维数组
怎么定义一维数组
为n个变量连续分配存储空间
所有变量数据类型必须相同
所有变量所占字节大小必须相等
操作:
初始化
完全初始化: int a[5]={1,2,3,4,5}
不完全初始化,未初始化的元素自动为0:int a[5]={1,2,3};
不初始化,所有元素都是垃圾值 int a[5];
清零 int a[5] = {0};
错误写法: int a[5];
a[5] = {1,2,3,4,5,};//错误,只有在定义数组的同时才可以整体赋值,其他情况下整体赋值都是错误的
int a[5] = {1,2,3,4,5}
a[5] = 100;//没有a[5]这个元素
int a[5]={1,2,3,4,5};
int b[5];
如果要把a数组中的值全部复制给b数组
错误写法: b=a;//error
正确写法: for(i=0;i<5;++i)
b[i] = a[i];
倒置数组
#include<stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int i,j;
int t;
i=0;
j=4;
while(i<j)
{
t= a[i];
a[i] = a[j];
a[j] = t;
++i;
--j;
}
for(i=0;i<5;++i)
printf("%d\n",a[i]);
return 0;
}
一维数组名不代表数组中所有元素,一维数组名代表数组第一个元素的地址
二维数组
多维数组
函数
为什么需要函数
避免了重复性操作,有利于程序的模块化
什么叫函数
逻辑上:能够完成特定功能的独立代码块
物理上:能够接收数据,能够对接收的数据进行处理,将数据处理的结果返回
总结:是个工具,为了解决大量类似问题而设计的,函数可以看成一个黑匣子
如何定义函数
函数的返回值 函数的名字(函数的形参列表)
{
函数的执行体
}
1.函数定义的本质是详细描述函数之所以能够实现某个特定功能的具体方法
2.return: 终止被调函数,向主调函数返回表达式的值
3.函数的返回值类型也称函数的类型,因为如果函数名前的返回值类型和函数执行体中return表达式;中表达式的类型不同的话,则最终函数返回值类型 以函数名前的返回值类型为准。
例子: 最终以int为准
函数的分类
有参函数和无参函数
有返回值和无返回值函数
库函数和用户自定函数
普通函数和主函数(main)
一个程序必须有且只能有一个主函数,主函数可以调普通函数,其他函数不能调用主函数。普通函数可以相互调用,主函数是程序的入口也是出口
判断是不是一个素数
注意的问题
常用的系统函数
专题:
递归(栈)
指针
#include<stdio.h>
int main()
{
int * p;//p是变量的名字,int * 表示p变量存放的是int类型变量地址
int i = 3;
//p=&i;//ok
//p=i;//error,因为类型不一致,p只能存放int类型变量的地址,不能存放int变量的值
//p=55;//error,原因同上
return 0;
}
#include<stdio.h>
int main()
{
int * p;//p是变量的名字,int * 表示p变量存放的是int类型变量地址
//int * p; 不能表示定义了一个名字叫做*p的变量
//int * p; 这样理解:p是变量名,p变量的数据类型是int *类型,所谓int *;类型就是存放int变量地址的类型
int i = 3;
p=&i;
/* p保存了i的地址,因此p指向i.
p不是i,i也不是p。修改p的值不影响i。修改i也不影响p
如果一个指针变量指向某个普通变量 ,则
*指针变量 就完全等同于 普通变量
例子:
如果p是个指针变量,并且p存放了普通变量i的地址
则p指向了普通变量i
*p 就完全等同于 i 或者说:在所有出现*p的地方都可以替换为i,反之也行
*/
return 0;
}
#include<stdio.h>
int main()
{
int * p;
int i = 3;
int j;
p=&i;
j = *p;//*p就是以p的内容为地址的变量
printf("i=%d,j=%d\n",i,j);
return 0;
}
指针就是地址,地址就是指针。 指针就是一个值,一个内存单元的编号
地址就是内存单元的编号
指针变量是存放地址的变量
指针和指针变量是两个不同的概念
但是要注意:我们叙述时会把指针变量简称为指针,实际它们含义不一样
指针:C语言的灵魂
表示一些复杂的数据结构
快速的传递数据
使函数返回一个以上的值
能直接访问硬件
能够方便处理字符串
是理解面向对象语言中引用的基础
指针的定义
地址:内存单元的编号,从零开始的非负整数
范围:4G
指针:本质是一个操作受限的非负整数(只能相减)
基本类型指针常见错误:
#include<stdio.h>
int main()
{
int * p;
int i = 3;
*p = i; //把一个没有开辟的内存地址,垃圾变量进行赋值。一定会报错,很危险
printf("%d\n",i);
return 0;
}
经典指针互换两个数字:
#include<stdio.h>
//不能完成互换
void swap(int a,int b)
{
int t;
t=a;
a=b;
b=t;
return;
}
int main()
{
int a = 3;
int b = 5;
swap(a,b);
printf("a=%d,b=%d\n",a,b);
return 0;
}
#include<stdio.h>
//不能互换
void swap(int * p,int * q)
{
int * t;
t=p;
p=q;
q=t;
return;
}
int main()
{
int a = 3;
int b = 5;
swap(&a,&b);//swap(*p,*q)是错误的 swap(a,b);也是error
printf("a=%d,b=%d\n",a,b);
return 0;
}
#include<stdio.h>
void swap(int * p,int * q)
{
int t;//如果要互换*p和*q,则t必须定义为int
t= *p;
*p=*q;
*q=t;
return;
}
int main()
{
int a = 3;
int b = 5;
swap(&a,&b);//swap(*p,*q)是错误的 swap(a,b);也是error
printf("a=%d,b=%d\n",a,b);
return 0;
}
*的含义:
1.乘法
2.定义指针变量 int *p;//定义一个名字叫p的变量,int *表示p只能存放
3.指针运算符
该运算符放在已经定义好的指针变量的前面
如果p是一个已经定义好的指针变量
则*p表示以p的内容为地址的变量
函数执行完,空间被释放,所以这样不能改变a,b的值
如何通过被调函数修改主调函数普通变量的值:1.实参必须为该普通变量的地址
2.形参必须为指针变量
3,在被调函数中通过: *形参名 = … 的方式就可以修改主调函数相关变量的值
指针的分类:
1基本类型指针
2指针和数组
指针和一维数组
一维数组名:是个指针常量,存放的是一维数组第一个元素的地址
下标和指针的关系
如果p是个指针变量,则p[i]永远等价于*(p+i)
确定一个一维数组需要几个参数,如果一个函数要处理一个一维数组,则需要接受该数组的哪些信息
数组第一个元素的地址、数组的长度
指针变量的运算
不能相加、相乘、相除
如果两个指针变量指向的是同一块连续空间的不同存储单元,则这两个指针变量才能相减
一个指针变量到底占几个字节
sizeof(数据类型):返回值就是所占的字节数
假设p指向char类型变量(1个字节)
假设q指向int类型变量(4个字节)
假设r指向double类型变量(8个字节)
p q r本身所占的字节数是否一样?
答案:一样
总结:一个指针变量,无论它指向的变量占几个字节,该指针变量本身只占四个字节。
一个变量的地址使用该变量的首字节的地址来表示
指针和二维数组
3指针和函数
4指针和结构体
5多级指针
专题:
动态内存分配
传统数组的缺点:
1.数组长度必须事先指定,且只能是常整数,不能是变量
例子:int a[5]
2.传统形式定义的数组,该数组的内存程序员无法手动释放,数组一旦定义,系统为该数组分配的存储空间就会一直存在,除非数组所在的函数运行结束。
3.数组的长度不能在函数运行的过程中动态的扩充或缩小。一旦定义长度就不能在改变了。
4.A函数定义的数组,在A函数运行期间可以被其他函数使用,但A函数运行完毕之后,A函数中的数组将无法在被其他函数使用
传统方式定义的数组不能跨函数使用
为什么需要动态分配内存
很好的解决了传统数组的缺陷
动态内存分配举例_动态数组的构造
#include<stdio.h>
#include<malloc.h>
//malloc 是memory(内存) allocate(分配)的缩写
int main()
{
int i =5;//分配了4个字节 静态分配
int * p = (int *)malloc(4);
/*1.要使用malloc函数,必须添加malloc.h这个文件
2.malloc函数只有一个形参,并且形参是整型
3. 4表示请求系统为本程序分配4个字节
4.malloc函数会为我们分配地址,只能返回第一个字节的地址
5. 分配了8个字节,p变量占了4个字节,p所指向的内存也占4字节
6.p本身所占的内存是静态的,p所指向的内存是动态的
*/
*p = 5;//*p代表一个int变量,*P整型变量内存分配和i变量分配不同
free(p);//把p所指向的内存释放,p本身的内存是静态的,不能由程序员释放,p本身的内存只能在p变量函数运行终止时由系统自动释放
return 0;
}
#include<stdio.h>
#include<malloc.h>
int main()
{
int a[5];//如果int占4字节,则本数组总共包含20个字节,每4字节被当做一个int变量使用
int len;
int * pArr;
int i;
printf("请输入你需要存放的元素个数");
scanf("%d",&len);
pArr = (int *)malloc(4*len);
//对动态一维数组进行赋值
for(i=0;i<len;i++)
scanf("%d",&pArr[i]);
//对一维数组进行输出
printf("一维数组的内容是:\n");
for(i=0;i<len;++i)
printf("%d\n",pArr[i]);
free(pArr);//释放动态分配的数组
return 0;
}
静态内存和动态内存的比较
静态内存是由系统自动分配,由系统自动释放
静态内存是在栈中分配的
动态内存是由程序员手动分配,手动释放
动态内存是在堆分配的
跨函数使用内存的问题
结构体
为什么需要结构体?
为了表示一些复杂的事物,而普通的基本类型无法满足实际要求
什么叫结构体
把一些基本类型数据组合在一起形成新的复合数据类型,叫结构体
如何定义结构体
推荐使用第一种
怎么使用结构体变量?
赋值和初始化
定义的同时可以整体赋值
如果定义完之后,则只能单个赋初值
如何取出结构体变量的每个成员
1.结构体变量名.成员名
2.指针变量名->成员名,在计算机内部会被转化成 (*指针变量名).成员名的方式来执行,所以说这两种方式是等价的
结构体变量的运算
结构体变量和结构体变量指针作为函数参数传递问题
推荐使用结构体指针变量作为函数参数来传递
举例:
动态构造存放学生信息的结构体数据
枚举:
什么是枚举
把一个事务所有可能一一列举出来
怎么使用枚举
枚举的优缺点
代码更安全,书写麻烦
链表
算法:
通俗定义:解题的方法和步骤
狭义定义:对存储数据的操作
广义定义:广义的算法也叫泛型,无论数据是如何存储的,对该数据的操作都是一样的