命名空间的定义必须定义在全局范围
命名空间下可以存放变量、函数、结构体、类
命名空间可以重名,重名命名空间相当于合并操作
命名空间可以嵌套命名空间
无名命名空间,意味着命名空间中的都是静态的,作用域为整个文件
命名空间是可以取别名的
namespace newA=A;全局变量的检测增强
c++编译器对于全局变量的声明和定义,检测会增强
c语言下的全局变量的声明和定义
int a;//定义 int a;//声明 int a;//声明
//编译不通过 int a; int a; int a;c++中所有变量和函数都必须有类型
void foo(x,y){
return 100;
}
void test01(){
foo(1);
foo(1,2);
foo(1,2,3);
}
更严格的类型转换
在c++中,不同的变量一般是不能直接赋值的,需要相应的强转
struct类型的增强在c++中使用结构体类型,可以不写struct关键字
三目运算符//编译出错 (a(a const修饰的变量在c语言中const修饰的局部变量保存在栈区
void test01(){ const int a= 10; int *p = &a; *p = 100; printf("a=%d",a);//输出a=100 } const int b=10;//保存在常量区,无法修改 void test02(){ int *p = &b; *p = 100; printf("b=%d",b);//出错 }void test011(){ const int a=10; //对const 修饰的局部变量取地址,编译器会产生一个临时变量保存a的地址 int *p = (int*)&a; *p = 100; cout<内联函数1、为什么要有内联函数
第一个在c中也会出现,宏看起来像一个函数调用,但是会有隐藏一些难以发现的错误
第二个问题是c++特有的,预处理器不允许访问类的成员,也就是说预处理器宏不能用作类的成员函数
宏函数和内联函数的区别内联函数和宏函数一样,省去编译的开销
#define MYADD(a,b) a+b inline int myadd(int a,int b) { return a+b; } void test01(){ int a=10; int b=20; //int c=MYADD(a,b)*5;//a+b*5 替换发生在预处理阶段 int c=myadd(a,b)*5;//替换发生在编译阶段 cout<类的成员函数默认默认编译器会将它做成内联函数 内联函数与编译器 以下情况编译器可能考虑不会将函数进行内联编译:
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数进行取址操作
函数的重载函数的重载:
在c++中函数的名字是可以重名的,也就是可以有多个相同的函数存在
实现函数重载的条件:
同一作用域
参数个数不同
参数类型不同
参数顺序不同
编译器编译会修改函数的函数名
#if __cpluspus extern "C " { #endif int myadd(int a,int b); #if __cplusplus } #endif结构体struct student { int age; int id; char name[256]; void print(){ cout<对象的构造与析构class Person{ public: int age; string name; Person(){ } Person(int age,string name){ this.age=age; this.name=name; } } test(){ Person(1,"lucy");//匿名对象 Person(); Persong(p1);//匿名对象不能使用括号调用拷贝构造 } //隐式调用构造函数 void test1(){ Person p={1,"lucy"}; Person p2 = p1;//隐式调用拷贝构造 }调用时机1、旧对象初始化新对象
2、传参
3、返回对象
析构顺序class Phone{ public: Phone(string pho_name){ this.pho_name=pho_name; cout<<"Phone的构造"<构造先调用里面,析构先析构外面
explicit关键字c++提供了关键字explicit,禁止通过构造函数进行的隐式转换,声明为explicit的构造函数不能在隐式转换中使用。
友元class Building{ friend void print_Building(Building &b); public: Building(string hall,string bedroom){ this.bedroom = bedroom; this.hall = hall; } string hall; private: string bedroom; }; void print_Building(Building &b){ cout<类成为另一个类的友元
class Building; class Goodgay { public: Goodgay(string hall,string bedroom); void visit(); Building *b; } class Building { friend void Goodgay::visit(); public: Building(string hall,string bedroom){ this->bedroom = bedroom; this->hall = hall; } string hall; private: string bedroom; }; Goodgay::Goodgay(string hall,string bedroom){ b = new Building(hall,bedroom); } void Goodgay::visit(){ cout<hall<<" "< bedroom< 成员变量和函数的存储 class person{ public: int a;//普通的成员变量 static int b;//静态成员不存在类的实例化的对象中 void show(){//普通成员函数不存在类实例化的对象中 cout<makefile makefile语法规则目标:依赖文件列表命令列表 1、目标:
通常是要产生的文件名称,目标可以是可执行文件或其他obj文件,也可是一个动作的名称
2、依赖文件:
是用来输入从而产生目标的文件
一个目标通常有几个依赖文件(可以没有)
3、命令:
make执行的动作,一个规则可以含几个命令(可以没有)
有多个命令时,每个命令占一行
简单的makefile实例
##main.c #include#include"main.h> int main(void){ printf("hello make worldn"); printf("PI=%lfn",PI); return 0; } ## main.c #define PI 3.1415926main:main.c main.h gcc main.c -o main clean: rm mainmake命令格式make [-f file] [targets] [-f file]: make默认在工作目录中虚招名问GUNmakefile、makefile、Makefile的文件作为makefile输入文件 -f可以指定以上名字意外的文件作为makefile输入文件 2.[targets]: 若使用make命令时没有指定目标,则make工具默认会实现makefile文件内的第一个目标,然后退出 制定了make工具要实现的目标,目标可以是一个或多个(多个目标间用空格隔开)。假想目标前面makefile中出现的文件称之为将向目标
假想目标并不是一个真正的文件名,通常是一个目标集合或者动作
可以没有依赖或者命令
一般需要显示的使用make + 名字 显示调用
all exec1 exec2
clean:
rm *.o exec
make clean
makefile变量makefile 变量类似C语言中的宏,当makefile被make工具解析时,其中的变量会被展开
变量的作用:
保存文件名列表
保存文件目录列表
保存编译器名
保存编译器参数
保存表一起的输出
。。。
makefile的变量分类:1、自定义变量
在makefile文件中定义的变量
2、系统环境变量
make工具解析makefile前,读取系统发环境变量并设置为makefile的变量
3、预定义变量(自动变量)
自定义变量语法定义变量: 变量名=变量值 引用变量: $(变量名)或${变量名} makefile的变量名: makefile的变量名可以以数字开头注意:
变量是大小写敏感的
变量一般都是makefile的头部定义
变量几乎可在makefile的任何地方使用
cc=gcc #cc=arm-linux-gcc obj=main.0 printf1.0 target=main cfalgs=-Wall -g $(target):$(obj) $(cc) $(obj) -o $(target) $(cflages) main.o:main.c main.h printf1.h $(cc) -c main.c -o main.o $(cflags) printf1.o:printf1.c main.h $(cc) -c printf1.c -o printf1.o $(cflags) clean: rm $(obj) $(target)预定义变量makefile中有许多预定义变量,这些变量具有特殊的含义,可在makefile中直接使用。
$@ 目标名 $< 依赖文件列表中的第一个文件 $^ 依赖文件列表中出去重复文件的部分 AR 归档维护程序的程序名,默认值为ar ARFLAGS 归档维护程序的选项 AS 汇编程序的名称,默认值为as ASFLAGS 汇编程序的程序 CC C编译器的名称,默认值为cc CFLAGS C编译器的选项 CPP C与编译器的名称,默认值为$(CC) -E CPPFLAGS C预编译的选项 CXX c++编译器的名称,默认值为g++ CXXFLAGS c++编译器的选项CC=gcc obj=main obj1=sub obj2=sum OBJ=main.o sub.o sum.o CFLAGS=-Wall -g $(obj):$(OBJ) $(CC) $^ -o $@ $(obj).o:$(obj).c $(CC) $(CFLAGS) -c $< -o $@ $(obj1).0:$(obj1).c $(CC) $(CFLAGS) -c $< -o $@ $(obj2).o:$(obj2).c $(CC) $(CFLAGS) -c $< -o $@ clean: rm *.o $(obj) a.out -rf最精简版
CC=gcc obj=main OBJ=main.o sub.o sum.o CFALGS=-Wall -g $(obj):$(OBJ) $(CC) $^ -o $@ %*.o:%*.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm *.o $(obj) a.out -rfc 内存分配在32位系统下,每个进程的寻址范围是4G
在32位操作系统中,虚拟内存被分为两个部分,3G的用户空间和1G内核空间,其中用户空间是当前进程所私有的,内核空间,是一个系统中所有的进程所公有的
在运行程序的时候,操作系统会将虚拟内存进行分区
普通全局变量
- 堆,在动态申请内存的时候,在堆中开辟内存
- 栈,主要存放局部变量
- 静态全局区
- 未出世化的静态全局区,静态变量或全局变量,没有初始化,存在此区
- 初始化的静态全局区,全局变量、静态变量,辅国智的,存在此区
- 代码区,存放程序代码
- 文字常量区,存放常量
作用范围:
全局变量的作用范围,是程序的所有地方,在不同的文件中需要声明 extern int num;
生命周期:
程序运行的整个过程,一直存在,知道程序结束
静态全局变量作用范围:
只能在定义它的源文件中有效
生命周期:
在程序运行的整个过程中一直存在
静态的局部变量作用范围:
在它定义的函数或符合语句中有效
生命周期:
第一次调用函数的时候,开辟空间赋值,函数结束后,不是放,以后再调用函数的时候,就不在位其开辟空间,也不赋初值
外部函数普通函数,都是外部函数,函数可以再程序的任何一个文件中调用
内部函数static修饰的函数,在定义的源文件中有效
C语言编译过程 选择性编译#ifdef AAA 代码段一 #else 代码段二 #endif#ifndef AAA 代码段一 #else 代码段二 #endif //常用于防止头文件重复包含#if 表达式 程序段一 #else 程序段二 #endif;函数指针函数指针的定义
返回值类型 (*函数指针变量名)(形参列表);
int (*p)(int,int);//定义了一个函数指针变量p,p指向的函数必须有一个整型的返回值,有两个整型参数 int max(int x,int y){} int min(intx,int y){} p=max;p=min;函数指针数组
int(*p[10])(int,int);
定义了一个函数指针数组,有10个元素p[0]~p[9],每个元素都是函数指针变量,指向的函数,必须有整型的返回值,两个整型参数。
将一个函数以参数的形式传递给另一个参数,即函数回调
易混淆的指针第一组
int *a[10]
这是个指针数组,数组a中有10个整数的指针变量
int (*a)[10]
数组指针变量,它是个指针变量。它占4个字节,村地址编号。
它指向一个数组,它加1的化,指向下个数组
int **p;
这个是个指针的指针,保存指针变量的地址。
第二组:
int *f(void)
他是个函数的生命,生命的这个函数返回值为int*类型.
int (*f)(void)
f是个函数指针变量,存放函数的地址。
字符串处理函数 获取字符串的长度头文件:#include
函数定义: size_t strlen(const char*s);
字符串拷贝函数#include字符串追加函数char *strcpy(char*dest ,const char *src){ //功能:将src赋值给dest } 头文件: #include
函数定义: char* strcat(char*dest, const char *src);
字符串比较函数strcmp/strncmp
函数定义: int strcmp(const char *s1,const char *s2);
字符查找函数头文件:#include
函数定义:char *strchr(const char *s,int c);
在字符指针s直线的字符串中,找ascii为c的字符
注意,是首次匹配,如果说s指向的字符串中有多个ascii为c的字符,则找的是第一个字符:找到了返回找到的字符的地址,到不到返回null
函数定义:char *strrchr(const char *s, int c);
函数的说明:末次匹配
在s指向的字符串中,找最后一次出现的ascii为c的字符
返回值:末次匹配的字符的地址
字符串匹配函数#include
char *strstr(const char *haystack,const char *needle);
函数说明:
在haystack指向的字符串中查找needle自小那个的字符串,也是首次匹配返回值:
找到了:找到的字符串的首地址
没找到:返回null
字符串转换函数atoi/atol/atof
头文件:#include
函数的定义:int atoi(const char *nptr);
函数的功能:
将nptr指向的字符产转换成整数
字符串切割函数头文件:#include
函数定义:char* strtok(char *str,const char *delim);
函数的功能:
字符串的切割,按照delim指向的字符串中的字符,切割str指向的字符串。起始就是在str指向的字符串中发现了delim字符串中的字符,就将其编程’ ’,调用一次strtok之切割一次,切割一次之后,再去切割的时候strtok的第一个参数串null,意思是接着上次切割的位置继续切割
#include格式化字符串操作函数#include int main(int argc,char *argv[]) { char s[] = "111:22222:33:444444:55555"; char *ret; ret = strtok(s,":"); printf("ret = %sn",ret);//ret = 111 //后面所有切割时都要将strtok的第一个参数串Null ret = strtok(NULL,":"); printf("ret = %sn",ret);//ret = 22222 } int sprintf(char *buf,const char *format, …);
//输出到buf制定的内存区域
例:
char buf[20];
sprintf(buf,“%d:%d:%d”,2013,10,1);
printf(“buf=%sn”,buf);
int sscanf(const char * buf,const char *format, …);
//从buf制定的内存区域中读取信息
例: int a, b,c;
sscanf(“2013:10:1”,“%d:%d:%d”,&a,&b,&c);
printf(“%d %d %dn”,a,b,c);
constconst修饰普通局部变量,不能通过变量修改值,可以通过变量的地址修改值
const修饰指针变量的类型,不能通过指针变量修改地址里的值
const 修改指针变量,不能更改指针变量保存的地址
如果const既修饰指针变量的类型,又修斯和指针变量,则只能通过原本变量修改值
位段在结构体中,以位为单位的成员,称之为位段(位域)
struct packed_data{ unsigned int a:2; unsigned int b:6; unsigned int c:4; unsigned int d:4; unsigned int i; }data; //注意:不能对位段成员取地址共用体在进行 某些算法的时候,需要使用集中不同类型的变量存到同一段内存单元中,几个变量所使用空间相互重叠
枚举enum week{ mon, tue,wed, thu ,fri, sat, sun } int main(int argc,char *arg[]){ enum week day=mon; printf("day = %dn",day); //day = 1 day = fri; printf("day = %dn",day);//day = 4 }文件行缓冲
标准io库函数,往标准输出(屏幕)输出东西的时候是行缓冲的
所谓行缓冲就是缓冲区碰到换行符的时候才刷新缓冲区
如果不刷新缓冲区,无法对文件执行读写操作
全缓冲
标准io库函数,往普通文件读写数据的,是全缓冲的,
碰到换行符也不刷新缓冲区,即缓冲区满了,才刷新缓冲区
刷新缓冲区的情况
1、缓冲区满了,刷新缓冲区
2、认为刷新缓冲区fflush(文件指针)
3、程序正常结束会刷新缓冲区
无缓冲
在读写文件的时候通过系统调用 io(read write),对文件进行读写数据,这个时候是无缓冲的,即写数据会立马进入文件,读数据会立马进入内存
文件在磁盘上存储的时候,物理上都是用二进制来存储。标准io库函数,对文件操作的时候,不管文件的编码哥哥是(字符编码、或二进制),二十按字节对文件机型读写,即把文件看成一个字节流
文件指针C语言中有三个特殊的文件指针无需定义,在程序中可以直接使用
stdin: 标准输入 ,默认为当前终端(键盘)
stdout:标准输出,默认为当前终端(屏幕)
stderr:标准错误输出设备文件,默认为当前终端(屏幕)
打开文件fopen函数定义:
FILE *fopen(const char *path,const char *mode);
读写文件操作int fgetc(FILE *stream);
函数说明:
fgetc从stream所标示的文件中读取一个字节,将字节值返回
返回值:
以t的方式:读到文件结尾返回EOF
以b的方式:读到文件结尾,使用feof判断结尾
函数的定义:
int fputc(int c,FILE *stream)
函数的说明:
fputc将c的值写到stream所代表的文件中。
返回值:
如果输出成功,则返回输出的字节值;
如果输出失败,则返回一个EOF
EOF是在stdio.h文件中定义的符号常量,值为-1
一次读写一个字符串函数定义:
char *fgets(char *s,int size,FILE *stream);
函数的说明:
从stream所致的文件中读取字符,在读取的时候碰到换行符或这碰到文件的末尾停止读取,或者是读取了size-1个字节停止读取,在读取的内容后面会加一个 ,作为字符串的结尾
返回值:
成功返回目的数组的首地址,即s
失败返回NULL
函数的定义:
int fputs(const char *s,FILE *stream);
函数的说明:
将s指向的字符串,写道stream所代表的文件中
返回值:
成功返回写入的字节数
读文件size_f fread(void *ptr,size_t size, size_t nmemb,FILE *stream);
函数说明:
fread函数从stream所表示的文件中读取数据,一块是size个字节,共nmemb块,存放到ptr指向的内存里
返回值:
实际读取的块数。
int num; num = fread(str,100,3,fp); //从fp所代表的文件中读取内容存放到str子项的内存中,读取的字节数为,每块100个字节,3块。 //返回值num,如果督导300个字节返回值num为3,如果读到大于200小于300个字节写文件fwrite
函数定义
size_t fwrite(void *ptr ,size_t size,size_t nmemb,FILE * stream);
函数说明:
fwrite 函数将ptr指向的内存里的数据,向stream所表示的文件中写入数据,一块是size个字节,共nmemb块
返回值:
实际写入的块数
随机读写rewind、fseek函数
int fseek(FILE *stream,long offset, int whence);
//int fseek(文件类型指针,位移量,起始点)
函数功能:
移动文件流的读写位置
参数:
whence 起始位置
文件开头 SEEK_SET 0
文件当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2
系统编程 文件描述符文件描述符是非负整数。打开现存文件或新建文件时,系统(内核)会返回一个文件描述符。文件描述符用来指定一打开的文件。
#define STDIN_FILENO 0 //标准输入的文件描述符
#define STDOUT_FILENO 1 //标准输出的文件描述符
#define STDERR_FILENO 2 //标准错误的文件描述符
open函数打开一个文件
#include
#include
#include
当文件存在时使用:
int open(const char *pathname,int flag);
当文件不存在时使用:
int open(const char*pathname,int flags,mode_t mode);
参数:
pathname:文件的路径及文件名
flags: open函数的行为标志
mode :文件权限(可读、可写、可执行)的设置
函数调用出错后输出错误信息//通过全局变量errno #include//errno是一个全局变量,当函数调用失败后,可以通过errno获取错误码 void perror(const char*s); //功能:输出函数调用失败的错误信息 //参数: // s:打印错误信息的提示 消息 #includeclose函数#include #include #include #include int main(int argc,char const *argv[]){ int fd; fd=open("file.txt",O_RDONLY); if(fd==-1){ //printf("errno=%dn",errno); perror("fail to open"); return -1; } printf("fd=%dn",fd); return 0; } 关闭一个文件
#include
int close(int fd);
参数:
fd:指定文件的文件描述符
返回值:
成功:0
失败:-1
注意:一个进程最多创建1024个文件描述符
write函数#include
ssize_t write(int fd,const void *addr,size_t count);
参数:
fd:文件描述符
addr:数据首地址
count:写入数据的字节个数
返回值:
成功返回实际写入数据的字节个数
失败返回-1,可以利用perror查看原因
read函数#include
ssize_t read(int fd,void *addr,size_t count);
参数:
fd:文件描述符
addr:内存首地址
count:读取的字节个数
返回值:
成功返回实际读写到的字节个数
失败返回-1,可以利用perror区查看原因
remove库函数删除文件
#include
int remove(const char*pathname);
参数:
pathname:文件的路径
失败返回-1,可以利用perror查看原因
成功返回0
进程的创建linux环境下,创建进程的主要方法是调用以下两个函数
#include
#include
pid_t fork(void);
pid_t vfork(void);
创建一个新进程
pid_t fork(void)
功能:
fork()函数用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程成为父进程
返回值:
成功:子进程中返回0,父进程中返回子进程id。
失败:返回-1
进程挂起#include
unsigned int sleep(unsigned int sec);
功能:
进程观其指定的描述,知道指定地点实践用完或者收到型号才解除挂起
返回值:
若进程挂起到sec指定的实践则返回0,若有信号终端则返回剩余的描述
注意:
进程挂起指定的秒数后程序病不会执行,系统知识将进程切换到就绪态
进程的等待父子进程有时需要简单的简称见同步,如父进程等待子进程的结束
linux提供以下两个等待函数wait()、waitpid()
#include
#incude
pid_t wait(int *status);
功能:
等待子进程终止如果子进程终止,此函数会回收子进程的资源。
调用wait函数的进程会挂起,知道它的一个子进程退出或收到一个不能被忽略的信号使才被唤醒。
若调用进程没有子进程或它的子进程已经结束,该函数立即返回
返回值:
如果执行成功则返回子进程的进程号。
出错返回-1,失败袁勇存于errno中。
取出子进程的退出信息
WIFEXITED(status)
如果子进程使正常终止的,取出的字段值非零
WEXITSTATUS(status)
返回子进程的退出状态,退出状态保存在status变量的8~16位。在用此宏前应先用宏WIFEXITED判断子进程是否正常退出,正常退出才可以使用此宏。
注意:
此status是个wait的参数指向的整型变量。
pid_t waitpid(pid_t pid, int *status, int options)
功能:
等待子进程终止,如果子进程终止了,此函数会回收子进程的资源
返回值:
如果执行成功则返回子进程id
出错返回-1,失败原因存在与errno中
status参数中包含子进程退出时的状态信息。
options参数能进一步控制waitpid的操作:
0:
同wait,阻塞父进程,等待子进程退出。
WNOHANG:
没有任何已经结束的子进程,则立即返回
WUNTRACED:
如果子进程暂停了则此函数马上返回,并且不予以例会子进程的结束状态
返回值:
成功:
返回状态改变了的子进程的进程号;如果设置了选项WNOHANG并且pid指定的进程存在则返回0.
出错:
返回-1.当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这是errno被设置为ECHAILD.
特殊进程僵尸进程(Zombie Process)
进程已运行结束,但进程的占用的资源未被回收,这样的进程称为僵尸进程。
子进程已运行结束,父进程尾调用wait或waitpid函数回收子进程资源,子进程变为僵尸进程
孤儿进程(Orphan Process)
父进程运行结束,但子进程未运行结束
守护进程(Daemon process)
守护进程是个特殊的孤儿进程,这种进程脱离终端,在后台运行
进程的终止在linux下可以通过 以下方式结束正在运行的进程:
void exit(int value);
void _exit(int value);
exit和_exit的区别:
exit为库函数,而_exit为系统调用
exit会刷新缓冲区,但_exit不会刷新缓冲区
进程退出清理进程在推出前可以用atexit函数注册退出处理函数
#include
int atexit(void(*function)(void));
功能:注册进程正常结束前调用的函数,进程退出执行注册函数。
参数:function:进程结束前,动用函数的入口地址。一个进程可以多次调用atexit函数注册清理函数,正常结束前调用函数的顺序可注册是的顺序相反
进程的创建–vfork函数#include
#include
pid_t vfork(void)
fork和vfork函数的区别:
vfork保证子进程先运行,在它调用exec或exit之后,父进程可以被调度运行。
vfork和fork一样都创建一个子进程,但它并不将父进程的地址空间完全赋值到子进程中,因为子进程会立即调用exec或exit,于是也就不访问该地址空间。
相反,在子进程中调用exec或exit之前,它在父进程的地址空间中运行,在exec之后子进程会有自己的进程空间。
int main(int argc,char* argv[]){ pid_t pid; pid= vfork(); if(pid<0){ perror("fail to vfork"); exit(1); } if(pid == 0) { int i = 0; for(i = 0;i<5;i++){ printf("this is son processn"); sleep(1); } exit(0); }else{ while(1) { printf("this is father processn"); sleep(1); } } }进程的替换exec函数族,是由六个exec函数组成的。
1、exec函数族提供了六种在进程中启动另一个程序的方法。
2、exec哈桑农户族可以更具只从的文件名或目录名查找可执行文件
3、调用exec函数的进程并不创建新的进程,故调用exec前后,进程的进程号并不会改变,其执行的程序完全由新的程序替换,而新的程序则从其main函数开始执行
#includeint execl(const char *pathname,const char *arg0,...,null); int execlp(const char *filename,const char *arg0,...,null); int execle(const char *pathname,const char *arg0,...,null,char *const envp[]); int execv(const char *pathname,char*const argv[]); int execvp(const char *filename,char *const argv[]); int execve(const char *pathname,char *const argv[],char *const envp[]); l(list):
参数地址列表,以空指针结尾。
参数地址列表
char *arg0,char *arg1,…,char *argn,null
v(vector):
存有各参数地址的执政数组的地址。
使用时先构造一个指针数组,指针数组存各参数的地址,然后将该指针数组地址作为函数的参数。
p(path)
按path环境变量指定的目录搜索可执行文件。
以p结尾的exec函数取文件名作为参数。当指定filename作为参数时,若filename中包含/,则将其视为卢金明,并直接到指定的路径中执行程序。
e(environment)
存有环境变量字符串地址的指针数组的地址。execle和execve改变的是exec启动的函数的环境变量(新的环境变量完全由enviroment指定),其他四个唤术启动的程序则使用系统默认的环境变量。
注意:
exec函数族与一般的函数不同,exec函数族中的函数执行成功后不会返回。只有调用失败了,他们才会返回-1.失败后从源程序的调用点接着往下执行。
在平时的编程中,如果用到了exec函数族,一定要加错误判断语句。
kill函数#include
#include
int kill(pid_t pid,int signum);
功能:
给指定进程发送信号
参数:
pid:见下页
signum:信号的编号
返回值:
成功返回0,失败返回-1
pid的取值有四种情况:
pid>0:将信号传送给进程ID为pid 的进程
pid=0:将信号传送给当前进程所在进程组中的所有进程
pid=-1:将信号传送给系统内所有的进程。
pid<-1:将信号传给指定进程组的所有进程。这个进程组号等于pid的绝对值
alarm函数#include
unsigned int alarm(unsigned int seconds);
功能:
在seconds秒后,向调用进程发送一个SIGALRM信号,SIGALRM信号的默认动作是终止调用alarm函数的进程。
返回值:
若以前没有设置过定时器,或设置的定时器超市,返回0;否则返回定时器剩余的秒数,并重新设定定时器。
#includeraise函数#include int main(int argc,char *argv[]) { alarm(5); while(1){ printf("hello world"); sleep(1); } return 0; } #include
int raise(int signum);
功能:
给调用进程本身发送一个信号。
参数:
signum:信号的编号
返回值:
成功返回0,失败返回-1
abort函数#include
void abort(void)
功能:
向进程发送一个SIGABRT信号,默认情况下进程会退出
注意:
即使SIGABRT信号被加入阻塞集,一旦进程调用了abort函数,进程也还是会被终止,且在终止前会刷新缓冲区,关文件描述符。
pause函数#include
int pause(void);
功能:
将调用进程挂起直至捕捉到信号为止。这个函数通常用于判断信号是否已到。
返回值:
指导捕获到信号,pause函数才返回-1,且errno被设置成eintr
signal函数进程接收到信号后的处理方式
1、执行系统默认动作
2、忽略此信号
3、执行自定义信号处理函数
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
功能:
注册信号处理函数(不可用于SIGKILL、SIGSTOP信号),即确定收到信号后处理函数的入口地址。
参数:
signum:信号编号
handler的取值:忽略该信号:SIG_IGN
执行系统默认动作:SIG_DFL
自定义信号处理函数:信号处理函数名
返回值:
成功:返回函数地址,该地址为此信号上一次注册的信号处理函数的地址。
失败:返回SIG_ERR
#include#include #include #include void handler(int sig); int main(int argc,char const *argv[]) { #if 0 if(signal(SIGINT,SIG_DFL)==SIG_ERR) { perror("fail to signal"); exit(1); } if(signal(SIGQUIT,SIG_DFL)==SIG_ERR) { perror("fail to signal"); exit(1); } #endif #if 0 if(signal(SIGINT,SIG_IGN)==SIG_ERR) { perror("fail to signal"); exit(1); } if(signal(SIGQUIT,SIG_IGN)==SIG_ERR) { perror("fail to signal"); exit(1); } if(signal(SIGTSTP,SIG_IGN)==SIG_ERR) { perror("fail to signal"); exit(1); } #endif if 0 if(signal(SIGINT,handler)==SIG_ERR) { perror("fail to signal"); exit(1); } if(signal(SIGQUIT,handler)==SIG_ERR) { perror("fail to signal"); exit(1); } if(signal(SIGTSTP,handler)==SIG_ERR) { perror("fail to signal"); exit(1); } #endif while(1) { printf("hello worldn"); sleep(1); } return 0; } void handler(int sig){ if(sig==SIGINT) { printf("SIGINT正在处理n"); } if(sig==SIGQUIT) { printf("SIGQUIT正在处理n"); } if(sig==SIGTSTP) { printf("STGTSTP正在处理n"); } } #include可重入函数#include #include #include void *ret_handler; void hanler(int sig) { printf("*******************n"); printf("nihao biejingn"); printf("welcome to 1000phonen"); printf("*******************n"); if(signal(SIGINT,ret_handler)==SIG_ERR) { perror("fail to signal"); exit(1); } } int main(int argc,char const *argv[]) { if((ret_handler=signal(SIGINT,handler))==SIG_ERR) { perror("fail to signal"); exit(1); } while(1) { printf("hello worldn"); sleep(1); } return 0; } 可重入函数是指函数可以由多个任务并发使用,而不必担心数据错误
可重入函数就是可以被中断的函数,当前函数可以在任何时刻中断,并执行另一块代码,当执行执行完毕后,回到原本的代码可以继续运行。
编写可重入函数:
1、不适用(返回)静态的数据、全局变量(除非用信号量互斥)
2、不调用动态内存分配、释放的函数
3、不调用任何不可重入的函数(如标准I/O函数)。
注:
即使信号处理函数使用的都是可重入函数(常见的可重入函数),也要注意进入处理函数时,首先要保存errno的值,结束时,在混服原值。因为,信号处理过程中,errno值随时可能改变
信号集 信号集概述一个用户进程常常需要对多个信号进行处理。为了方便对多个信号进行处理,在linux系统中引入了信号集。
信号集是用来表示多个信号的数据类型
sigemptyset函数初始化一个空的信号集
#include
int sigemptyset(sigset_t *set);
功能:
初始化由set指向的信号集,清除其中所有的信号即初始化一个空的信号集
参数:
set:信号集表示的地址,以后操作此信号集,对set进行操作就可以了。
返回值:
成功返回0,失败返回-1。
sigfillset函数#include
int sigfillset(sigset_t *set);
功能:初始化信号集set,将信号集合设置为所有信号的集合。
参数:
信号集表示的地址,以后操作此信号集,对set进行操作就可以了。
返回值:
成功返回0,失败返回-1.
sigismember函数判断某个集合中是否有某个信号
#include
int sigismember(const sigset_t *set,int signum);
功能:
判断signum标识的信号是否在信号集合set之中。
参数:
set:信号集标识符号的地址。
signum:信号的编号
返回值:
在信号集中返回1,不在信号集中返回0
错误,返回-1.
sigaddset函数向某个集合中添加一个信号
#include
int sigaddset(sigset_t *set,int signum);
功能:
将信号signum加入到信号集合set之中。
参数:
set:信号集标识的地址。
signum:信号的编号
返回值:
成功返回0,失败返回-1.
sigdelset函数从某个信号集中删除一个信号
#include
int sigdelset(sigset_t *set,int signum);
功能:
将signum所标识信号从信号集合set中删除
参数:
set:信号集标识的地址
signum:信号的编号
返回值:
成功:返回0
失败:返回-1
#includesigprocmask函数#include int main(int argc,char*argv[]) { sigset_t set; int ret = 0; sigemptyset(&set); ret = sigismember(&set,SIGINT); if(ret == 0 ) { printf("SIGINT is not a member of sigprocmask nret=%dn",ret); } sigaddset(&set,SIGINT); sigaddset(&set,SIGQUIT); ret = sigismember(&set,SIGINT); if(ret ==1) { printf("SIGINT is a member of sigprocmask nret = %dn",ret); } return 0; } 创建一个阻塞集合
#include
int sigprocmask(int how ,const sigset_t *set ,sigset_t *oldset);
功能:
检查或修改信号阻塞集,根据how 指定的方法对进程的阻塞集合进行修改,新的信号阻塞集由set指定,而原先的信号阻塞集合由oldset保存。
参数:
how:信号阻塞集合的修改方法。
SIG_BLOCK:向信号阻塞集合中添加set信号集
SIG_UNBLOCK:从信号阻塞集合中删除set集合
SIG_SETMASK:将信号阻塞集合设置为set集合
set:要操作的信号集地址。
oldset:保存原先信号集地址。
注:若set为null,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到oldset中。
返回值:
成功:返回0
失败:返回-1
#include无名管道的创建pipe函数#include #include #include int main(int argc,char *argv[]) { sigset_t set; int i=0; sigemptyset(&set); sigaddset(&set,SIGINT); while(1) { sigprocmask(SIG_BLOCK,&set,null); for(i = 0;i<5;i++) { printf("SIGINT signal is blockedn"); sleep(1); } sigprocmask(SIG_UNBLOCK,&set,null); for(i=0;i<5;i++) { printf("SIGINT signal unblockedn"); sleep(1); } } return 0; } #include
int pipe(int filedes[2]);
功能:经由参数filedes返回连个文件描述符
参数:
filledes为int型数组的首地址,其存放了管道的文件描述符fd[0]、fd[1].
filedes[0]为读而打开,filedes[1]为写而打开管道,filedes[0]的输出是filedes[1]的输入
返回值:
成功:返回0
失败:返回-1
#include无名管道的读写规律 读写端存在,只读不写#include #include int main(int argc,char const *argv[]) { int fd_pipe[2]; if(pipe(fd_pipe)==-1) { perror("fail to pipe"); exit(1); } printf("fd_pipe[0]=%dn",fd_pipe[0]); printf("fd_pipe[1]=%dn",fd_pipe[1]); if(write(fd_pipe[1],"hello world",12)==-1) { perror("fail to write"); exit(1); } char buf[32]=""; if(read(fd_pipe[0],buf,sizeof(buf))==-1) { perror("fail to read"); exit(1); } printf("buf=[%s]",buf); return 0; } 如果管道中有数据,会正常读取数据;如果管道中没有数据,则读操作会被阻塞
读写端都存在,只写不读如果一直执行写操作,则无名管道对应的缓冲区会被写满,写满之后,write函数会被阻塞,默认无名管道的缓冲区为64k字节
只有读端如果管道中有数据,则会被读取,没有数据read函数返回0;
只有写端如果关闭读端,一旦执行写操作,就会产生一个信号SIGPIPE(管道破裂)
fcntl函数非阻塞:如果是阻塞,管道中没有数据,read会一直等待,指导有数据才会继续运行,否则一直等待,如果是非阻塞,read函数运行时,会先看一下管道中是否有数据,如果有数据,则正常运行读取数据,如果管道中没有数据,则read函数会立即返回,继续下面的代码运行
有名管道(FIFO)有名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同,其特点是:
1、半双工,数据在同意时刻只能在一个方向上流动
2、写入FIFO中的数据遵循陷入先出的规则
3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须实现越好数据的格式,如多少字节算一个消息等
4、FIFO在文件系统中作为一个特殊的文件而存在并且在文件系统中可见。但FIFO中的内容却存放在内存中。
5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同
6、从FIFO读数据是一次性操作,数据一旦被堵,他就从FIFO之中被抛弃,释放空间以便写更过的数据。
7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。
8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信。
有名管道的创建方法一: mkfifo 文件名方法二: int mkfifo(const char *pathname,mode_t mode); 功能:创建一个有名管道,产生一个本地系统可见的文件pathname 参数: pathname:有名管道创建后生成的文件 mode:管道文件的权限,一般通过八进制数进行设置即可#include线程等待int main(int argc,char const *argv[]) { if(mkfifo("fifo_file",0664)==-1) { if(errno != EEXIST){ perror("fail to mkfifo"); exit(1); } } return 0; } #include
int pthread_join(pthread_t thread,void**retval);
功能:等待子线程结束,并回收子线程
参数:thread:被等待的线程号
retval:用来存储线程退出状态的指针的地址
返回值:
成功返回0,失败返回非0
线程分离linux线程执行和windows不同,pthread有两种状态:
可结合的或者分离的,线程默认创建为结合态。
如果线程时结合态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8k多)。只有当调用pthread_join之后这些资源才会被释放。
若是detached状态的线程,这些资源在线程函数退出或者pthread_exit时会自动被释放,使用pthread_detach函数将线程设置为分离态。
创建一个线程后应回收其资源,但使用pthread_join函数会使调用者阻塞,故Linux提供线程分离函数
#inlcude
int pthread_detach(pthread_t thread);
功能:
使调用线程与当前进程分离,使其成为一个独立的线程,该线程终止时,系统将自动回收它的资源。
参数:
thread:线程号
返回值:
成功:返回0,失败返回非0.
计算机网络 socketsocket分类
SOCK_STREAM,流式套接字,用于TCP
SOCK_DGRAM,数据报套接字,用于UDP
SOCK_RAW,原始套接字,对于其他的协议操作时使用
文件描述符的赋值dup和dup2是连个非常常用的系统调用,都是用来复制一个文件的描述符,是新的文件描述符也标识旧的文件描述符所标识的文件。
int dup(int oldfd);
int dup2(int oldfd,int newfd);
dup和dup2经常用来重定向进程的stdin、stdout和stderr.
dup函数#include
int dup(int old);
功能:赋值oldfd文件描述符,并分配一个新的文件描述符,新的文件描述符是调用进程文件描述符中最小可用的文件描述符。
参数:用复制的文件描述符oldfd;
返回值:
成功:新文件描述符
失败:返回-1,错误代码存于errno中
dup2函数#include
功能:
赋值一根打开的文件描述符oldfd,并分配新的文件描述符nwefd,newfd也标识oldfd所标识的文件。
注意:
newfd是小于文件描述符最大允许值的非负证书,如果newfd是一个已经打开的文件描述符,则首先关闭该文件,然后再复制
参数:
要复制的文件描述符oldfd
分配的新文件描述符newfd
返回值:
成功:返回newfd
失败:返回-1,错误代码存在于errno中
有名管道的创建方法一:用shell命令mkfifo创建有名管道
mkfifo 文件名
方法二:使用mkfifo函数
#include
#include
int mkfifo(const char *pathname,mode_t mode);
功能:创建一个有名管道,产生一个本地文件系统可见的文件pathname
参数:panthname:有名管道创建后生成的文件,可以带路径
mode:管道文件的权限
成功:返回0
失败:返回-1
#incude有名管道的基本读写操作#include #include #include #include #include int main(int argc,char const *argv[]) { if(mkfifo("fifo_file",0664)==-1) { perror("fail to mkfifo"); exit(1); } return 0; } 由于有名管道在本地创建了一个管道文件,所以系统调用的io函数基本都可以对有名管道进行操作,但是不能使用lseek修改文件的偏移量
注意:有名管道创建的文件知识起到标识的作用,真正有名管道实现进程间通信还是在内核空间开辟内存,所以本地产生的文件知识一个标识。
#incude有名管道的读写规律(阻塞) 读写端都存在,只写不读 在一个进程中,只有读端 线程的创建#include #include #include #include #include int main(int argc,char const *argv[]) { if(mkfifo("fifo_file",0664)==-1) { if(errono != EEXIST) { perror("fail to mkfifo"); exit(1); } } int fd; fd = open("fifo_file",O_RDWR); if(fd==-1) { perror("fail to open"); exit(1); } //通过write函数向管道中写入数据 if(write(fd,"hello world",strlen("hello world"))==-1) { perror("fail to write"); exit(1); } char buf[32]=""; if(read(fd,buf,sizeof(buf))==-1) { perror("fail to read"); exit(1); } printf("buf=[%s]n",buf); return 0; } #include
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,
void * (* start_routine)(void *),void *arg);
功能:创建一个线程
参数:
thread:线程标识符地址。
attr:线程属性结构体地址。
start_routine:线程函数的入口地址
arg:传给线程函数的参数
返回值:
成功:返回0
失败:返回非0
与fork不同的是pthread_create创建吸纳成不与父线程在同一点开始运行,二十从指定的函数开始运行,该函数运行完后,该线程也就退出了。
线程依赖进程存在的,如果创建线程的进程结束了,线程也就结束了。
线程函数的程序在pthread库中,故链接时要加上参数-lpthread
UDP编程流程服务器:
创建套接字
将服务器的ip地址、端口与套接字进行绑定
接受数据
发送数据
客户端:
创建套接字
发送数据
接受数据
关闭套接字
UDP-创建套接字int socket(int family,int type,int protocol);
功能:
创建一个用于网络通信的socket套接字
参数:
family:协议族(AF_INET、AF_INET6、PF_PACKET等)
type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAM等)
protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等)
返回值:
套接字
特点:
创建套接字时,系统不会分配端口
创建的套接字默认属性时主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的
头文件:
#include
#includeUDP-发送、绑定、接收数据 IPv4套接字地址结构#include #include #include int main(int argc,char const *argv[]) { int sockfd; if(sockfd=socket(AF_INET,SOCK_DGRAM,0)==-1){ perror("fail to socket"); exit(1); } printf("sockfd= %dn",sockfd) return 0; } 头文件:#include
struct in_addr { in_addr_t addr;//ip地址 4字节 }; struct sockaddr_in { sa_family_t sin_family;//协议族 2字节 in_port_t sin_port;//端口号 2字节 struct in_addr sin_addr;//ip地址 4字节 char sin_zero[8];//填充,不起任何作用 };为了使不同格式地址能被传入套接字函数,地址需要强制转化成套接字地址结构
通用结构体sockaddr
#include
struct sockaddr { sa_family sa_family;//2字节 char sa_data[14]; }两种地址使用场合在定义源地址和目的地址结构的时候,选用struct sockaddr_in
当调用编程接口函数,且该函数需要传入地址结构时需要用struct sockaddr进行强制转换
bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));发送数据-sendtossize_t sendto(int sockfd,const void *buff,size_t nbytes,int flags,const struct sockaddr *to, socklen_t addrlen);
功能:
向to结构指针中指定的ip,发送UDP数据
参数:
sockfd:套接字
buf: 发送数据缓冲区
nbytes:发送数据缓冲区的大小
flags:一般为0
to:子痫目的主机地址结构体的指针
addrlen: to所指向的内容的长度
注意:
通过to和addrlen确定目的地址
可以发送0长度的UDP数据包
#includebind#include //socket #include #include //exit #include //sockaddr_in #include//htons inet_addr #include //close #include int main(int argc,char const *argv[]) { int sockfd; if(sockfd=socket(AF_INET,SOCK_DGRAM,0)==-1){ perror("fail to socket"); exit(1); } printf("sockfd= %dn",sockfd); //填充服务其网络信息结构体sockaddr_in struct sockaddr_in serveraddr; socklen_t addrlen = sizeof(serveraddr); serveraddr.sin_family = AF_INET;//协议族,AF_INET:ipv4网络协议 serveraddr.sin_addr.s_addr=inet_addr("192.168.3.78");//ip地址 serveraddr.sin_port = htons(8080); //发送数据 char buf[128]=""; while(1) { fgets(buf,128,stdin); buf[strlen(buf)-1]=' ';//把buf字符串中的n转化为 if(sendto(sockfd,buf,N,0,(struct sockaddr*)&serveraddr,addrlen)==-1){ perror("fail to sendto"); exit(1); } } close(sockfd); return 0; } #include接收数据-recvfromint bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen); //功能:将套接字与网络信息结构体绑定 //参数: //sockfd:文件描述符,socket的返回值 //addr:网络信息结构体 //通用结构体(一般不用) //struct aockaddr // 网络信息结构体 sockaddr_in // #include // struct sockaddr_in // struct sockaddr_in // addrlen: addr的长度 // 返回值: // 成功:0 // 失败:-1 ssize_t recvfrom(int sockfd,void *buf,size_t nbytes, int flags,struct sockaddr *from,socklen_t *addrlen);
功能:
接收UDP数据,并将源地址信息保存在from指向的结构体中
参数:
sockfd:套接字
buf:接收数据缓冲区
nbytes:接收数据缓冲区的大小
flags:套接字标志(常为0)
from:源地址结构体指针,用来保存数据来源
addrlen:from所指内容的长度
注意:
通过from和addrlen参数存放数据来源信息
from和addrlen可以为NULL,表示不保存数据来源
返回值:
成功:接收到的字符数
失败:-1



