<一>数学函数
在数学中我们用过sin和ln这样的函数,例如sin(π/2)=1,ln1=0等等,在C语言中也可以使用这些函数(ln函数在C标准库中叫做log):
它有六种基本函数(初等基本表示):三角函数数值表(斜边为r,对边为y,邻边为x。)   在平面直角坐标系xOy中,从点O引出一条射线OP,设旋转角为θ,设OP=r,P点的坐标为(x,y)有   
正弦函数 sinθ=y/r 正弦(sin):角α的对边 比 斜边   
余弦函数 cosθ=x/r 余弦(cos):角α的邻边 比 斜边   
正切函数 tanθ=y/x 正切(tan):角α的对边 比 邻边   
余切函数 cotθ=x/y 余切(cot):角α的邻边 比 对边   
正割函数 secθ=r/x 正割(sec):角α的斜边 比 邻边   
余割函数 cscθ=r/y 余割(csc):角α的斜边 比 对边
Sin(π/2)=y/r=1,因为y=1;r=1;
更详细了解三角函数:http://baike.baidu.com/view/91555.htm
Log对数的概念
英语名词:logarithms。如果a^b=n,那么log(a)(n)=b。其中,a叫做“底数”,n叫做“真数”,b叫做“以a为底的n的对数”。 log(a)(n)函数叫做对数函数。对数函数中n的定义域是n>0,零和负数没有对数;a的定义域是a>0且a≠1。
详细了解log函数:http://baike.baidu.com/view/426996.htm
ln(x)是以e为底的x的对数。   
f(x)=lnx的导函数为f'(x)=1/x.   
ln(a)+ln(b)=ln(a*b)   
ln(a)-ln(b)=ln(a/b)
编写C语言中使用数学函数
#include <math.h>
#include <stdio.h>
int main()
{
 double pi =3.1416;
 printf("sin(pi/2)=%f\nln1=%f\n", sin(pi/2), log(1.0));
 return 0;
}
现在我们可以完全理解printf语句了:原来printf也是一个函数,上例中的printf("sin(pi/2)=%f\nln1=%f\n", sin(pi/2), log(1.0))是带三个参数的函数调用,而函数调用也是一种表达式,因此printf语句也是表达式语句的一种,我们调用printf不是为了得到它的返回值,而是为了利用它所产生的副作用(Side Effect)--打印。C语言的函数可以有Side Effect,这一点是它和数学函数在概念上的根本区别。
程序第一行的#号(Pound Sign,Number Sign或Hash Sign)和include表示包含一个头文件(Header File),后面尖括号(Angel Bracket)中就是文件名(这些头文件通常位于/usr/include目录下)。头文件中声明了我们程序中使用的库函数,根据先声明后使用的原则,要使用printf函数必须包含stdio.h,要使用数学函数必须包含math.h,如果什么库函数都不使用就不必包含任何头文件,例如写一个程序int main(void){int a;a=2;return 0;},不需要包含头文件就可以编译通过,当然这个程序什么也做不了。使用math.h中声明的库函数还有一点特殊之处,gcc命令行必须加-lm选项,因为数学函数位于libm.so库文件中(这些库文件通常位于/lib目录下),-lm选项告诉编译器,我们程序中用到的数学函数要到这个库文件里找。
<二>自定义函数
main()主函数就是一个典型例子。
我们对照着main函数的定义来看语法规则:
#include <stdio.h>
int main(void) /*函数定义 → 返回值类型 函数名(参数列表) 函数体*/
{                                 /*函数定义 → 返回值类型 函数名(参数列表) 函数体
 int hour = 11;             *函数体 → { 语句列表 }
 printf("%d\n",hour);       *语句列表 → 语句列表项 语句列表项 ...
 return 0;     *语句列表项 → 语句
}       * 语句列表项 → 变量声明、类型声明或非定义的函数声明
       *非定义的函数声明 → 返回值类型 函数名(参数列表);
       */

关于main函数需要注意两点:
(1)书上的main函数定义写成main(){...}的形式,不写返回值类型也不写参数列表,这是Old Style C的风格
(2)其实操作系统在调用main函数时是传参数的,main函数最标准的形式应该是int main(int argc, char *argv[])
较简单的自定义函数

#include <stdio.h>
void newline(void)
{
 printf("\n");
}
void threeline(void)
{
 newline();
 newline();
 newline();
}
int main(void)
{
 printf("Three lines:\n");
 threeline();
 printf("Another three lines.\n");
 threeline();
 return 0;
}
通过这个简单的例子可以体会到:
1.同一个函数可以被多次调用。
2.可以用一个函数调用另一个函数,后者再去调第三个函数。
3.通过自定义函数可以给一组复杂的操作起一个简单的名字,例如threeline。对于main函数来说,只需要通过threeline这个简单的名字来调用就行了,不必知道打印三个空行具体怎么做,所有的复杂操作都被隐藏在threeline这个名字后面。
4.使用自定义函数可以使代码更简洁,main函数在任何地方想打印三个空行只需调用一个简单的threeline(),而不必每次都写三个printf("\n")。
 
执行编译时,先从主函数执行,
第一步:执行printf()函数,将字符串字面值输出,然后换行。
第二步:执行threeline()定义的函数,然后找到对应threeline()函数,执行第一个newline()函数,然后找到newline()函数,执行printf()函数输出换行。执行完以后返回threeline()函数,再执行第二个newline()函数。执行第三条语句原理是一样的。最后返回main()主函数,执行输出printf("Another three lines.\n");语句。调用第二次threeline()函数原理是一样的。执行完以后将0返回。

#include <stdio.h>
void newline(void);
void threeline(void);
int main(void)
{
 printf("Three lines:\n");
 threeline();
 printf("Another three lines.\n");
 threeline();
 return 0;
}
void newline(void)
{
 printf("?\n");
}
void threeline(void)
{
 newline();
 newline();
 newline();
}
如果注释掉声明void newline(void);和void threeline(void);,但仍然能编译通过,运行结果也对。这里涉及到的规则称为函数的隐式声明(Implicit Declaration),在main函数中调用threeline时并没有声明它,编译器认为此处隐式声明了int threeline(void);,隐式声明的函数返回值类型都是int,由于我们调用这个函数时没有传任何参数,所以编译器认为这个隐式声明的参数类型是void,这样函数的参数和返回值类型都确定下来了,编译器根据这些信息为函数调用生成相应的指令。然后编译器接着往下看,看到threeline函数的原型是void threeline(void),和先前的隐式声明的返回值类型不符,所以报警告。好在我们也没用到这个函数的返回值,所以执行结果仍然正确
<三>形参和实参
我们需要在函数定义中指明参数的个数和每个参数的类型,定义参数就像定义变量一样,需要为每个参数指明类型,参数的命名也要遵循标识符命名规则
#include <stdio.h>
void max(int x,int y)
{
 if(x>y)
 {
  printf("%d\n",x);
 } 
 else
 {
  printf("%d\n",y); 
 }
}
int main(void)
{
 max(3,4);
 return 0;
}
需要注意的是,定义变量时可以把相同类型的变量列在一起,而定义参数却不可以,例如下面这样的定义是错的:
void max(int x, y)
{
 if(x>y)
 {
  printf("%d\n",x);
 } 
 else
 {
  printf("%d\n",y); 
 }
}
 
记住这条基本原理:形参相当于函数中定义的变量,调用函数传递参数的过程相当于定义形参变量并且用实参的值来初始化。
#include <stdio.h>
void max(int x,int y)
{
 if(x>y)
 {
  printf("%d\n",x);
 } 
 else
 {
  printf("%d\n",y); 
 }
}
int main(void)
{
 int a=3,b=4;
 max(a,b);
 return 0;
}
1、定义一个函数increment,它的作用是把传进来的参数加1。例如:
#include <stdio.h>
void increment(int x)
{
 x = x + 1;
}
int main(void)
{
 int i = 1, j = 2;
 increment(i); /* i now becomes 2 但本身参数i没有改变*/
 increment(j); /* j now becomes 3 */
 printf("%d\n",i);
 printf("%d\n",j);
 return 0;
}
我们在main函数中调用increment增加变量i和j的值,这样能奏效吗?为什么?
#include <stdio.h>
void increment(int *x) /*定义一个整型指针参数,装参数地址*/
{
        *x = *x + 1;
}
                                                                    
int main(void)
{
        int i = 1, j = 2;
        increment(&i); /* i now becomes 2,本身i参数改变 */
        increment(&j); /* j now becomes 3 */
        printf("%d\n",i);
        printf("%d\n",j);
        return 0;
}
2、如果在一个程序中调用了printf函数却不包含头文件,例如int main(void) { printf("\n"); },编译时会报警告:warning: incompatible implicit declaration of built-in function ‘printf’。请分析错误原因。
printf是个输出函数,声明头文件"#include <stdio.h>"的作用就是用来提供这些信息的stdio.h是C编译系统提供的一个文件名,stdio是"standard input & output"的缩写,即有关标准输入输出的信息。执行main()函数,有个输出函数,找不到系统的标准输入输出函数头文件,因此会报警告。
<4>全局变量、局部变量和作用域
我们把函数中定义的变量称为局部变量(Local Variable),由于形参相当于函数中定义的变量,所以形参也是一种局部变量。在这里“局部”有两层含义
1、一个函数中定义的变量不能被另一个函数使用。例如max中的x和y在main函数中没有定义,不能使用,同样main函数中的局部变量也不能被max函数使用。如果这样定义:
#include <stdio.h>
void max(int x,int y)
{
 if(x>y)
 {
  printf("%d\n",x);
 } 
 else
 {
  printf("%d\n",y); 
 }
}
int main(void)
{
 int a=3,b=4,x=5,y=6;
 max(a,b);
 return 0;
}
2、每次调用函数时局部变量都表示不同的存储空间。局部变量在每次函数调用时分配存储空间,在每次函数返回时释放存储空间,例如调用max(23, 59)时分配x和y两个变量的存储空间,在里面分别存上23和59,函数返回时释放它们的存储空间,下次再调用max(12, 20)时又分配x和y的存储空间,在里面分别存上12和20
全局变量和局部变量
#include <stdio.h>
int a =8,b =9;
void aa()
{
 printf("%d %d\n",a,b);
 
}
int main(void)
{
 int a= 3;
 aa();
 printf("%d %d\n",a,b); 
 return 0;
}
全局变量任何函数都可以访问,程序运行访问全局变量与书写全局变量顺序有些不同,据有前辈介绍大型项目有些Bug往往是对全局变量读写顺序不正确而引起的,找Bug困难。因此建议:虽然全局变量用起来很方便,但一定要慎用,能用函数传参代替的就不要用全局变量。在声明主函数前先声明全局变量a=8,b=9,在main()主函数定义一个a=3局部变量,在main()函数中a的参数为3,其他函数的参数都使用全局变量8,没有声明局部变量的变量,参数的值都是从全局变量定义中获取。但要注意一点:局部变量可以用类型相符的任意表达式来初始化,而全局变量只能用常量表达式(Constant Expression)初始化
 
参考《Linux C编程一站式》学习总结,大部分内容来自《Linux C编程一站式》