【C/C++】部分库函数整理

C++库函数查漏补缺

  1. string s2(s1, x, y);

    • 从s的x位置开始,连续y个字符赋值给s2
  2. if(str.find(“xxx”) != string::npos);

    • string类提供了六种查找函数,每种函数以不同形式的 find 命名,这些操作全都制返回 string::size_type 类型的值,以下标形式标记查找匹配所发生的位置;或者返回一个名为 string::npos 的特殊值,说明查找没有匹配。 即字符串str中不存在字符串 “xxx”
  3. **int sigaction(int signum, const struct sigaction act, sigaction oldact);

    • 其功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)。signum参数指出要捕获的信号类型,act参数指定新的信号处理方式,oldact参数输出先前信号的处理方式(如果不为NULL的话)。参考链接
struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}
* sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数
* sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
* sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。 
    * SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
    * SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
    * SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
  1. signal()函数
    • 第一个参数signum:指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。
    • 第二个参数handler:描述了与信号关联的动作,它可以取以下三种值:
      • SIG_IGN,表示忽略该信号
      • SIG_DFL,表示恢复对信号的系统默认处理。不写此处理函数默认也是执行系统默认操作。
      • sighandler_t类型的函数指针,此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler 所指定的函数。(int)signum是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  1. c_str();
    • 函数原型为const char *c_str();c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同.
    • 这是为了兼容c语言,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string对象转换成c中的字符串样式
    • 注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针,如下代码
//比如:最好不要这样: 
char* c; 
string s="1234"; 
c = s.c_str(); //c最后指向的内容是垃圾,因为s对象被析构,其内容被处理,同时编译器也将报错:将一个const char *赋与一个char *

//应该这样用: 
char c[20]; 
string s="1234"; 
strcpy(c,s.c_str()); //这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作
* c_str() 以 char* 形式传回 string 内含字符串,如果一个函数要求char*参数,可以使用c_str()方法:
string s = "Hello World!";
printf("%s", s.c_str()); //输出 "Hello World!"
  1. ifstream in(st.c_str())

    • C++中有流这样一个概念(stream),包括输入、输出流等,用户使用流的形式与系统打交道
    • ifstream 定义了一个读入文件流(包括文件和设备都可认为是“文件流”),in是ifstream类的一个对象,其名字为in
    • in指输入(也包括从文件中读入),out指输出(包括输出到显示设备,或输出到另一个文件等)
  2. enum

    • 枚举类型,定义第一个参数后,后续参数自增
enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};
//第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1
//这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推
  1. gets();

    • 从标准输入设备读字符串函数,其可以无限读取,不会判断上限,以回车结束读取,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出
  2. fread()、fwrite()

    • fread 函数从文件 fp 中读出 size*count 个字节保存到 buf 中
    • fwrite 把 buf 中的 size*count 个字节写到文件 fp 中
    • 函数 fread 和 fwrite 的返回值为读或写的记录数,成功时返回的记录数等于 count 参数,出错或读到文件末尾时返回的记录数小于 count,也可能返回 0。
    • 函数的原型如下面的代码所示:
size_t fread(void *buf, size_t size, size_t count, FILE *fp);
size_t fwrite(const void * buf, size_t size, size_t count, FILE *fp);
//参数 size 是指单个元素的大小(其单位是字节而不是位,例如,读取一个 int 型数据就是 4 字节);
//参数 count 指出要读或写的元素个数,这些元素在 buf 所指的内存空间中连续存放,共占“size*count”个字节。
  1. daemon()函数
    • daemon()函数主要用于希望脱离控制台,以守护进程形式在后台运行的程序
    • 函数原型#include <unistd.h>——int daemon(int nochdir, int noclose)
      • 当nochdir为0时,daemon将更改进城的根目录为root(“/”)
      • 当noclose为0是,daemon将进程的STDIN, STDOUT, STDERR都重定向到/dev/null
    • 通过ps+grep找到对应的后台进程,使用kill命令将进程杀死;也可创建shell脚本对进程的启动、关闭、重启进行自动管理
//daemon的实现大致如下:
int daemon( int nochdir,  int noclose )
{
   pid_t pid;
   if ( !nochdir && chdir("/") != 0 ) //如果nochdir=0,那么改变到"/"根目录
       return -1;
   if ( !noclose ) //如果没有noclose标志
   {
        int fd = open("/dev/null", O_RDWR); 
        if ( fd  <  0 )
            return -1;
        /* 重定向标准输入、输出、错误到/dev/null,键盘的输入将对进程无任何影响,进程的输出也不会输出到终端*/
        dup(fd, 0);
        dup(fd, 1);
        dup(fd, 2);
        close(fd);
    }
       pid = fork();  //创建子进程.
       if (pid  <  0)  //失败
        return -1;
       if (pid > 0)
           _exit(0); //返回执行的是父进程,那么父进程退出,让子进程变成真正的孤儿进程.
    //创建的 daemon子进程执行到这里了
   if ( setsid()  < 0 )   //创建新的会话,并使得子进程成为新会话的领头进程
           return -1;
   return 0;  //成功创建daemon子进程
}

//使用实例:
int main()
{
    daemon(1, 1)//参数根据需求确定
    /*  在这里添加你需要在后台做的工作代码  */
}
  1. rlimit
    • 每个进程在运行时系统都会设置资源限制。Linux系统中使用rlimit来表示,每个进程都可以设置不同的资源限制,当前进程和其以后fork的子进程会遵循此限制,而其他进程不受当前进程条件的影响
    • 进程的资源限制由系统在启动时进程0初始化,后续进程从进程0处继承,各个进程之后都可以利用下面两个API获取和重设进程资源限制
#include <sys/resource.h>

struct rlimit {
    rlim_t rlim_cur; //current limit
    rlim_t rlim_max; //max limit value for "rlim_cur"
};

int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);
//返回说明:成功执行时,返回0。失败返回-1,errno被设为以下的某个值
    //EFAULT:rlim指针指向的空间不可访问
    //EINVAL:参数无效
    //EPERM :增加资源限制值时,权能不允许
//RLIMIT_AS/RLIMIT_VMEM: 均表示address_space限制,可用内存用户地址空间最大长度,会影响到sbrk和mmap函数。
//RLIMIT_STACK:栈的长度,默认一般是8K
//RLIMIT_CORE:程序crash后生成的core dump文件的大小,如果为0将不生成对应的core文件。
//RLIMIT_NOFILE:进程能够打开的最多文件数目,此限制会影响到sysconf的_SC_OPEN_MAX的返回值。
//RLIMIT_NPROC:每个用户ID能够拥有的最大子进程数目,此限制会影响到sysconf的_SC_CHILD_MAX的返回值。
//RLIMIT_NICE:对应进程的优先级nice值。
//RLIMIT_SWAP:进程能够消耗的最大swap空间。
//RLIMIT_CPU:CPU时间的最大值(秒单位),超过此限制后会发送SIGXCPU信号给进程。
//RLIMIT_DATA:数据段的最大长度。默认为unlimited
RLIMIT_FSIZE:创建文件的最大字节长度。默认为ulimited
//RLIMIT_MSGQUEUE:为posix消息队列可分配的最大存储字节数
//RLIMIT_SIGPENDING:可排队的信号最大数量
//RLIMIT_NPTS:可同时打开的伪终端数目
//RLIMIT_RSS:最大可驻内存字节长度
//RLIMIT_SBSIZE:单个用户所有套接字缓冲区的最大长度
//RLIMIT_MEMLOCK:一个进程使用mlock能够锁定存储空间中的最大字节长度
  1. timeval结构体
    • gettimeofday()函数,获取当前时间(保存在结构体timeval中)
struct timeval {
time_t tv_sec; // 秒
long tv_usec;  // 微秒
};
  1. *int setsockopt( int socket, int level, int option_name,const void optval, size_t optlen);函数

    • 第一个参数socket是套接字描述符;
    • 第二个参数level是被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET;
      • 1)、SOL_SOCKET:通用套接字选项.
      • 2)、IPPROTO_IP:IP选项.
      • 3)、IPPROTO_TCP:TCP选项.
    • 第三个参数option_name指定控制的方式,option_name的取值取决于level,以linux 2.6内核为例,在套接字级别上(SOL_SOCKET),option_name可以有以下取 值:
      • SO_REUSEADDR,打开或关闭地址复用功能。当option_value不等于0时,打开,否则,关闭。它实际所做的工作是置sock->sk->sk_reuse为1或0。参考链接
    • 第四个参数optval:指向包含新选项值的缓冲。
    • 第四个参数optlen:现选项的长度。
  2. inet_pton()和inet_ntop()函数详解

  3. epoll

    • epoll返回时已经明确的知道哪个sokcet_fd发生了事件,不用再逐个比对,从而提高了效率。
    • select的FD_SETSIZE是有限的(默认1024个),而epoll是没有限止的只与系统资源有关
    • 1、epoll_create函数
      • 函数声明:int epoll_create(int size)
      • 该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数,不受限。
    • 2、epoll_ctl函数
      • 函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
      • 该函数用于控制某个epoll文件描述符上的事件,可以注册事件、修改事件、删除事件。
      • 参数:
        • epfd:由epoll_create生成的epoll专用的文件描述符
        • op:要进行的操作例如注册事件,EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除
        • fd:关联的文件描述符
        • event:指向epoll_event的指针
      • 如果调用成功返回0,不成功返回-1
      • 用到的数据结构
typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

struct epoll_event {
    __uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
};

//如:
struct epoll_event ev;
ev.data.fd=listenfd;       //设置与要处理的事件相关的文件描述符
ev.events=EPOLLIN|EPOLLET; //设置要处理的事件类型
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); //注册epoll事件

//常用的事件类型:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符有事件发生;
* 3、epoll_wait函数
    * 函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
    * 该函数用于轮询I/O事件的发生;
    * 参数:
        * epfd:由 epoll_create 生成的epoll专用的文件描述符;
        * epoll_event:用于回传代处理事件的数组;
        * maxevents:每次能处理的事件数;
        * timeout:等待I/O事件发生的超时值;-1表示阻塞,0表示非阻塞。一般-1即可返回事件数
    * epoll_wait运行的原理是
        * 等侍注册在 epfd 上的 socket_fd 的事件的发生,如果发生则将发生的 sokct_fd 和事件类型放入到 events 数组中。并且将注册在 epfd 上的 socket_fd 的事件类型给清空,所以如果下一个循环你还要关注这个 socket_fd 的话,则需要用 epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置 socket_fd 的事件类型。这时不用 EPOLL_CTL_ADD,因为 socket_fd并未清空,只是事件类型清空。这一步非常重要。
* 4、单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。所以你还是有必要建立线程池来发挥更大的效能。
//man中给出了epoll的用法,example程序如下:
for(;;) {
    nfds = epoll_wait(kdpfd, events, maxevents, -1);

    for(n = 0; n < nfds; ++n) {
        if(events[n].data.fd == listener) {
            client = accept(listener, (struct sockaddr *) &local, &addrlen);
            if(client < 0){
                perror("accept");
                continue;
            }
            setnonblocking(client);
            ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = client;
            if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
                fprintf(stderr, "epoll set insertion error: fd=%d\n", client);
                return -1;
            }
        }
        else
            do_use_fd(events[n].data.fd);
    }
}
//此时使用的是ET模式,即边沿触发,类似于电平触发,epoll中的边沿触发的意思是只对新到的数据进行通知,而内核缓冲区中如果是旧数据则不进行通知,所以在do_use_fd函数中应该使用如下循环,才能将内核缓冲区中的数据读完。
while (1) {
    len = recv(*******);
    if (len == -1) {
    if(errno == EAGAIN)
        break;
    perror("recv");
    break;
    }
    do something with the recved data...
}

//在上面例子中没有说明对于listen_socket_fd该如何处理,有的时候会使用两个线程,一个用来监听accept、另一个用来监听epoll_wait。
//如果是这样使用的话,则listen_socket_fd使用默认的阻塞方式就行了,而如果epoll_wait和accept处于一个线程中,即全部由epoll_wait进行监听,则需将listen_socket_fd也设置成非阻塞的。
//这样对accept也应使用while包起来(类似于上面的recv),因为epoll_wait返回时只是说有连接到来了,并没有说有几个连接,而且在ET模式下epoll_wait不会再因为上一次的连接还没读完而返回,这种情况确实存在,我因为这个问题而耗费了一天多的时间.
//这里需要说明的是,每调用一次accept将从内核中的已连接队列中的队头读取一个连接,因为在并发访问的环境下,有可能有多个连接“同时”到达,而epoll_wait只返回了一次
* 5、唯一有点麻烦是epoll有2种工作方式:LT和ET
    * LT(level triggered)
        * 缺省的工作方式,同时支持block和no-block socket。
        * 这种模式中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。
        * 如果你不作任何操作,内核还是会继续通知你,所以这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。
    * ET(Edge triggered)
        * 高速工作方式,只支持no-block socket。
        * 这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,且不再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送、接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。
        * 但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。
  1. printf()、sprintf()、fprintf()

    • printf() 是把格式化字符串输出到标准输出(一般是屏幕)
    • sprintf() 是把格式化字符串输出到指定字符串,所以参数比printf多了个char * ,那就是目标字符串的地址
    • fprintf() 是把格式化字符串输出到指定文件中,所以参数比printf多了个文件指针File * ,那是目标文件的文件描述符(文件流指针)
  2. **sem_t sem_open(const char name, int oflag,/mode_t mode, unsigned int value/);函数

    • 功能:用来创建一个新的命名信号量或者使用一个现有的信号量
    • name:信号量的名字
    • oflag:使用O_CREAT(如果信号量已经存在,则O_CREAT什么都不做函数也不出错)。如果使用O_CREAT|O_EXCL(表示如果信号量已存在,则创建失败,sem_open函数出错返回)
    • mode:指定信号量访问权限
    • value:指定信号量的初始值。取值范围是0~SEM_VALUE_MAX
    • 参考链接
  3. fcntl系统调用

    • 用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
    • fcntl函数功能依据cmd的值的不同而不同,参考链接
//函数原型
#include<unistd.h>  
#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); 
  1. on_exit()函数
    • 函数原型:int on_exit(void (*function)(int, void *), void *arg);
    • on_exit()用来注册终止处理程序,当程序通过调用exit()或从main中返回时,参数function所指函数被调用,之后由exit()结束程序
    • 第一个参数是指定的处理函数,第二个参数是来自on_exit()函数中的arg指针,会传给参数function函数
    • 成功返回0,失败返回非0值
    • 同一个函数若注册多次,那它也会被调用多次;
        * 当一个子进程是通过fork()产生时,它将继承父进程的所有终止处理程序。在成功调用exec系列函数后,所有的终止处理程序都会被删除
//示例  
#include<stdlib.h>
void my_exit(int status,void *arg)
{
    printf("before exit()!\n");
    printf("exit (%d)\n",status);
    printf("arg = %s\n",(char*)arg);
}

void main()
{
    char *str="test";
    on_exit(my_exit,(void *)str);
    exit(1234);
}
//执行
//before exit()!
//exit (1234)
//arg = test
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值