读书笔记:C和指针6

第十六章:标准函数库
    16.5 信号
        程序中所发生的事件绝大多数都是由程序本身所引起的,例如执行各种语句和请求输入。但是,有些程序必须遇到的事件却不是程序本身所引发的。
        一个常见例子就是用户中断了程序。如果部分计算好的结果必须进行保存以避免数据的丢失,程序必须预备对这类事件作出反应,
        虽然它没有办法预测什么时候会发生这种情况。
        信号就是用于这种目的。信号(signal)表示一种事件,它可能异步地发生,也就是并不与程序实行过程中的任何事件同步。
        如果程序并未安排怎样处理一个特定的信号,那么当该信号出现时程序就作出一个缺省的反应。标准并未定义这个缺省反应是什么,
        但绝大多数编译器都选择终止程序。另外,程序可以调用signal函数,或者忽略这个信号,或者设置一个信号处理函数(siganl handler),
        当信号发生时程序就调用这个函数。
        <signal.h>
        SIGABRT about函数引发的信号,请求异常终止
        SIGFPE 算数错误,常见有算术上溢,下溢以及除零错误    同步!!
        SIGILL 非法指令
        SIGSEGV 检测到对内存的非法访问
        这几个信号是同步的,因为它们都是在程序内部发生。而且每次运行都在同一个地方发生相同的错误。
        
        SIGINT 收到一个交互性注意信号                       异步!!
        SIGTERM 收到一个终止程序的请求
        它们在程序的外部产生,通常是由程序的用户所触发,表示用户试图向程序传达一些信息。
        在实现了这两个信号的系统里,一种常见策略是为SIGINT 定义一个信号处理函数,目的是执行一些日常维护工作(housekeeping)并在程序退出前保存数据。
        但是,SIGTERM则不配备信号处理函数,这样当程序终止时便不必执行这些日常维护工作。
        
        int raise(int sig); 显式地引发一个信号
        程序使用三种方式对它作出反应。缺省的反应有编译器定义,通常是终止程序;
        信号被忽略;设置一个信号处理函数。
        
        void (*signal(int sig, void (*handler)(int)))(int);
        省略返回类型 signal(int sig, (*handler)(int));
        从原型中去掉参数 void (*signal())(int); 返回一个函数指针
        事实上,signal返回一个指向该信号以前的处理函数的指针。通过保存这个值,你可以为信号设置一个处理函数并在将来恢复为先前的处理函数。
        如果调用signal失败,例如由于非法的代理函数所致,函数将返回SIG_ERR 。这个值是个宏,它在signal.h中定义。
        
        signal.h 还定义了另外两个宏,SIG_DFL 和 SIG_IGN,作为signal函数的第2个参数。
        SIG_DFL 恢复对该信号的缺省反应,SIG_IGN 使该信号被忽略。
        
        信号处理函数
        信号处理函数可能执行的工作类型是很有限的。如果信号是异步的,也就是说不是由于调用about 或 raise 函数引起的,
        信号处理函数便不应该调用除signal之外的任何库函数,因为这种情况下其结果是未定义的。
        而且,信号处理函数除了能向一个类型为volatile sig_atomic_t 的静态变量赋一个值外,可能无法访问其他任何静态数据。
        为了保证真正的安全,信号处理函数所能做的就是对这些变量之一进行设置然后返回。程序的剩余部分必须定期检查变量的值,看看有没有信号发生。
        
        这些严格的限制是由于信号处理的本质产生的。信号通常用于提示发生了错误,在这些情况下,CPU的行为是精确定义的,
        但在程序中,错误所处的上下文环境可能很不相同,因此它们不一定能够良好定义。
        警告
        标准表示信号处理函数可以通过调用exit终止程序。用于除了SIGABRT之外所有信号的处理函数也可以调用about终止程序。
        但是,由于这两个都是库函数,所以当他们被异步信号处理函数调用时可能无法正常运行。
        
        volatile
        信号可能在任何时候发生,所以由信号处理函数修改的变量的值可能在任何时候发生改变。因此,你不能指望这些变量在两条相邻的程序语句中具有相同的值。
        volatile关键字告诉编译器这个事实,防止它以一种可能修改程序含义的方式“优化”程序。
        例如
        if(value){
            printf("True\n");
        }
        else{
            printf("False\n");
        }
        if(value){
            printf("True\n");
        }
        else{
            printf("False\n");
        }
        在普通情况下,你可能认为第二个测试和第一个测试具有相同的结果。如果信号处理函数修改了这个变量,第二个测试结果可能不同。
        除非变量被声明为volatile,否则编译器可能会用下面的代码进行替换,从而对程序进行“优化”。
        if(value){
            printf("True\n");
            printf("True\n");
        }
        else{
            printf("False\n");
            printf("False\n");
        }
        
        从一个信号处理函数返回导致程序的执行流从发生地点恢复执行。这个规则的例外情况是SIGFPE。由于计算无法完成,从这个信号返回的效果是未定义的。
        警告
        如果你希望捕捉将来同种类型的信号,从当前的处理函数返回之前注意要调用signal函数重新设置信号处理函数,
        否则只有第一个信号才会被捕捉,接下来的信号将使用缺省反应进行处理。
        
    16.6 打印可变参数列表 <stdarg.h>
        int vprintf(char const *format, va_list arg);
        int vfprintf(FILE *stream, char const *format, va_list arg);
        int vsprintf(char *buffer, char const *format, va_list arg);
        在调用这些函数之前,arg 参数必须使用va_start 进行初始化。
    
    16.7 执行环境
        终止执行 <stdlib.h>
        
        void abort(void);
        void atexit(void (func)(void));
        void exit(int status);
        
        abort 不正常终止一个正在执行的程序。引发SIGABRT信号。
        atexit 可以把一些函数注册为退出函数(exit function)。当程序将要正常终止时,退出函数被调用。
        exit 正常终止程序。如果main函数返回一个值结束,那么其效果相当于用这个值作为参数调用exit函数。
        当exit 函数被调用时,所有被atexit函数注册为退出函数的函数将按照他们所注册的顺序被反序调用。然后所有流缓冲区被刷新,所有文件被关闭。
        用tmpfile函数创建的文件被删除,然后,退出状态返回给宿主环境,程序停止工作。
        警告
        如果任何一个用atexit注册为退出函数的函数再次调用了exit,其效果是未定义的。这个错误可能导致一个无限循环,很可能只有当堆栈的内存耗尽后才停止。
        
        断言 <assert.h>
        断言就是声明某种东西应该为真。ANSI C 实现了assert宏,它在调试程序时很有用。
        void assert(int experssion);
        如果假(零),就向标准错误打印一条诊断信息并终止程序。这条信息的格式由编译器定义的,但它将包含这个表达式和源文件的名字以及断言所在的行号。
        
        例如,如果一个函数必须用一个不能为NULL的指针参数进行调用,那么函数可以用断言验证这个值:
        assert(value != NULL);
        如果函数错误的接受了一个NULL参数,程序将打印一条类似下面形式的信息:
        Assertion failed: value != NULL, file.c line 274
        
        提示
        用这种方法使用断言是调试变得更容易。
        
        当程序被完整的测试完毕之后,你可以在编译时通过定义NDEBUG消除所有的断言。你可以使用-DNDEBUG编译器命令行选项或者在源文件中头文件assert.h被包含之前增加这个定义
        #define NDEBUG
        当NDEBUG被定义后,预处理器将丢弃所有的断言,这样就消除了这方面的开销,而不必从源文件中把所有的断言实际删除。
        
        环境 <stdlib.h>
        环境就是一个有编译器定义的名字/值对的列表,它由操作系统进行维护。getenv函数在这个列表中查找一个特定的名字,如果找到,返回一个指向其对应值的指针。
        如果找不到,返回NULL
        char *getenv(char const *name);
        
        执行系统命令 <stdlib.h>
        void system(char const *command);
        
        排序和查找 <stdlib.h>
        void qsort(void *base, size_t n_elements, size_t el_size, int (* compare)(void const *, void const *));
        void *bsearch(void const *key, void const *base, size_tn_elements, size_t el_size, int (*compare)(void const *, void const *));
        
    16.8 locale
        "C" locale
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值