zoukankan      html  css  js  c++  java
  • C语言常见的函数调用

    C语言常见的函数调用

    isatty,函数名,主要功能是检查设备类型,判断文件描述词是否为终端机。

    函数名: isatty

    用 法: int isatty(int desc);

    返回值:如果参数desc所代表的文件描述词为一终端机则返回1,否则返回0。

    程序例:

    #include <stdio.h>

    #include <io.h>

    int main(void)

    {

    int handle;

    handle = fileno(stdout);

    if (isatty(handle))

    printf("Handle %d is a device type ", handle);

    else

    printf("Handle %d isn't a device type ", handle);

    re

    函数名称:fileno(在VC++6.0下为_fileno)

    函数原型:int _fileno( FILE *stream );

    函数功能:fileno()用来取得参数stream指定的文件流所使用的文件描述符

    返回值:某个数据流的文件描述符

    头文件:stdio.h

    相关函数:open,fopen,fclose

    void *memset(void *s, int ch, size_t n);

    函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。

    memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体数组进行清零操作的一种最快方法

    函数原型

    char *fgets(char *buf, int bufsize, FILE *stream);

    参数

    *buf: 字符型指针,指向用来存储所得数据的地址。

    bufsize: 整型数据,指明存储数据的大小。

    *stream: 文件结构体指针,将要读取的文件流。

    返回值

    1. 成功,则返回第一个参数buf;
    2. 在读字符时遇到end-of-file,则eof指示器被设置,如果还没读入任何字符就遇到这种情况,则buf保持原来的内容,返回NULL;
    3. 如果发生读入错误,error指示器被设置,返回NULL,buf的值可能被改变。

    chdirC语言中的一个系统调用函数(同cd),用于改变当前工作目录,其参数为Path 目标目录,可以是绝对目录或相对目录。

    exec函数

    linux下c语言编程exec函数使用

    2012年04月10日 09:39:27

    阅读数:19800

    exec用被执行的程序完全替换调用它的程序的影像。fork创建一个新的进程就产生了一个新的PID,exec启动一个新程序,替换原有的进程,因此这个新的被exec执行的进程的PID不会改变,和调用exec函数的进程一样。

      下面来看下exec函数族:

      #include <uniSTd.h>

      int execl(cONst char *path, const char *arg, ...);

      int execlp(const char *file, const char *arg, ...);

      int execle(const char *path, const char *arg, ..., char *const envp[]);

      int execv(const char *path, char *const argv[]);

      int execvp(const char *file, char *const argv[]);

      int execve(const char *path, char *const argv[], char *const envp[]);

      exec函数族装入并运行程序pathname,并将参数arg0(arg1,arg2,argv[],envp[])传递给子程序,出错返回-1。在exec函数族中,后缀l、v、p、e添加到exec后,所指定的函数将具有某种操作能力有后缀:

    execl("/bin/ls","ls","-a",NULL)

    execv("/bin/ls",arg)

    execlp("ls","ls","-a",NULL)

    execvp("ls",arg)

    execle("/bin/ls","ls","-a",NULL,envp)

    execve("/bin/ls",arg,envp)

    assert()使用

      assert()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句,它的作用是终止程序以免导致严重后果,同时也便于查找错误

    linux编程之dup与dup2

    在linux下,通过open打开以文件后,会返回一个文件描述符,文件描述符会指向一个文件表,文件表中的节点指针会指向节点表。看下图:

    打开文件的内核数据结构

    dup和dup2两个函数都可以用来复制打开的文件描述符,复制成功后和复制源共享同一个文件表。看下表

    执行dup后的内核数据结构

    dup函数

    fd1=dup(fd)

    fd1和fd共享一个文件表(对df进行什么操作,fd1也会有相应的操作,fd和fd1是同步的)

    具体解释:

       

    #inclue<stdio.h>

    #include<sys/types.h>

    #include<unistd.h>

    #include<fcntl.h>

    #include<stdlib.h>

    int main()

    {

    char buf[6]={0};

    char buf1[6]={0};

    int fd = open("file",O_RDWR|O_CREAT,0644);

    if(fd < 0)

    printf("open error");

    printf("fd:%d ",fd);

    //输出fd=3;

    write(fd,"hello,world",12);

    lseek(fd,0,SEEK_SET);  //将文件偏移量置为0,就是从第一个字符开始读(h开始)

    read(fd,buf,5);

    printf("fd:%s",buf);//输出hello

    int fd1 = dup(fd);

    read(fd1,buf1,5); //之前做的是对fd的读写操作,并没有对fd1做任何操作。但在这对fd1进行了读,如果输出数据。说明fd和fd1是同步的(fd做了什么相当于fd1也做了什么)

    printf("fd1:%s ",buf1); //输出,worl

    //既然输出的是fd中的内容,说明fd和fd1共用一个文件表,读到的是,worl,而不是hello(我们在上面将偏移量从第一个字符开始,输出hello之后,fd的偏移量距离开始有5个字符当我们再次读fd的时候,它是从第6个字符开始读的,很明显,第6个是逗号,往后读5个,就是,worl),说明偏移量是一致的。(其实不用写偏移量,因为共用文件表就意味着文件偏移量也共用)

    printf("fd1:%d ",fd1);//输出fd1 = 4

    //fd=3不等于fd1说明不共用同一个文件描述符。这也是dup和dup2的区别。

    close(fd);

    close(fd1);

    return 0;

    }

    (2)dup2函数

       

    fd2 = dup2(fd,fd1);

    fd2用的fd1(第二个参数)的描述符,用的fd(第一个参数)的文件(和fd共享一个文件表,当然也共享文件偏移量)

    强调第几个参数是因为如果你写成fd2=dup2(fd1,fd);那么fd2 =fd,和fd1共享同一个文件表。

       

    #inclue<stdio.h>

    #include<sys/types.h>

    #include<unistd.h>

    #include<fcntl.h>

    #include<stdlib.h>

    int main()

    {

    int fd = open("file",O_RDWR|O_CREAT,0644);

    if(fd < 0)

    printf("open error");

    printf("fd:%d ",fd);

    //输出fd=3;

    int fd1 =open("text",,O_RDWR|O_CREAT,0644);

    if(fd1 < 0)

    printf("open error");

    printf("fd1:%d ",fd1);

    //输出fd1=4;

    int fd2 = dup2(fd,fd1);

    printf("fd2:%d ",fd2);

    //输出fd2=4;

    //fd1 =fd2=4;说明fd2使用了fd1的文件描述符。

       

    char buf[12]="hello,world";

    write(fd,buf,12); //我们对fd进行了写,并没有对fd2进行写

    read(fd2,buf,12);//但是我们对fd2读的时候,如果没有写,怎么可能读出来呢

    printf("fd2:%s ",buf);//事实是读出来了

    //输出fd2:hello,world    //说明fd和fd2共用一个文件表。

       

    lseek(fd,5,SEEK_SET);//距离开始偏移5位,说明下次读的时候是从第6个开始,注意我们是对fd进行偏移,没有对fd2偏移

    read(fd2,buf,5);  //但是如果读fd2结果是从第6个字符开始的

    buf[5]=0; //如果不写这句,输出的buf是按照12个字符输出的。因为定义buf的时候数组中可以放12个字符。

    printf("fd2:%s ",buf);//输出fd2:,worl  //说明fd2和fd共享文件偏移量。

    close(fd);

    close(fd2);

    return 0;

    }

    dup和dup2的区别

    dup:fd1= dup(fd);目标描述符使用了fd的文件表

    dup2:fd2 = dup2(fd1,fd)目标描述符使用了fd1的描述符,使用了fd的文件表

    linux编程之pipe()函数

    管道是一种把两个进程之间的标准输入和标准输出连接起来的机制,从而提供一种让多个进程间通信的方法,当进程创建管道时,每次都需要提供两个文件描述符来操作管道。其中一个对管道进行写操作,另一个对管道进行读操作。对管道的读写与一般的IO系统函数一致,使用write()函数写入数据,使用read()读出数据。

    #include<unistd.h>

    int pipe(int filedes[2])

    返回值:成功,返回0,否则返回-1。参数数组包含pipe使用的两个文件的描述符。fd[0]:读管道,fd[1]写管道。

    必须在fork()中调用pipe(),否则子进程不会继承文件描述符。两个进程不共享祖先进程,就不能使用pipe。但是可以使用命名管道。

    1 #include<stdio.h>

    2 #include<stdlib.h>

    3 #include<string.h>

    4 #include<unistd.h>

    5 #include<sys/types.h>

    6 int main(void){

    7 int result=-1;

    8 int fd[2],nbytes;

    9 pid_t pid;

    10 char string[]="hell world, my pipe!";

    11 char readbuffer[100];

    12 int *write_fd=&fd[1];

    13 int *read_fd=&fd[0];

    14 result=pipe(fd);;

    15 if(-1==result){

    16 printf("fail to create pipe ");

    17 return -1;

    18 }

    19 pid=fork();

    20 if(-1==pid){

    21 printf("fail to fork ");

    22 return -1;

    23 }

    24 if(0==pid){

    25 close(*read_fd);

    26 result=write(*write_fd,string,strlen(string));

    27 return 0;

    28 }else{

    29 close(*write_fd);

    30 nbytes=read(*read_fd,readbuffer,sizeof(readbuffer));

    31 printf("the parent receive %d bytes data: %s ",nbytes,readbuffer);

    32 }

    33 return 0;

    34 }

    the parent receive 20 bytes data: hell world, my pipe!

    #include <stdio.h>

    #include <stdlib.h>

    #include <unistd.h>

    #define COUNT (10)

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

    {  

      int pipefd[2];  

      int read_count = 0;

      char buf[COUNT] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};;  

      if (pipe(pipefd) == -1) {  

        perror("call pipe failed ");  

        exit(EXIT_FAILURE);  

      }  

      printf("write %d chars to pipe1 ", COUNT);

      write(pipefd[1], buf, COUNT);  

      while (read(pipefd[0], &buf, 1) > 0) 

      {

        printf("read %c from pipe0 ", buf[0]);

        read_count++;

        if(read_count == COUNT)

        {

          printf("total read %d chars ", read_count);

          break;

        }  

      } 

      close(pipefd[0]);  

      close(pipefd[1]);  

    }  

    编译及执行结果:

    [root@alexs-centos core_dump]# gcc pipe.c 
    [root@alexs-centos core_dump]# ./a.out 
    write 10 chars to pipe1 
    read 0 from pipe0
    read 1 from pipe0
    read 2 from pipe0
    read 3 from pipe0
    read 4 from pipe0
    read 5 from pipe0
    read 6 from pipe0
    read 7 from pipe0
    read 8 from pipe0
    read 9 from pipe0
    total read 10 chars 

     

    从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2), 0与进程的标准输入相关联,1与进程的标准输出相关联,2与进程的标准错误输出相关联,一个进程当前有哪些打开的文件描述符可以通过/proc/进程ID/fd目录查看

     

    C语言提供了几个标准库函数,可以将任意类型(整型、长整型、浮点型等)的数字转换为字符串。


    1.int/float to string/array:

    C语言提供了几个标准库函数,可以将任意类型(整型、长整型、浮点型等)的数字转换为字符串,下面列举了各函数的方法及其说明。
    ● itoa():将整型值转换为字符串。
    ● ltoa():将长整型值转换为字符串。
    ● ultoa():将无符号长整型值转换为字符串。
    ● gcvt():将浮点型数转换为字符串,取四舍五入。
    ● ecvt():将双精度浮点型值转换为字符串,转换结果中不包含十进制小数点。
    ● fcvt():指定位数为转换精度,其余同ecvt()。


    除此外,还可以使用sprintf系列函数把数字转换成字符串,其比itoa()系列函数运行速度慢

    2. string/array to int/float
    C/C++语言提供了几个标准库函数,可以将字符串转换为任意类型(整型、长整型、浮点型等)。

    ● atof():将字符串转换为双精度浮点型值。
    ● atoi():将字符串转换为整型值。
    ● atol():将字符串转换为长整型值。
    ● strtod():将字符串转换为双精度浮点型值,并报告不能被转换的所有剩余数字。
    ● strtol():将字符串转换为长整值,并报告不能被转换的所有剩余数字。
    ● strtoul():将字符串转换为无符号长整型值,并报告不能被转换的所有剩余数字。

    以下是用itoa()函数将整数转换为字符串的一个例子:
    # include <stdio.h>
    # include <stdlib.h>
    void main (void)
    {
    int num = 100;
    char str[25];
    itoa(num, str, 10);
    printf("The number 'num' is %d and the string 'str' is %s. " ,
    num, str);
    }

    itoa()函数有3个参数:第一个参数是要转换的数字,第二个参数是要写入转换结果的目标字符串,第三个参数是转移数字时所用 的基数。在上例中,转换基数为10。10:十进制;2:二进制...

     

     

    C语言pthread_create传递带多个参数的函数& pthread_join

    pthread_create是类Unix操作系统(Unix、Linux、Mac OS X等)的创建线程的函数,头文件在pthread.h中。函数的声明如下:

    int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,

    (void*)(*start_rtn)(void*),void *arg);

    //返回值:若成功则返回0,否则返回错误编号

    参数
    第一个参数为指向线程标识符的指针。
    第二个参数用来设置线程属性。
    第三个参数是线程运行函数的起始地址。
    最后一个参数是运行函数的参数。

    从第三个函数可以看到,传入的函数参数需要为void*类型。但是很多情况下需要线程处理的函数是多参数的。可以通过把参数封装成结构体的方式来实现传递带多个参数的函数。

    struct fun_para

    {

    var para1;//参数1

    var para2;//参数2

    .......

    }

    将这个结构体指针,作为void *形参的实际参数传递

    struct fun_para para;

    pthread_create(&ntid, NULL, thr_fn,&para);

    接着在线程的调用函数thr_fn中可以通过下面的方式使用通过para传入的参数。

    void *thr_fn(void *arg)

    {

    fun_para *para;

    para = (fun_para *) arg;

    para->para1;//参数1

    para->para2;//参数2

    ......

    //pthread_exit(0);

    return ((void *)0);

    }

    Additional Mark: 代码中如果没有pthread_join,主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
    函数定义:

    int pthread_join(pthread_t thread, void **retval);

    • 1

    描述 : pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。
    参数: thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。
    返回值 : 0代表成功。 失败,返回的则是错误号。

    tmp1 = pthread_join(tid, &retval);

    if (tmp1 != 0)

    {

    printf("cannot join with thread1 ");

    }

    多线程下变量-原子操作 __sync_fetch_and_add等等

        当然我们知道,count++这种操作不是原子的。一个自加操作,本质是分成三步的:
         1 从缓存取到寄存器
         2 在寄存器加1
         3 存入缓存。
        由于时序的因素,多个线程操作同一个全局变量,会出现问题。这也是并发编程的难点。在目前多核条件下,这种困境会越来越彰显出来。
        最简单的处理办法就是加锁保护,这也是我最初的解决方案。看下面的代码:
          pthread_mutex_t count_lock = PTHREAD_MUTEX_INITIALIZER;

          pthread_mutex_lock(&count_lock);
          global_int++;
          pthread_mutex_unlock(&count_lock);
        后来在网上查找资料,找到了__sync_fetch_and_add系列的命令,发现这个系列命令讲的最好的一篇文章,英文好的同学可以直接去看原文。Multithreaded simple data type access and atomic variables

         __sync_fetch_and_add系列一共有十二个函数,有加/减/与/或/异或/等函数的原子性操作函数,__sync_fetch_and_add,顾名思义,现fetch,然后自加,返回的是自加以前的值。以count = 4为例,调用__sync_fetch_and_add(&count,1),之后,返回值是4,然后,count变成了5.
        有__sync_fetch_and_add,自然也就有__sync_add_and_fetch,呵呵这个的意思就很清楚了,先自加,在返回。他们哥俩的关系与i++和++i的关系是一样的。被谭浩强他老人家收过保护费的都会清楚了。
        有了这个宝贝函数,我们就有新的解决办法了。对于多线程对全局变量进行自加,我们就再也不用理线程锁了。下面这行代码,和上面被pthread_mutex保护的那行代码作用是一样的,而且也是线程安全的。

    __sync_fetch_and_add( &global_int, 1 );
        下面是这群函数的全家福,大家看名字就知道是这些函数是干啥的了。

    在用gcc编译的时候要加上选项 -march=i686
    type __sync_fetch_and_add (type *ptr, type value);
    type __sync_fetch_and_sub (type *ptr, type value);
    type __sync_fetch_and_or (type *ptr, type value);
    type __sync_fetch_and_and (type *ptr, type value);
    type __sync_fetch_and_xor (type *ptr, type value);
    type __sync_fetch_and_nand (type *ptr, type value);
    type __sync_add_and_fetch (type *ptr, type value);
    type __sync_sub_and_fetch (type *ptr, type value);
    type __sync_or_and_fetch (type *ptr, type value);
    type __sync_and_and_fetch (type *ptr, type value);
    type __sync_xor_and_fetch (type *ptr, type value);
    type __sync_nand_and_fetch (type *ptr, type value);

  • 相关阅读:
    虚函数与动态多态
    我读苏东坡
    Linux高性能服务器编程学习-第三章思维导图笔记
    Linux高性能服务器编程学习-第二章思维导图笔记
    Linux高性能服务器编程学习-第一章思维导图笔记
    【Knockout】五、创建自定义绑定
    【Knockout】三、data-bind声明式绑定
    【Knockout】四、绑定上下文
    【Knockout】一、认识Knockout,你会爱上它
    【Knockout】二、监控属性Observables
  • 原文地址:https://www.cnblogs.com/kexinxin/p/9939134.html
Copyright © 2011-2022 走看看