在linux环境下基于TCP/IP架构实现文件传输——3、知识点总结

在linux环境下基于TCP/IP架构实现文件传输——知识点总结

引言

以下是笔者在做基于TCP/IP架构实现文件传输时用到的知识点总结

1. memset

函数介绍
void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节(typedef unsigned int size_t)用ch替换并返回s。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。
memset()函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度.

2. URL格式

在WWW上,每一信息资源都有统一的且在网上唯一的地址,该地址就叫URL(Uniform Resource Locator,统一资源定位器),它是WWW的统一资源定位标志,就是指网络地址。

3.基于linux系统的CS模型实现

CS模型
在这里插入图片描述

4.Vi/Vim

VI中复制与粘贴基本方法: Shift+Insert

5. gcc

  1. 生成可执行程序
    最简单的生成可执行文件的写法为:
    $ cd demo #进入源文件所在的目录
    $ gcc main.c #在 gcc 命令后面紧跟源文件名
    打开 demo 目录,会看到多了一个名为 a.out 的文件,这就是最终生成的可执行文件
    如果不想使用默认的文件名,那么可以通过-o选项来自定义文件名,例如:
    $ gcc main.c -o main.out
    这样生成的可执行程序的名字就是main.out。
    因为Linux下可执行文件的后缀仅仅是一种形式上的,所以可执行文件也可以不带后缀,例如:
    $ gcc main.c -o main
    这样生成的可执行程序的名字就是main。
    通过-o选项也可以将可执行文件输出到其他目录,并不一定非得在当前目录下,例如:
    $ gcc main.c -o ./out/main.out
    或者
    $ gcc main.c -o out/main.out
    表示将可执行文件输出到当前目录下的out目录,并命名为main.out。./表示当前目录,如果不写,默认也是当前目录。
    注意:out 目录必须存在,如果不存在,gcc 命令不会自动创建,而是抛出一个错误。
  2. 运行可执行程序
    上面我们生成了可执行程序,那么该如何运行它呢?很简单,在控制台中输入程序的名字就可以,如下所示:
    $ ./a.out
    ./表示当前目录,整条命令的意思是运行当前目录下的 a.out 程序。如果不写./,Linux 会到系统路径下查找 a.out,而系统路径下显然不存在这个程序,所以会运行失败。
    所谓系统路径,就是环境变量指定的路径,我们可以通过修改环境变量添加自己的路径,或者删除某个路径。很多时候,一条Linux命令对应一个可执行程序,如果执行命令时没有指明路径,那么就会到系统路径下查找对应的程序。

6. ctrl-c、ctrl-z 、ctrl+d

一、ctrl-c
  发送SIGINT信号(程序终止(interrupt)信号)给前台进程组中的所有进程。
  常用于终止正在运行的程序。
二、ctrl-z
  发送SIGTSTP信号(停止进程的运行,但该信号可以被处理和忽略)
  给前台进程组中的所有进程,常用于挂起一个进程。
  如果需要恢复到前台输入fg,恢复到后台输入bg
三、ctrl+d
  不是发送信号,而是表示一个特殊的二进制值,表示EOF。
  EOF是一个计算机术语,为End Of File的缩写,通常在文本的最后存在此字符表示资料结束。

7. 指针的强制转换

1、指针类型强制转换:
int m;
int *pm = &m;
char *cp = (char *)&m;
pm指向一个整型,cp指向整型数的第一个字节
综上所述: 指针强制转换仅仅是将地址后的内容按照不同的类型进行变量解释和读取。(但是如果强制转换的指针所指向数据的字节数不一样,会存在数据错误。比如:一个指向char型数据的指针p,p指向的内容只有一个字节,p是将地址后面的一个字节的内容按照char进行解读取;但是如果将其强制转换成int型指针pn,*pn将地址后面的四个字节的内容按照int进行解读取,这样就会多读了后面的内存,出现错误。

8.Linux下的socket()函数(文件传输助手)

在 Linux 下使用 <sys/socket.h> 头文件中 socket() 函数来创建套接字,原型为:
int socket(int af, int type, int protocol);

  1. af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
    大家需要记住127.0.0.1,它是一个特殊IP地址,表示本机地址,后面的教程会经常用到。 你也可以使用 PF 前缀,PF 是“Protocol Family”的简写,它和 AF 是一样的。例如,PF_INET 等价于 AF_INET,PF_INET6 等价于 AF_INET6。
  2. type为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字)和SOCK_DGRAM(数据报套接字/无连接的套接字),我们已经在《套接字有哪些类型》一节中进行了介绍。
  3. protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
    有了地址类型和数据传输方式,还不足以决定采用哪种协议吗?为什么还需要第三个参数呢?
    正如大家所想,一般情况下有了 af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到这样的情况:有两种不同的协议支持同一种地址类型和数据传输类型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。
    本教程使用 IPv4 地址,参数 af 的值为 PF_INET。如果使用 SOCK_STREAM 传输数据,那么满足这两个条件的协议只有 TCP,因此可以这样来调用 socket() 函数:
    Int tcp_socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //IPPROTO_TCP表示TCP协议
    这种套接字称为 TCP 套接字。
    如果使用 SOCK_DGRAM 传输方式,那么满足这两个条件的协议只有 UDP,因此可以这样来调用 socket() 函数:
    int udp_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); //IPPROTO_UDP表示UDP协议
    这种套接字称为 UDP 套接字。
    上面两种情况都只有一种协议满足条件,可以将 protocol 的值设为 0,系统会自动推演出应该使用什么协议,如下所示:
    int tcp_socket = socket(AF_INET,SOCK_STREAM,0); //创建TCP套接字
    int udp_socket = socket(AF_INET,SOCK_DGRAM,0); //创建UDP套接字
    后面的教程中多采用这种简化写法。

1.TCP编程的一般步骤

TCP编程的服务器端一般步骤是: 1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt(); 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();   
4、开启监听,用函数listen();   
5、接收客户端上来的连接,用函数accept();   
6、收发数据,用函数send()和recv(),或者read()和write();   
7、关闭网络连接;   
8、关闭监听;
TCP编程的客户端一般步骤是:   
1、创建一个socket,用函数socket();   
2、设置socket属性,用函数setsockopt();
可选   
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选   
4、设置要连接的对方的IP地址和端口等属性;   
5、连接服务器,用函数connect();   
6、收发数据,用函数send()和recv(),或者read()和write();   
7、关闭网络连接;
先创建socket,然后绑定这个地址端口,在对这个地址进行监听,此时客户端若发送一个连接请求,服务器接收成功后从连接队列里面取出一个已经连接好的socket,返回这个socket的文件描述符,此时这个socket专门用于数据通信。

2.Linux下socket设置为非阻塞方式和fcntl系统调用

用以下方法将socket设置为非阻塞方式
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
用以下方法将socket设置为非阻塞方式
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);

将非阻塞的设置回阻塞可以用
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags & ~O_NONBLOCK);
功能描述:根据文件描述词来操作文件的特性。
文件控制函数
fcntl – file control
头文件:
#include <fcntl.h>;
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
[描述]
Fcntl()针对(文件)描述符提供控制.参数fd是被参数cmd操作(如下面的描述)的描述符.
针对cmd的值,fcntl能够接受第三个参数int arg
fcntl函数有5种功能:
1.复制一个现有的描述符(cmd=F_DUPFD).
2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
cmd值:
F_DUPFD 返回一个如下描述的(文件)描述符:
o 最小的大于或等于arg的一个可用的描述符
o 与原始操作符一样的某对象的引用
o 如果对象是文件(file)的话,返回一个新的描述符,这个描述符与arg共享相同的偏移量(offset)
o 相同的访问模式(读,写或读/写)
o 相同的文件状态标志(如:两个文件描述符共享相同的状态标志)
o 与新的文件描述符结合在一起的close-on-exec标志被设置成交叉式访问execve(2)的系统调用
F_GETFD 取得与文件描述符fd联合close-on-exec标志,类似FD_CLOEXEC.如果返回值和FD_CLOEXEC进行与运算结果是0的话,文件保持交叉式访问exec(),否则如果通过exec运行的话,文件将被关闭(arg被忽略)
F_SETFD 设置close-on-exec旗标。该旗标以参数arg的FD_CLOEXEC位决定。 F_GETFL 取得fd的文件状态标志,如同下面的描述一样(arg被忽略)
F_SETFL 设置给arg描述符状态标志,可以更改的几个标志是: O_APPEND, O_NONBLOCK,O_SYNC和O_ASYNC。 F_GETOWN 取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回成负值(arg被忽略)
F_SETOWN 设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明,否则,arg将被认为是进程id 命令字(cmd)F_GETFL和F_SETFL的标志如下面的描述:
O_NONBLOCK 非阻塞I/O;如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,read或write调用返回-1和EAGAIN错误
O_APPEND 强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
O_DIRECT 最小化或去掉reading和writing的缓存影响.系统将企图避免缓存你的读或写的数据.如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响.如果这个标志用的不够好,将大大的降低性能
O_ASYNC 当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候
在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希望修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。
fcntl的返回值 与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD,F_GETFD,F_GETFL以及F_GETOWN。第一个返回新
的文件描述符,第二个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID。

3.int main(int argc,char *argv[])

在main()函数中允许带2个参数,一个为整型argc,另一个是指向字符型的指针数组argv[]。格式:int main(int argc,char *argv[])
其中整型argc表示命令行中字符串的个数,指针数组argv[]指向命令行中的各个字符串。这两个参数可以用任何合法的标识符命名,但是习惯采用argc和argv。带参数的main()函数一般能在调用其时追加参数,如DOS命令一样。

4. htons函数

uint16_t htons(uint16_t hostshort);
htons的功能:将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)
参数u_short hostshort: 16位无符号整数
返回值:TCP / IP网络字节顺序
htons 是把你机器上的整数转换成“网络字节序”, 网络字节序是 big-endian,也就是整数的高位字节存放在内存的低地址处。 而我们常用的 x86 CPU (intel, AMD) 电脑是 little-endian,也就是整数的低位字节放在内存的低字节处。举个例子吧。假定你的port是0x1234,在网络字节序里 这个port放到内存中就应该显示成addr addr+1,也就是:0x12 0x34;而在x86电脑上,0x1234放到内存中实际是:addr addr+1,也就是:0x34 0x12。htons 的用处就是把实际内存中的整数存放方式调整成“网络字节序”的方式。
第一个问题:为什么使用两个字节,也就是16位来存储。
这个简单一些,因为一个字节只能存储8位2进制数,而计算机的端口数量是65536个,也就是2^16,两个字节。
第二个为题:为什么计算机需要大端模式和小端模式?
小端模式 :强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样。
大端模式 :符号位的判定固定为第一个字节,容易判断正负。

5.bind函数

函数原型
bind函数把一个本地协议地址赋予一个套接字.
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr addr, socklen_t addrlen);
成功 : 返回0.
失败 : 返回-1.
6.accept函数
#include <sys/types.h> /
See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockdf:
socket文件描述符
addr:
传出参数,返回链接客户端地址信息,含IP地址和端口号
addrlen:
传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小
返回值:
成功返回一个新的socket文件描述符,用于和客户端通信,失败返回-1,设置errno.

6.accept()函数

过程1:TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。
过程2:
1.有人从很远很远的地方尝试调用connect()来连接你的机器上的某个端口(当然是你已经在listen()的)。
2.他的连接将被listen 加入等待队列等待accept()函数的调用(加入等待队列的最多数目由调用listen()函数的第二个参数backlog 来决定)。
3.你调用accept()函数,告诉他你准备连接。accept函数将回返回一个新的套接字描述符,这个描述符就代表了这个连接!
1)原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
2)参数:
sockfd :为服务器的socket描述字;
addr:存储着远程连接过来的计算机的信息(比如远程计算机的IP 地址和端口)。用于返回客户端的协议地址。
addrlen:sizeof(struct sockaddr_in)
3)返回值:
如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
4)注意:
1.accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字.
2.accept函数返回的是已连接的socket描述字.
3.一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。

7. write()

函数定义:ssize_t write (int fd, const void * buf, size_t count);
函数说明:write()会把参数buf所指的内存写入count个字节到参数放到所指的文件内。
返回值:如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。

8. fread()

size_t fread(void *ptr, size_t size, size_t nmemb, FILE stream) 从给定流 stream 读取数据到 ptr 所指向的数组中。
参数
ptr – 这是指向带有最小尺寸 size
nmemb 字节的内存块的指针。
size – 这是要读取的每个元素的大小,以字节为单位。
nmemb – 这是元素的个数,每个元素的大小为 size 字节。
stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
返回值
成功读取的元素总数会以 size_t 对象返回,size_t 对象是一个整型数据类型。如果总数与 nmemb 参数不同,则可能发生了一个错误或者到达了文件末尾。
fread读取文件时,每读完一次,指针便自动偏移一次。

9.ISEEK函数

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能
调整读写的位置,就像在纸上写字时,挪动笔尖所指位置是一样的。C库的标准io函数里面有一个fseek函数,也是用于调整读写位置的,fseek就是对lseek系统函数封装后实现的,后面讲到标准io时,还会讲到fseek函数。
返回值
调用成功
返回当前读写位置相对于文件开始位置的偏移量(字节)。
可以使用lseek函数获取文件的大小,将文件读写的位置移动到最末尾,然后获取返回值,这个返回值就是文件头与文件尾之间的字节数,也就是文件大小。
调用失败
返回-1,并给errno设置错误号。

10. fseek重定位文件指针

用法:int fseek( FILE *stram, long offset ,int position);
描述:该函数用来设置文件指针stream的位置。如果执行成功,stream 将以position 为基准,偏移offset 个字节的位置。如果执行失败,则不改变指针的位置。
position 用来设置从文件的哪个位置开始偏移,
SEEK_SET 从文件开头位置
SEEK_CUR 从文件当前位置
SEEK_END 从文件末尾位置
函数:long ftell(FILE *stream);
功能: 返回文件当前指针位置,
函数功能:使用fseek函数后在调用函数ftell就能很容易确定函数当前位置

11. pthread_create

总述:pthread_create是(Unix、Linux、Mac OS X)等操作系统的创建线程的函数。它的功能是创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数。
pthread_create的返回值表示成功,返回0;表示出错,返回表示-1。
函数原型声明:#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, //新创建的线程ID指向的内存单元。
const pthread_attr_t *restrict attr, //线程属性,默认为NULL
void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行
void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。
);

12. –pthread命令

在使用多线程函数时,终端输入如下:
gcc demo.c -o server.out –pthread

13. pthread_self

头文件
#include <pthread.h>
函数原型
pthread_t pthread_self(void);
函数作用:获得线程自身的ID。pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则将产生奇怪的结果。
功能
获取当前调用线程的 thread identifier(标识号).

14. recv函数

int recv( SOCKET s, char FAR *buf, int len, int flags );
不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。
(1)第一个参数指定接收端套接字描述符;
(2)第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
(3)第三个参数指明buf的长度;
(4)第四个参数一般置0。

15. send函数

int send( SOCKET s, const char FAR *buf, int len, int flags );
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。
客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
(1)第一个参数指定发送端套接字描述符;
(2)第二个参数指明一个存放应用程序要发送数据的缓冲区;
(3)第三个参数指明实际要发送的数据的字节数;
(4)第四个参数一般置0。

9. bzero和memset函数

bzero函数
函数原型:void bzero(void *s, int n);
头文件:#include <string.h>
功能:将字符串s的前n个字节置为0,一般来说n通常取sizeof(s),将整块空间清零。
返回值:无返回值
memset函数
函数原型:void *memset(void *s,int c,size_t n);
头文件:#include <string.h> 或者#include <memory.h>
说明:将s中前n个字节替换为c并返回s
作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法
这里我们看到c是int类型的,需要注意两点:
1)若s指向的地址是char类型的,c的值可以是任意字符值;
2)若s指向的地址不是char类型,c的值只能是-1或者0。
因为-1和0转化成二进制后每一位都是一样的,设int型占4个字节,则-1=0XFFFFFFFF, 0=0X00000000。

10. open()函数

#include <fcntl.h>
int open(const char pathname, int oflag, … / mode_t mode */);
open函数用来打开或创建一个文件,若成功返回文件描述符,否则返回-1。
pathname是要打开或创建文件的名字。
参数mode 则有下列数种组合, 只有在建立新文件时才会生效, 此外真正建文件时的权限会受到umask 值所影响, 因此该文件权限应该为 (mode-umaks).
S_IRWXU00700 权限, 代表该文件所有者具有可读、可写及可执行的权限.
S_IRUSR 或S_IREAD, 00400 权限, 代表该文件所有者具有可读取的权限.
S_IWUSR 或S_IWRITE, 00200 权限, 代表该文件所有者具有可写入的权限.
S_IXUSR 或S_IEXEC, 00100 权限, 代表该文件所有者具有可执行的权限.
S_IRWXG 00070 权限, 代表该文件用户组具有可读、可写及可执行的权限.
S_IRGRP 00040 权限, 代表该文件用户组具有可读的权限.
S_IWGRP 00020 权限, 代表该文件用户组具有可写入的权限.
S_IXGRP 00010 权限, 代表该文件用户组具有可执行的权限.
S_IRWXO 00007 权限, 代表其他用户具有可读、可写及可执行的权限.
S_IROTH 00004 权限, 代表其他用户具有可读的权限
S_IWOTH 00002 权限, 代表其他用户具有可写入的权限.
S_IXOTH 00001 权限, 代表其他用户具有可执行的权限。

11. snprintf()

snprintf(),函数原型为int snprintf(char *str, size_t size, const char *format, …)
将可变参数 “…” 按照format的格式格式化为字符串,然后再将其拷贝至str中。

12. fscanf

fscanf 函数原型为int fscanf(FILE * stream, const char * format, [argument…]); 其功能为根据数据格式(format),从输入流(stream)中读入数据,存储到argument中,遇到空格和换行时结束。fscanf位于C标准库头文件<stdio.h>中

13. strchr和strrchr函数

strchr 函数原型的一般格式如下:
char *strchr(const char *s, int c);
它表示在字符串 s 中查找字符 c,返回字符 c 第一次在字符串 s 中出现的位置,如果未找到字符 c,则返回 NULL。也就是说,strchr 函数在字符串 s 中从前到后(或者称为从左到右)查找字符 c,找到字符 c 第一次出现的位置就返回,返回值指向这个位置,如果找不到字符 c 就返回 NULL。
相对于 strchr 函数,strrchr 函数原型的一般格式如下:
char *strrchr(const char *s, int c);
与 strchr 函数一样,它同样表示在字符串 s 中查找字符 c,返回字符 c 第一次在字符串 s 中出现的位置,如果未找到字符 c,则返回 NULL。但两者唯一不同的是,strrchr 函数在字符串 s 中是从后到前(或者称为从右向左)查找字符 c,找到字符 c 第一次出现的位置就返回,返回值指向这个位置。

14. strlen函数

strlen所作的是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符’\0’为止,然后返回计数器值(长度不包含’\0’)。

15. sprintf

srpintf()函数的功能非常强大:效率比一些字符串操作函数要高;而且更具灵活性;可以将想要的结果输出到指定的字符串中,也可作为缓冲区,而printf只能输出到命令行上。
函数功能:格式化字符串,将格式化的数据写入字符串中。
函数原型:int sprintf(char *buffer, const char *format, [argument]…)
参数:
(1)buffer:是char类型的指针,指向写入的字符串指针;
(2)format:格式化字符串,即在程序中想要的格式;
(3)argument:可选参数,可以为任意类型的数据;
函数返回值:buffer指向的字符串的长度;

16. remove()

remove()是C语言中的函数,一般作用是删除文件。函数原型是int remove(cha r*filename)。

17. strncpy

strncpy函数用于将指定长度的字符串复制到字符数组中,是 C语言的库函数之一,来自 C语言标准库,定义于 string.h。语法形式为:char *strncpy(char *dest, const char *src, int n),表示把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest。

18.Signal()函数

#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);
signal函数的功能:为指定的信号安装一个新的信号处理函数。
21.strcmp
strcmp函数是string compare(字符串比较)的缩写,用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。

19. 指针空间的申请和释放

一、定义指针的时候一定要初始化。
变量定义的时候给变量初始化,这是保证不出错的一个很好的习惯。尤其是在指针的使用上,如果我们没有给指针初始化,就会出现野指针,该指针的指向并不是我们所希望的,一旦错误的释放了这个指针,就会发生内存的访问。那么如何初始化指针变量呢,一般有以下几种方法:
1、初始化空指针
int* pInteger=NULL;
2、用已有的变量初始化
int length=5;
int* pInteger=&length;
3、用内存分配函数给指针分配空间
int* pInteger=(int*)malloc(10sizeof(int));//为指针分配大小为10个整数的内存空间。
二、正确的申请和释放内存
指针使用之后,如果不释放指针所使用的内存,就会造成内存的泄露,这样就会有大量内存由于没能释放,别的程序不可以使用这部分内存,如果一个程序不停申请内存而不去释放内存,很快就会造成系统的崩溃。那么如何正确的申请和释放内存呢。
1、指针初始化,上面已讲
2、正确的申请内存
如何申请内存才算是正确的申请内存呢?首先要判断指针是否为空,如果不为空,则释放该指针所指向的内存块,如果不释放这块内存,而直接就申请内存,就会造成内存的泄露。申请内存后,一定要判断是否申请成功。
如:int
pInteger=NULL;//指针定义处

if(pInteger != NULL)
{
free(pInteger);
pInteger=NULL;//指针释放之后并不为空,要设置其为空
}
pInteger=(int*)malloc(10sizeof(int));
if(pInteger = NULL)
{
printf(“内存申请没有成功/n!”);
exit(0);
}

3、内存释放
程序使用完了指针,一定要记得释放指针指向的内存。释放后一定要记得设置指针为空指针。因为free函数在释放指针后,只是单纯的释放了该指针指向的内存空间,而没有将指针赋为空值。所以一定要记得在释放指针后将指针赋为空值。
如:
int
pInteger=NULL;//指针定义处

free(pInteger);//释放指针
pInteger=NULL; //指针赋为空值
三、使用指针时一定要判断指针是否为空
在使用指针时一定要判断指针是否为空,如果为空,则做相应的操作。如果不做判断,则可能错误的使用空指针。
如:char* dest=NULL;

strcpy(dest,“string”);//如果dest为空则出错
正确的使用方法为:
if(dest == NULL)
{
dest=(char*)malloc(7*sizeof(char));//因为字符串以“/0”结束,
//所以要申请7个字符的内存
//判断内存申请是否成功

}
strcpy(dest,“string”);

20.signal信号处理机制

可以用函数signal注册一个信号捕捉函数。原型为:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal 的第1个参数signum表示要捕捉的信号,第2个参数是个函数指针,表示要对该信号进行捕捉的函数,该参数也可以是SIG_DEF(表示交由系统缺省处理,相当于白注册了)或SIG_IGN(表示忽略掉该信号而不做任何处理)。signal如果调用成功,返回以前该信号的处理函数的地址,否则返回 SIG_ERR。
sighandler_t是信号捕捉函数,由signal函数注册,注册以后,在整个进程运行过程中均有效,并且对不同的信号可以注册同一个信号捕捉函数。该函数只有一个参数,表示信号值。

21. 多个if与多个else if详解

下面两句代码,执行结果
形式如下
if ……if……if……else
if ……else if …… else if……else……
通过观看下面的代码与结果截图可以明白多个if与多个else if 及else执行的情况,简单说就是如果是多个else if的话,只要第一个if条件成立,即使满足else if的条件也不会执行else if及else的内容,如果是多个if的话,最后的else会执行的;else与最近的if匹配,包括else if 的if

22. strcmp()函数:比较两个字符串

C语言 strcmp() 函数用于对两个字符串进行比较(区分大小写)。头文件:string.h
语法/原型:
int strcmp(const char* stri1,const char* str2);
参数 str1 和 str2 是参与比较的两个字符串。
strcmp() 会根据 ASCII 编码依次比较 str1 和 str2 的每一个字符,直到出现不到的字符,或者到达字符串末尾(遇见\0)。
返回值:
• 如果返回值 < 0,则表示 str1 小于 str2。
• 如果返回值 > 0,则表示 str2 小于 str1。
• 如果返回值 = 0,则表示 str1 等于 str2。

23. strchr和strrchr函数及用法

strchr 函数原型的一般格式如下:
char *strchr(const char *s, int c);
它表示在字符串 s 中查找字符 c,返回字符 c 第一次在字符串 s 中出现的位置,如果未找到字符 c,则返回 NULL。也就是说,strchr 函数在字符串 s 中从前到后(或者称为从左到右)查找字符 c,找到字符 c 第一次出现的位置就返回,返回值指向这个位置,如果找不到字符 c 就返回 NULL。相对于 strchr 函数,strrchr 函数原型的一般格式如下:
char *strrchr(const char *s, int c);
与 strchr 函数一样,它同样表示在字符串 s 中查找字符 c,返回字符 c 第一次在字符串 s 中出现的位置,如果未找到字符 c,则返回 NULL。但两者唯一不同的是,strrchr 函数在字符串 s 中是从后到前(或者称为从右向左)查找字符 c,找到字符 c 第一次出现的位置就返回,返回值指向这个位置。

24、关于数组做为行参和实参

在函数声明中,如果有下面的写法: int func(int a[10]) { … } 其中int a[10]的声明编译器自动处理为int *a 也就是说,在函数的形参列表里指定数组的成员个数是没有意义的,所以,你传入的实参究竟是比10多还是比10少对于调用函数而言都是合法的。 另外,你在函数的形参里指定数组的大小,编译器实际上并不给这个数组分配实际的空间,也就是说,形参里即使你定义成数组,实际上也是当指针来用,所以一下三种方式是等价的: int fun(int a[]) int fun(int *a) int fun(int a[10])

25、sleep,usleep可能会导致线程堵塞的问题

sleep() may be implemented using SIGALRM; mixing calls to alarm(2) and
sleep() is a bad idea.
sleep和usleep不能使用的原因不是什么线程不安全.而是在某些平台上这两个函数可能会使用SIGALRM,导致程序其他位置的定时机制失效。
实际程序中根本就不应该出现sleep和usleep,如果非要休眠不可,应考虑nanosleep或者select

26、if(a=0)

if(a=0)中,把0赋值给了a,所以括号内的结果是0,0代表逻辑“假”,所以执行else语句y+=y,因此结果是10+10=20。if的条件是非0值时执行对应语句,条件等于0时执行else语句。

27. C++ 关于书上说的“编译的时候分配内存”

编译时分配内存 --------------- 编译时是不分配内存的。此时只是根据声明时的类型进行占位,到以后程序执行时分配内存才会正确。所以声明是给编译器看的,聪明的编译器能根据声明帮你识别错误。
运行时分配内存 --------------- 这是对的,运行时程序是必须调到“内存”的。因为CPU(其中有多个寄存器)只与内存打交道的。程序在进入实际内存之前要首先分配物理内存。
编译过程 -------------- 只能简单说一下,因为如果要详细的话,就是一本书了《编译原理》。编译器能够识别语法,数据类型等等。然后逐行逐句检查编译成二进制数据的obj文件,然后再由链接程序将其链接成一个EXE文件。此时的程序是以EXE文件的形式存放在磁盘上。
运行过程 -------------- 当执行这个EXE文件以后,此程序就被加载到内存中,成为进程。此时一开始程序会初始化一些全局对象,然后找到入口函数(main()或者WinMain()),就开始按程序的执行语句开始执行。此时需要的内存只能在程序的堆上进行动态增加/释放了。

28. usleep函数

作用:usleep函数能把进程挂起一段时间, 单位是微秒(千分之一毫秒)
头文件: unistd.h
语法: void usleep(int micro_seconds);
返回值: 无
内容说明:本函数可暂时使程序停止执行。参数 micro_seconds 为要暂停的微秒数(us)。
这个函数不能工作在windows 操作系统中。用在Linux的测试环境下面。
参见: usleep 与sleep()类似,用于 延迟挂起进程。进程被挂起放到ready queue。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值