一、make与makefile
1.回顾:
目标的语法
目标名:依赖目标
@命令
@命令
make的命令行使用
make -f make脚本文件 目标名
2.目标的划分
目标依赖
3.默认规则:
a.不指定目标,执行第一个目标
b.不指定make文件,默认文件是makefile Makefile
makefile优先
4.目标的调用规则:(make把目标当作文件)
make执行目标:
搜索与目标相同的文件
如果文件存在,则判定日期。
日期最新,则停止执行,输出提示
日期不是最新,则进行执行。
比较:当前目标与依赖目标
5.建议:
只要有文件输出,就把任务作为一个目标,并且把输出的文件作为目标名。
6.潜规则(不建议)
.c目标与.o目标。
查找.o目标,目标不存在,就把.o替换成.c
如果.c存在,实施潜规则:直接调用gcc把.c执为.o
7.变量
变量名=值 值
$(变量名) ${变量}
8.伪目标:
不把目标作为文件处理的目标称为伪目标
声明伪目标
没有:
1.make的配置
2.make的变量的操作函数
3.多make文件使用
二、环境变量
1.使用main的参数
int main(int args,char *argv[],char **arge)
{
}
命令行参数argv与环境行arge都是字符串数组.
约定:最后一个字符串是NULL/0
2.在C的标准库提供:外部变量
extern char **environ;
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <stdio.h> #include <unistd.h> extern char **environ; int main(/*int args,char *argv[],char*arge[]*/) { /* while(*arge) { printf("%s\n",*arge); arge++; } */ /* int i=0; while(arge[i]) { printf("%s\n",arge[i]); i++; } */ while(*environ) { printf("%s\n",*environ); environ++; } }
3.修改获取某个环境变量
getenv/setenv/unsetenv
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <stdlib.h> #include <stdio.h> main() { char *val; val=getenv("PATH"); printf("%s\n",val); }
三、IO的基础
1.认识内核对象
不允许访问内核设备和内存,但可以通过内核系统函数去访问.
对每个内核对象进行编号ID.
如果访问内核对象,只能通过ID.
编程模型:
申请得到一个ID
在内核系统函数中使用ID得到对应内核对象数据
2.怎么访问文件
使用函数,传递一个文件,系统打开文件,加载文件数据,
返回一个ID.
使用函数,传递ID,得到数据.
使用函数传递ID,告诉系统释放文件.
ID:文件描述符号.file description (fd)
每个程序执行的时候都有一个目录,存放打开的文件描述符号
3.每个程序默认打开三个文件设备:
0:标准输入
1:标准输出
2:错误输出
4.操作文件描述符号
ssize_t write(int fd,
void *buf,//要写入内核对象的数据
size_t size);//写入数据大小
返回:
>0 实际写入的数据
-1 写入错误
ssize_t read(int fd,
void *buf,//返回数据的空间
size_t size);//空间大小
返回:
>0:实际读取的数据
=0:碰到文件结束符号EOF (ctrl+d)
-1:读取错误
建议:
0:输入
1:输出
2:错误输出
课堂练习:
1.使用write向0 1 2 写数据
2.使用read从0 1 读取数据,并判定输入的情况,然后根据相应的结果输出提示
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <stdlib.h> #include <stdio.h> main() { //printf("%d\n",getpid()); //while(1); /* int r=write(0,"Hello\n",6); write(1,"world\n",6); write(2,"louis\n",6); int a=20; write(1,&a,4); */ char buf[32]; //memset(buf,0,32); bzero(buf,32); int r=read(0,buf,30); printf("实际输入:%d\n",r); if(r>0) { buf[r]=0; printf("::%s\n",buf); } if(r==0) { printf("ctrl+d\n"); } if(r==-1) { printf("输入错误!\n"); } }
三.基于文件的描述符号
1.得到文件描述符号/释放文件描述符号
a.文件类型
目录文件d
普通文件f
字符设备文件c
块设备文件b
软连接文件l
管道文件p
socket文件s
b.文件的属性
1.属性的表达方式:绝对模式(0666类似的八进制数),字符模式(rwx)
0 0 0 0
拥有者 组 其他用户
0666
2.文件的权限属性:
读
写
执行
粘附位权限
用户设置位权限
组设置位权限
0 0 0 0 0
特殊权限 Owner group 其他用户
s:
S
t
T
2.1. s设置位
2:组设置位
4:用户设置位
s对执行有效
无效的设置位使用S表示
设置位向其他用户开放拥有者权限的权限.用户设置位
设置位向其他用户开放组用户权限的权限.组用户设置位
设置位只对执行程序有意义(执行权限有意义)
2.2. t设置位
1:表示沾附位设置
t对写文件有意义
没有执行权限的沾附位使用T表示.
沾附的目的:防止有些权限的用户删除文件.
程序在执行的时候到底拥有的是执行者用户的权限
还是文件拥有者的权限.
程序执行中有两个用户:
实际用户
有效用户
总结:
沾附位的作用: 防止其他有写权限用户删除文件
设置位的作用: 向其他执行者开发组或者用户的权限.
练习:
1.使用cat创建一个文件
2.设置沾附位,并观察属性
3.设置用户设置位, 并观察属性
4.设置组设置位, 并观察属性
5.考虑w权限与沾附位的关系
6.考虑x权限与设置位的关系.
2.通过文件描述符号读写各种数据.
open函数与creat函数
int open( const char *filename,//文件名 int flags,//open的方式[创建/打开] mode_t mode//权限(只有创建的时候有效) )
返回:
>=0:内核文件描述符号.
=-1:打开/创建失败
open的方式:
必选方式:O_RDONLY O_WRONLY O_RDWR,必须选择一个
创建/打开:O_CREAT
可选方式:
对打开可选方式:O_APPEND O_TRUNC(清空数据)
对创建可选方式:O_EXCL
组合:
创建:
O_RDWR|O_CREAT
O_RDWR|O_CREAT | O_EXCL
打开:
O_RDWR
O_RDWR|O_APPEND
O_RDWR|O_TRUNC
权限:
建议使用8进制数
关闭
void close(int fd);
案例1:
创建文件
案例2:
创建文件并写入数据
20 short float
tom 20 99.99
bush 70 65.00
达内 40 100.00
注意:
文件的创建的权限受系统的权限屏蔽的影响
umask //显示屏蔽权限.
umask 0666 //设置权限屏蔽.
ulimit -a 显示所有的其他限制.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/*创建文件*/ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> main() { int fd; char name[20]; short age; float score; char sex; fd=open("test.dat", O_RDWR|O_CREAT|O_EXCL, 00666); if(fd==-1) printf("open error:%m\n"),exit(-1); //写第一条 memcpy(name,"tom",strlen("tom")+1); age=20; score=99.99; sex='F'; write(fd,name,sizeof(name)); write(fd,&age,sizeof age); write(fd,&score,sizeof(float)); write(fd,&sex,sizeof(sex)); //写第二条 memcpy(name,"Bush",strlen("Bush")+1); age=70; score=65.00; sex='M'; write(fd,name,sizeof(name)); write(fd,&age,sizeof age); write(fd,&score,sizeof(float)); write(fd,&sex,sizeof(sex)); //写第三条 memcpy(name,"达内",strlen("达内")+1); age=10; score=99.00; sex='F'; write(fd,name,sizeof(name)); write(fd,&age,sizeof age); write(fd,&score,sizeof(float)); write(fd,&sex,sizeof(sex)); close(fd); }
案例3:
打开文件读取数据
重点:
怎么打开读取
文件尾的判定
基本类型的数据读写.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> main() { char name[20]; short age; float score; char sex; int fd; int r; fd=open("test.dat",O_RDONLY); if(fd==-1) printf("open error:%m\n"),exit(-1); while(1) { r=read(fd,name,sizeof(name)); if(r==0) break; r=read(fd,&age,sizeof(short)); r=read(fd,&score,sizeof(float)); r=read(fd,&sex,sizeof(sex)); printf("%s,\t%4hd,\t%.2f,\t%1c\n", name,age,score,sex); } close(fd); }
案例4:
结构体读取
描述:从键盘读取若干条数据,保存到文件
数据追加
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> struct stu { int no; char name[20]; float score; }; /* 1.判定文件是否存在,存在打开,不存在创建 2.输入记录 3.保存记录 4.提示继续输入 5.继续/不继续 6.关闭文件 */ int openfile(const char *filename) { int fd; fd=open(filename,O_RDWR|O_CREAT|O_EXCL,0666); if(fd==-1)//表示文件存在,则打开 { fd=open(filename,O_RDWR|O_APPEND); return fd; } return fd; } void input(struct stu *record) { bzero(record,sizeof(struct stu)); printf("输入学号:"); scanf("%d",&(record->no)); printf("输入姓名:"); scanf("%s",record->name); printf("输入成绩:"); scanf("%f",&(record->score)); } void save(int fd,struct stu *record) { write(fd,record,sizeof(struct stu)); } int iscontinue() { char c; printf("是否继续输入:\n"); //fflush(stdin); //fflush(stdout); scanf("\n%c",&c); if(c=='Y' || c=='y') { return 1; } return 0; } int main() { int fd; int r; struct stu s={0}; fd=openfile("stu.dat"); if(fd==-1) printf("openfile:%m\n"),exit(-1); while(1) { input(&s); save(fd,&s); r=iscontinue(); if(r==0) break; system("clear"); } close(fd); printf("输入完毕!\n"); }
3.文件描述符号与重定向
1.判定文件描述符号与终端的邦定关系
int isatty(int fd)
返回非0:fd输出终端
0:fd输出被重定向
2.防止重定向
/dev/tty
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> int main() { int fd; printf("Hello\n"); write(1,"World\n",6); fd=open("/dev/tty",O_WRONLY); if(isatty(1)) { write(1,"notredir\n",9); } else { write(1,"redir\n",6); } write(fd,"Killer\n",7); }
总结:
1.make的多目标依赖规则以及伪目标
2.文件的创建与打开(了解设置位的作用)
3.文件的读写(字符串/基本类型/结构体)
4.了解描述符号与重定向
作业:
1.完成上课的练习.
2.写一个程序使用结构体读取1种的数据,
并全部打印数据,
并打印平均成绩
3.写一个程序:
查询1种的数据.比如:输入姓名,查询成绩
4.写一个程序,录入保存如下数据:
书名 出版社 价格 存储量 作者
5.写一个程序负责文件拷贝
main 存在的文件 新的文件名
要求:
文件存在就拷贝,不存在提示错误.