other subject of C language

C语言的其他专题

可变长的实参列表

l  va_list:该类型适合于保存宏va_startva_argva_end所需的信息,为了访问到一个可变长实参列表中的实参,必须声明一个类型为va_list的对象;

l  va_start:在一个可变长实参列表中的实参被访问之前,先要调用这个宏,这个宏的功能是:初始化用va_list声明的对象,以便让宏va_argva_end来使用;

l  va_arg:这个宏展开成一个表示可变长实参列表中下一个实参的值和类型的表达式,每次对va_arg的调用都要修改va_list声明的对象,已使这个对象指向列表中的下一个实参;

l  va_end:当一个函数的可变长实参列表时通过宏va_start来引用时,宏va_end可用于从这样的函数中正常返回。

 

//using variable-length argument lists

#include <stdio.h>

#include <stdarg.h>

 

double average(int i,…);//prototype

int main(void)

{

    double w = 37.5;

    double x = 22.5;

    double y = 1.7;

    double z = 10.2;

 

    printf("%s%.1f\n%s%.1f\n%s%.1f\n%s%.1f\n\n",

            "w = ",w,"x = ",x,"y = ",y,"z = ",z);

    printf("%s%.3f\n%s%.3f\n%s%.3f\n",

            "The averange of w and x is ",average(2,w,x),

            "The averange of w , x and y is ",average(3,w,x,y),

            "The averange of w , x ,y and z is ",average(4,w,x,y,z));

    return 0;

}

 

double average(int i,…)

{

    double total = 0;

    int j;

    va_list ap;//stores information needed by va_start and va_end

 

    va_start(ap,i);//initializes the va_list object

 

    for(j=1;j<=i;j++)

        total += va_arg(ap,double);

 

    va_end(ap);//clean up variable-length argument list

    return total/i;

}

执行效果为:

clip_image002

 

atexit函数接收一个指向某个函数的指针即函数名作为实参,该函数将在程序正常结束之时被调用。这个可以写一些结束语什么的。

关于文件的深入讨论

       C语言访问文件可以通过二进制的模式或者文本文件的模式,有些计算机不支持二进制文件,而此时如果程序员又使用二进制模式打开的话,那么这个文件将被作为文本文件处理

l  在要求高性能的场合,可以考虑采用二进制文件而不是文本文件;

l  如果要编写可移植的程序,最好使用文本文件。

标准函数库

标准函数库

算术

       求商和余数的函数div

字符串转换

       函数atoiatol执行基数为10的转换;

       而函数strtolstrtoul允许你在转化时指定基数,同时还允许访问字符串的剩余部分。

浮点表示形式

       函数modf把一个浮点值分成整数和小数两个部分。

非本地跳转

       setjmplongjmp函数提供了一种类似goto语句的机制,但它并不局限于一个函数的作用域之内。这些函数常用于深层嵌套的函数调用链

信号

       信号表示一种事件,它可能异步地发生,也就是并不予程序执行过程的任何时间同步。

信号处理函数

       由于信号可能在任何时候发生,所以由信号处理函数修改的变量的值可能会在任何时候发生改变。因此,我们不能指望这些变量在两条相邻的程序语句中肯定具有相同的值。volatile关键字告诉编译器这个事实,防止它以一种可能修改程序含义的方式优化程序。例如:

clip_image002[4]

通常情况下,上面的程序会认为第二个测试和第一个测试具有相同的结果,就优化为如下:

clip_image004[4]

而如果把变量value声明为volatile类型的,就不会进行此类优化。

有时,如果不用volatile修饰符,可能无法编写多线程程序,要么编译器失去大量优化的机会。

终止执行

l  abort函数用于不正常地终止一个正在执行的程序;

l  atexit函数可以把一些函数注册为退出函数exit function

l  exit函数用于正常终止程序;

断言

       assert(test)用于检测test是否为真。用这种方法可以使调试变得更容易。并且我们可以在头文件assert.h被包含之前,添加#define NDEBUG皆可以禁用所有的断言。

总结

l  frexpldexp函数在创建与机器无关的浮点数表示形式方面是很有用的。frexp函数用于计算一个给定值的表示形式;ldexp函数用于解释一个表示形式,恢复它的原先值;

l  qsort函数把一个数组中的值按照升序进行排序;

l  bsearch函数用于在一个已经排好序的数组中用二分法查找一个特定的值;

l  locale就是一组函数,根据世界各国的约定差异对C程序的行为进行调整;

l  使用setjmplongjmp可能导致晦涩难懂的代码;

l  使用断言可以简化程序的调试;

 

基本脚本编译

基本脚本编译

8.1 使用多条命令

       如果需要执行多个命令,只需用分号隔开即可。但是要注意命令行的最大字符不能超过255个字符。

8.2 创建脚本文件

       第一行为#!/bin/bash,一般#为注释行,但是第一行是个特例,#后面跟的!告诉shell运行下面的shell,紧接着下一行可以写一行注释用于说明该shell的作用。

       如果要执行需要绝对路径或者将该路径添加到PATH环境变量中。

8.3 显示消息

       可以使用echo what you want to show 来显示你想显示的信息,默认是不需要用引号来标记的,但是如果字符串中有了引号,就需要用引号来标记了。

       另外如果想让echo命令结果和echo文本字符串在一行可以使用-n参数,不换行操作。

8.4 使用变量

8.4.1 环境变量

       echo HOME $HOME

如果使用echo “my HOME is $HOME”,也可以识别,但是这里有个缺陷,比如,你输入echo “This cost of the item is $15”,如果定义过15这个环境变量,有可能会被替换掉,所有可以使用\来防止当作环境变量echo “This cost of the item is \$15”

8.4.2 用户变量

       用户变量可以是由不超过20个字符的字母、数字或下划线组成的文本字符串。用户变量区分大小写

       shell脚本自动为变量值确定数据类型。

8.4.3 反引号

       反引号允许将shell命令的输出赋值给变量。尽管这看起来好像意义不大,但是在脚本编程中,它是主要的构件。

       比如:dateok=`date`,就会运行反引号符号的命令并把结果赋值给变量dateok

8.5 重定向输入输出

8.5.1 输出重定向

date > log        :将日期写入log,如果log存在会覆盖掉

date >> log :将日期追加写入

8.5.2 输入重定向

       输入重定向是输出重定向的相反过程。输入重定向不是将命令的输出重定向到一个文件中,而是将一个文件的内容重定向到一条命令中。

       形式为:command < inputfile

       比如$ wc < filename,就可以输出filename的行数、字数、字符数的统计信息。

       还有一种重定向为内置输入重定向(inline input redirection)。这种方法允许在命令行中而非文件中为输入重定向指定数据。内置输入重定向符号是两个<<,除了这个符号,还需要指定一个文本标记来说明输入数据的开始和结尾。

       形式为:

$ command << marker

Data

Data

Data

marker

比如一个实例为:

$wc << EOF

>test string 1

>test string 2

>test string 3

>EOF

8.6 管道

       管道传送为将输出重定向到另一条命令,而不是将命令的输出重定向到一个文件。

       不要认为管道传送是一个一个地运行两个命令,Linux系统实际上同时运行两条命令,并在系统内部把它们连接在一起,第一条命令生成输出时,输出就立即发送给第二条命令,没有使用中间文件或者缓冲区来传送数据。

8.7 学计算

8.7.1 expr命令

       expr命令允许处理命令行中的等式,但是很笨拙,也能够区分一些不同的数学操作符和字符串操作符。

       例如,如果计算5*6,需要为expr 5 \* 6 ,而不能是expr 5*6,这会导致直接输出5*6,而中间用空格分开还必须防止*被误解,需要加上\

8.7.2 使用括号

       一种计算数学等式更简单的方法是,使用美元符号和方括号。例如a=$[5*8],当然这里就不用在担心*被误解的情况了。

8.7.3 浮点解决方案

       比较普遍的解决浮点的方案是使用内置的bash计算机,即bcBash计算器其实是一种编程语言,该语言允许在命令行中输入浮点表达式,然后解释表达式并计算它们,最后返回结果。Bash计算器可以识别:

l  数字(整数和浮点);

l  变量(简单变量和数组);

l  注释(以#符号开始的行或C语言的/**/对)

l  表达式;

l  编程语句(如if-then语句);

l  函数。

 

浮点运算被称为scale的内置变量控制,默认值为0,所以你计算1/3将为0,需要设置scale=N,然后1/3的精度就为小数点后N位。

       bc –q可以禁止bash计算器的冗余欢迎标语。

在脚本中使用bc

#!/bin/bash

var1=`echo “ scale=4;3.44/5” | bc`

echo The answer is $var1

对于简单的计算还好,但是如果计算很复杂,我们可以使用内置的输入重定向方法,而不使用文件重定向。例如:

#!/bin/bash

var1=10.46

var2=43.67

var3=33.2

var4=71

 

var5=`bc <<EOF

scale = 4

a1 = {$var1 & $ var2}

b1 = {$ var3 * $ var4}

a1+b1

EOF

`

echo The final answer for this message is $var5

       使用该种技术,可以在脚本中把每个操作和表达式放在单独的行中,使其更加清晰并便于阅读。

8.8 退出脚本

       在我们前面的脚本中,执行完最后一条命令,就终止了脚本,其实我们可以用一种更优雅的方式来完成。      

8.8.1 核对退出状态

       Linux提供$?特殊变量来保存最后一条命令执行结束的退出状态,如果想核对一条命令的退出状态,必须在这条命令运行完成之后立即查看或使用变量$?。它会改变为shell执行的最后一条命令的退出状态值。

       按照惯例,一条命令成功完成的退出状态时0。如果命令执行错误,那么退出状态会是一个正整数。范围在0~255

       Linux退出状态代码:

代码

描述

0

命令成功完成

1

通常的未知错误

2

误用shell命令

126

命令无法执行

127

没有找到命令

128

无效的退出参数

128+x

使用Linux信号x的致命错误

130

使用Ctrl-C终止的命令

255

规范外的退出状态

8.8.2 退出命令

       我们可以使用exit N命令来指定一个退出状态,不过如果N超过了255,那么将返回一个对256取模以后的值。