一、课前准备
1.Linux和unbutu安装
微信关注 软件管家 公众号->回复Linux->点击虚拟安装Linux系统教程
2.虚拟机联网
3.gcc和vim的安装及使用说明
首先按 :ctrl+shift+T 打开终端
输入sudo apt install gcc
接着输入Ubuntu密码(之前设置的密码)
就可以安装gcc啦
4.做一个简单的例程感受一下吧
(1)首先创建一个vim吧(vim可以理解为一个word文档编辑器)
vim这个编译器有许多快捷键,不掌握这些快捷键是很难完成编译的 vim入门教程
1.输入vim hello.c
2.进入vim后,按 a 进入编辑模式,输入下面的代码
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("Hello Wold!\n");
exit(0);
}
3.按 esc 键,退出编辑模式。再输入 :qw保存并退出
4.终端输入 gcc hello.c -o hello 编译器会自动编译hello.c 并生成一个 hello文件
5.输入 ./hello 运行hello这个文件
看到Hello word! 就算成功运行这个程序啦!
下面可以进入C语言的学习啦
二、数据类型
1.常量
(1) 整形常量:1,79
实型常量:3.14,1.999
字符常量:由单引号引起来的单个字符或转意字符’a’,’\n’,’\015’(八进制)
,’\x7f’(十六进制)
(2).字符串常量:由双引号引起来的一个或多个字符串组成的序列
如"",”a”,”asdXS\n\021”
(3)1标识常量(特点:处理在程序预处理阶段,一改全改,但不检查语法,只是单纯宏体与宏名替换)
a) #define PI 3.14(用宏体替换为宏名)
b) #define ADD 2+3(直接转换到上面)
c) #define MAX(a,b) (a>b?a:b)
(4). 什么时候用宏,什么时候用函数
答:节约时间用宏,保证稳定用函数
2.变量
定义:用来保存一些特定的内容,并且在程序执行过程中随时发生变化的量
1. 存储类型:auto static register extern(说明型)
a) auto:默认,自动分配空间,自动回收空间
b) register:(建议型)寄存器类型 只能定义局部变量,不能定义全局变量,只能定义32位大小的数据类型,寄存器没有地址,所以寄存器类型的变量无法打印出地址查看或使用 sush register int i=1
c) static:静态型,自动华初始值为0或NULL,并值其变量的值有继承性,仅能在局部使用。
d) extern:说明型,意味着不能改变被说明变量的值或类型
2.变量的生命周期和作用范围
a) 全局变量和局部变量
全局:从定义开始到程序结束
局部:从当前程序块开始,到程序块结束
b) 局部变量和局部变量
内部优先
3.运算符和表达式
运算符 | 符号 |
---|---|
1.单目运算符 | +、- 、 *、 \、 % 、 ++、 – 、 % 、/ |
关系运算符 | < <= >= != |
逻辑运算符! | &&(有零取零) 异或(有1取1) |
注意:短路特性 &&左边为0, | |
赋值运算符 | = (a = 3 -> a = a3) |
条件运算符 | ? : |
逗号运算符 | |
求字节数sizeof | 例:printf(“%d\n”,sizeof(int)); |
强制类型转换 | 例:a=(int)f; |
位运算的重要意义 | 硬件、电路很多都用位运算 |
移位操作 <<(右移)
~(取反)
|(按位或,有1取1)
^(按位异或,同值取零不同取一)
&(按位与,有零取零)
例: 将操作位中第n位置1 num=num | 1<<n
将操作数中第n位清0,其他位不变:num=num&~(1<<n)
测试第n定位:if(num & 1 <<n)
从一个指定宽度中取出其中某几位???:段中取出几位
三、输入、输出专题
1、格式化输入输出函数:scanf、printf
int printf(const char *format,)
format :"% [修饰符] 格式字符",
输出格式 | 字符 |
---|---|
十进制整数 | %d |
十六进制无符号整数 | %x |
八进制 | %o |
不带符号十进制整数 | %u |
单一字符 | %c |
字符串 | %s |
指数形式浮点数 | %e |
百分号本身 | %% |
修饰符 | 功能 |
---|---|
m | 输出数据宽度,数据长度<m,左补空格,否则按实际输出 |
.n | 对实数,指定指定小数点后位(四舍五入) |
.n | 对字符串,指定实际输出位 |
- | 输出数据左对齐(缺省右对齐) |
+ | 指定在该符号数的整数正数前显示正号(+) |
0 | 输出数值时指定左面不适用的空位自动添零 |
# | 在八进制和16进制前显示前导0,0x |
l | 在d,o,x,u前指定输出精度为long型 |
l | 在e,g,f前,指定输出精度为double型 |
printf和scanf的注意事项
printf("%s,%d\n",__FUNCTION__,__LINE__)
/* \n有强制刷新缓冲区的作用==查错时printf习惯性加\n
scanf("%d",&i)
//%s慎用,超出会发生越界,因为不知道存储空间大小
scanf放在循环很危险,注意能否接收到正常有效的内容,输出加\n
一般情况输入什么你就里面写什么
scanf("")
2、字符串输入输出函数:getchar、putchar
getchar,回收字符,尤其时功能字符回车、空格等
3、字符串输入输出函数:gets(!)的、对缓冲区不做检查,puts
最好不用gets,用getline
四、流程控制
顺序、选择、循环
NS图,流程图 工具Dia
简单结构与复杂结构:自然流程
- 顺序:语句逐句执行
- 选择:出现一种以上情况
- 循环:在某个条件成立的情况下,重复执行某个动作
关键字:
选择:if-else switch-case
循环:while do-while for if-goto
辅助控制:continue break
1.详解:if-else:
格式 if (exp)
cmd;
else if(exp)
cmd;
注意:else只找与它最近的if相匹配
2.详解 switch-case
语法格式:
switch(exp)
{
case 常量表达式:
break;
case 常量表达式
break;
…
default:
}
3.详解循环:while
while 最少循环0次
do-while 至少循环1次
4.for:(最少循环0次)
佛如(exp1;exp2;exp3)
loop;
if-goto:(慎用:go-to实现的是无条件的跳转,且不能跨函数跳转)
5.死循环:
while(1);
for( ; ; );
杀死死循环ctrl+c
6.辅助控制关键字: break continue
break跳出本层循环
continue跳出本次循环
选择循环练习题
(1)投资问题:以每年10%的单利息投资了100美元,B以每年5%的符合利息投资了100美元。编写一程序,计算要多少年B的投资总额会超过A的,并且显示出到那个时候两个人的各自总资产
(2)从终端读入数据,直到输入0值为止,计算出其中的偶数的个数及平均值和奇数的个数及平均值
(3)从终端输入若干字符,对其元音字母进行统计
(4)写出斐波那契数列的前40项(不能用数组实现)
1 1 2 3 5 8 13 21 。。。
(5)输出九九乘法表
(6)百钱买百鸡:鸡翁一,值钱5;鸡母一,值钱三;三鸡雏,值钱一,问鸡翁,鸡母,鸡雏各几何?
#include<stdio.h>
#include<stdlib.h>
static void text6(void)
{
int x,y,z,s;
for(x=0;x<20;x++)
{
for (y=0;y<100/3;y++)
{
z=100-x-y;
s=5*x+3*y+z/3;
if(s==100&&z%3==0)
{
printf("man=%d\twoman=%d\tchlidren=%d\n",x,y,z);
}
}
}
}
int main()
{
text6();
exit(0);
}
(7)求出1000以内的水仙花数(各位数的立方和为数本身)如153=1+125+27
static void text7(void)
{
int i;
int a,b,c;
for(i=100;i<1000;i++)
{
a=i/100;
b=i%100/10;
c=i%10;
if(a*a*a+b*b*b+c*c*c==i)
printf("The narcissistic number is:%d\n",i);
}
}
(8)求出1000以内的所有质数:2,3,5,7,11,13,17,19…
static void text8(void)
{
int i,j,mark;
for(i=2;i<1000;i++)
{
mark=1;
for(j=2;j<i/2;j++)
{
if(i%j==0)
{
mark=0;
break;
}
}
if (mark)
printf("The prime number is:%d\n",i);
}
return;
}
(9)在终端实现如下输出:
ABCDEF
BCDEF
CDEF
DEF
EF
F
static void text9(void)
{
int line=6,chars=6;
int i;
char ch;
for(i=0;i<line;i++)
{
for(ch='A';ch<'A'+chars;ch++)
{
printf("%c",ch);
}
printf("\n");
chars--;
}
}
(10)在终端输出钻石型
(11)之终端输入N个数(以字母Q/q作为终止),求和。
//注意:不只有输入q才会退出,只要输入不符合scanf的格式的都会退出
static void text11(void)
{
int sum=0;
int num;
printf("Please enter the first integet(q to quit)");
while(scanf("%d",&num)==1)
{
if(num=='q')
break;
sum+=num;
printf("Please enter other intget:");
}
printf("sum=%d",sum);
}
(12)从半径为1开始,输出圆的面积,直到圆的面积为100截至
static void text12(void)
{
int r=1;
float area;
for(area=1;area<=100;r++)
{
area=r*r*PI;
if(area>100)
break;
printf("area is %f\n",area);
}
五、数组
1.一维数组
(1)定义
【存储类型】数据类型 标识符 【下标】
特点:在内存中连续存放
理解:就是一个变量,连续存放
(2)初始化
部分元素初始化,初始化的函数该是多少就是多少,其他数为零
数组不能无条件出现在等号左边
(3)元素引用
数组名 【下标】
(4)数组名
数组名是表示地址的常量
(5)数组越界
数组越界不检查。靠程序员自身敏感程度
数组学习代码
#include<stdio.h>
#include<stdlib.h>
#define M 3
int main()
{
int i;
int arr[M];
printf("%ld\n",sizeof(arr));
for(i=0;i<M;i++)
printf("%p-->%d\n",&arr[i],arr[i]);
exit(0);
}
1.斐波那契数列
#include<stdio.h>
#include<stdlib.h>
#define a 10
int main()
{
int i;
int arr[a]={1,1};
for(i=2;i<10;i++)
arr[i]=arr[i-1]+arr[i-2];
for(i=0;i<10;i++)
printf("%d ",arr[i]);
exit(0);
}
2.数据排序:
冒泡,每次只判断一个数,比大小,朝前或朝后移动(小数慢慢浮起来,大数沉下去慢慢)
#include<stdio.h>
#include<stdlib.h>
#define N 10
static void sort1(void)
{
int i,j,tmp;
int a[N]={45,48,24,46,68,48,24,33,1,2};
for(i=0;i<N;i++)
printf("%d ",a[i]);
printf("\n");
for(i=0;i<(N);i++)
{
for(j=0;j<(N-i);j++)
{
if(a[j]>a[j+1])
{
tmp=a[j];
a[j]=a[j+1];
a[j+1]=tmp;
}
}
}
for (i=0;i<N;i++)
printf("%d ",a[i]);
printf("\n");
}
int main()
{
sort1();
}
选择法,快速排序(一个数据串中,找到最小的放在第一位)
3.进制转换、
//最后一个for循环有问题,一直没改对
static void base_convert(void)
{
int i=0;
int num,base,a[N];
printf("Please enter the converted num:");
scanf("%d",&num);
printf("Please enter the base:");
scanf("%d",&base);
do
{
a[i]=(num%base);
num=num/base;
printf("%d ",a[i]);
i++;
printf("i=%d",i);
}while(num != 0);
printf("\nB");
printf("end i=%d",i);
for(;i>=0;i--)
{
printf("%d",a[i]);
}
}
4.删数法求质数
2.二维数组
(1)定义
【存储类型】 数据类型 标识符 【行下标】 【列下标】
数组名即为数组起始位置
例:int M[2][3] 两行三列的数组
在存储空间按行存放如:M[0][1] M[0][2] M[1][0] M[][1] M[0][1]
部分数组初始化:int a[3][3]={{1,2}{5,6}}
全部数组初始化:int a[3][3]={{1,2,3},{4,5,6},{7,8,9}}
定义可以行省略:例如:int a[][N]={4,8,45,65,8}
(2)元素引用
例:
1.行列互换
include<stdio.h>
#include<stdlib.h>
#define N 3
#define M 2
static void change(void)
{
int i,j;
int a[M][N]={1,2,3,4,5,6},b[N][M];
for(i=0;i<M;i++)
{
for(j=0;j<N;j++)
{
printf("%d",a[i][j]);
b[j][i]=a[i][j];
}
printf("\n");
}
for(i=0;i<N;i++)
{
for(j=0;j<M;j++)
printf("%d",b[i][j]);
printf("\n");
}
}
int main()
{
change();
exit(0);
}
2.求最大值及其所在位置
3.求各行与各列的和
static void sum(void)
{
int i,j;
int a[3][4]={{1,2,3},{4,5,6}};
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
a[2][3]+=a[i][j];
a[2][j]+=a[i][j];
a[i][3]+=a[i][j];
printf("%4d",a[i][j]);
}
printf("\n");
}
for(i=0;i<3;i++)
{
{
for(j=0;j<4;j++)
printf("%4d",a[i][j]);
}
printf("\n");
}
}
4.矩阵乘积
(3)存储形式
在一块空间连续存储
(4)深入理解二维数组
二维数组,本质上是多个一维数组组成的
主要是理解行指针和列指针
3. 字符数组
1.定义,初始化,存储特点
【存储类型】 数据类型 标识符 【行下标】 【列下标】
2.单个字符初始化
输入语句:char str[N]={'a','b','c'};
输出语句:for(i=0;i<N;i++)
printf("%c,str[i]");
3.字符串常量初始化
int main()
{
int N=3;
char str[N];
gets(str);
puts(str);
}
上面这段代码很危险,虽然程序可以运行,但是如果输入超过2个字符
(系统会自动加一个尾零),之后的存储空间如果被保护过,则会发生段错误;
4.输入输出
scanf("%s",str);
printf("%s",str);
注意%s无法获得带分隔符的字符串
例如:
输入 hello word
输出为 hello
输入可以改为scanf("%s%s",str)来解决这个问题;
5.常用函数
为什么字符串有常用函数,而整形,浮点型等其他类型不需要函数呢
因为int等类型可以用if来比大小,也可以相互赋值,但是字符串的名字就是一个地址常量,没办法比较大小,赋值等操作,所以需要常用函数来解决这些问题
不能直接复制 例如: str=“abcde”,这个是不成立的
(1)strlen
作用:以尾零作为结束,显示当前字符串个数 (输入Hello word,返回值为5)
printf("%d\n",strlen(str))
(2)sizeof
作用:该字符串在内存所占字节个数 (输入Hello word,返回值为11)
printf("%d\n",sizeof(str));
(3)strcpy
用法strcpy(str,"abcde"); 作用:将abcde字符串copy到str中
puts(str);
(4)strncpy
用法:strncpy(str,“abcde”,STRSIZE); 作用:将abcde字符串copy到
str中,但是STRSIZE会限制copy个数,防止溢出
(5)stract
char *strcat(char *dest, const char *src);
作用:将两个字符串首尾连接
(6) strncmp
int strcmp(const char *s1, const char *s2);
作用:比较ASCLL码大小,根据比较大小情况,返回值为1,0,-1
6.字符数组的单词计数
计算有多少字符串
#include <stdio.h>
#include<stdlib.h>
int main()
{
char str[128];
int flag=0,count =0;
int i;
gets (str);
for (i=0;str[i]!='\0';i++)
{
if(str[i]==' ')
flag = 0;
else if(flag==0)
{
count++;
flag =1;
}
}
printf("count=%d\n",count);
exit(0);
}
4.多维数组
就是一维二维数组的递推
int a[2][3][4];
六、指针
1.变量与地址
变量名int i 这里的i是给程序员看到,i是一个抽象的地址
2.指针与指针变量
定义:[数据类型] *p=&i;
int *i=1;
int *p;
char *c
doubule *c
无论是什么类型的指针,所占字节数sizeof(c),均为8
虽然指针所占字节,指针变量和他所指类型的值,一定要相符
例子
#include <stdio.h>
#include<stdlib.h>
int main()
{
int i=1;
int *p;
p=&i;
printf("i=%d\n",i); //输出i的值
printf("&i=%p\n",&i); //i的地址
printf("p=%p\n",p ); //p的值,存放的是i的地址
printf("&p=%p\n",&p); // p的地址,一块独立的指针空间
printf("*p=%p\n",*p); //p所指的i中的内容
exit(0);
}
3.直接访问与间接访问
上图中:q=0x3000=&p
p=0x2000=&i
i=1
&q=0x4000
*q=*(&p)=p=&i
**q=*(&i)=i=1
如果能理解上述关系,指针这里基本就没问题了
想访问i的值,可以通过i *p **q三种形式访问
想访问i的地址,可以通过 &i p *q三种形式访问
4.空指针与野指针
指针定义:int *p =NULL (指针定义为NULL,是防止野指针的产生,如果你不清楚指向谁,就定义指针为NULL)
野指针是非常危险的,一定要让指针有去处
5.空类型
void *q=NULL (void类型的指针是一个万金油,它与任何指针类型都可以相互赋值)
6.定义与初始化的书写规则
int i=1:
int *p=&i
int **q =&i
7.指针与运算
指针的运算较少只有 &(取地址) *(取值) 关系运算 ++ --
8.指针与数组
(1).指针与一维数组
#include <stdio.h>
#include<stdlib.h>
int main()
{
int a[3]={1,2,3};
int *p=a;
int i;
for(i=0;i<sizeof(a)/sizeof(a[0]);i++)
printf("%d",a[i])
printf("\n");
exit(0);
}
(1)a[i] = *(a+i) = *(p+i)= p[i]
a是数组名,是常量不可以做赋值变换,例如a++
但是p是一个指针,可以做赋值变换,p++
注意:p++和p+1的区别
学会数组常量和数组变量的区别
(2).指针与二维数组
在行上的指针和在列上的指针
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a[2][3]={1,2,3,4,5,6};
int i,j;
for()
3
printf("%ld\n",sizeof(arr));
for(i=0;i<M;i++)
printf("%p-->%d\n",&arr[i],arr[i]);
exit(0);
}
(3).指针与字符数组
9.const与指针
这部分李老师讲的非常好,一听就懂
#define PI 3.14
const float PI=3.14
宏定义和const可以代替使用,区别是宏定义不做检测,const检查
(1)常量指针
用处,在封装一些函数时常用这种方式进行封装,因为其他人不可以通过指针来修改函数
const int *p
int const *p
int i=1;
const int *p =&i;
正确: i=10;//可以
错误:*p=10;//不可以
作用:保护*p不变,*p是只读的
(2)指针常量
int *const p;
int i=1;
int j=10;
int *const p =&i;
正确:*p=10;//可以改p指向地址里的值
错误:p=&j;
作用:指针变量指的东西不变
fopen:拷贝一块内存数据
口诀:常量指针不可以该值,指针常量,不可以改指针
例如:定义 const int *const p = &i;此时既不能改变指针里的值,又不能改变指针的指向
10.指针数组与数组指针
(1)数组指针(一个指针,指向数组)
定义:【存储类型】 数据类型 (*指针名) 如:int [3] *p
int main{
int a[2][3]={1,2,3,4,5,6};
int i,j;
int *p=*a;
int (*q)[3]=a;
for()
}
(2)指针数组(n个指针挨在一起)
int *arr[3]={3,5,7}
11.多级指针
不常用
七、函数
1.函数的定义
数据类型 函数名【数据类型 形参名,数据类型 形参名。。。。。】
int (返回的数据类型) arr (int argc,char *argv[] )
return 0; (结束当前函数)
argc 是 argument count的缩写,表示传入main函数的参数个数;
argv 是 argument vector的缩写,表示传入main函数的参数序列或指针,并且第一 个参数argv[0]一定是程序的名称,并且包含了程序所在的完整路径,所以确切的说需要我们输入的main函数的参数个数应该是argc-1个;
2.函数的传参
include<stdio.h>
#include<stdlib.h>
int value(int a,int b)
{
printf("%d %d\n",a,b);
return 0;
}
int main()
{
int i=3,j=5;
value(i,j);
return 0;
}
(1)值传递
用一个程序来解释值传递和地址传递的区别
#include<stdio.h>
#include<stdlib.h>
void swp(int i,int j)
{
int tmp;
tmp =i;
i=j;
j=tmp;
}
int main()
{
int i=3,j=5;
int tmp;
swp(i,j);
printf("%d\t%d\n",i,j);
return 0;
}
此条语句输出还是i=3,j=5并没有起到交换i与j的作用,如下图,只是新开辟出一块地址,进行了交换,仅i,j名字是一样的,但根本不是一回事
(2)地址传递
void swp(int *p,int *q)
{
int tmp;
tmp =*p;
*p=*q;
*q=tmp;
}
int main()
{
int i=3,j=5;
int tmp;
swp(&i,&j);
printf("%d\t%d\n",i,j);
return 0;
}
上面这个函数达到了交换数的目的,通过另外一个函数达到修改其他函数的目的,一般用地址传递
(3)全局变量
3.函数的调用
(1)嵌套调用
嵌套调用小例子:
#include<stdio.h>
#include<stdlib.h>
int max(int a,int b,int c)
{
int tmp;
tmp= a>b ? a:b;
return tmp>c?tmp:c;
}
int min(int a,int b,int c)
{
int tmp=a>b ? b:a;
return tmp>c ? c:tmp;
}
int dist(int a,int b,int c)
{
return max(a,b,c)-min(a,b,c);
}
int main()
{
int res;
int a=3,b=5,c=10;
res = dist(a,b,c);
printf("res=%d\n",res);
return 0;
}
(2)递归
递归:一个函数,直接或间接调用自身
一个递归小程序:
FUNCTION:该函数的作用此时运行函数的函数名
#include<stdio.h>
#include<stdlib.h>
int b()
{
printf("[%s]begin!\n",__FUNCTION__);
printf("[%s]b()end!\n",__FUNCTION__);
return 0;
}
int a()
{
printf("[%s]begin!\n",__FUNCTION__);
printf("[%s]call b()!\n",__FUNCTION__);
b();
printf("[%s]b() returned!\n",__FUNCTION__);
printf("[%s]end!\n",__FUNCTION__);
return 0;
}
int main()
{
printf("[%s]begin!\n",__FUNCTION__);
printf("[%s]call a()!\n",__FUNCTION__);
a();
printf("[%s]a() returned!\n",__FUNCTION__);
printf("[%s]end!\n",__FUNCTION__);
return 0;
}
例题:求一个数的阶乘
unc(int n)
{
if (n==1||n==0)
return 1;
return n*func(n-1);
}
int main()
{
int n;
int res;
scanf("%d",&n);
res =func(n);
printf("n!=%d",res);
return 0;
}
4.函数与数组
(1)函数与一维数组
#include<stdio.h>
#include<stdlib.h>
void print_arr (int *p)
{
int i;
printf("%s:%d\n",__FUNCTION__,sizeof(p));
}
int main()
{
int a[]={1,3,5,7,9};
printf("%s:%d\n",__FUNCTION__,sizeof(a));
print_arr(a);
exit(0);
}
(2)函数与二维数组
5.函数与指针
八、构造类型
1.结构体
(1)产生及意义
数组只能存储一种类型,结构体可以存储多种类型
(2)类型描述
struct 结构体名字
{
数据类型 成员1;
数据类型 成员2;
…
};
结构体定义在函数外(少数编译器不允许编译在函数内部)
(3)嵌套定义
定义方式一,分开定义
struct birthday_st
{
int year;
int month;
int day;
};
struct student_st
{
int id;
char name[NAMESIZE];
int year,month,day;
int math;
int chinese;
};
定义方式二,内部定义
struct birthday_st
{
int year;
int month;
int day;
};
struct student_st
{
int id;
char name[NAMESIZE];
struct birth
int year,month,day;
int math;
int chinese;
};
(4)定义变量(变量,数组,指针),初始化及成员引用
int main()
{
strunct simp_st a={123,456.789,‘a’};
a.i=112233;
printf("%d %f %c\n",a.i,a.f,a.ch);
exit(0);
}