C提高~函数及库函数

函数本质

为什么会有函数?

     在复杂程序中,整个程序被分成了多个源文件,一个文件分成多个函数,一个函数分成多个语句,这就是整个程序的组织形式。函数的出现是人(程序员和架构师)的需要,而不是机器(编译器、CPU)的需要。函数目的就是实现模块化编程,既让代码的可读性好,又方便分工,利于程序的组织

函数书写一般原则

  1. 遵循一定格式:函数返回类型、函数名、参数列表等;
  2. 一个函数只做一件事:函数不能太长,也不宜太短;
  3. 传参不宜过多:在ARM体系下,传参不宜超过4个;若传参需要超过4个最好考虑结构体打包;

函数是动词、变量是名词

 

函数的实质是数据处理器

 

函数的基本使用

函数三要素:定义、声明、调用

函数定义时函数的根本,函数名表示该函数在内存中的首地址,所以可用函数名来调用执行该函数(实质是指针解引用访问?);函数定义中的函数体是函数的执行的关键,函数将来执行时主要是执行函数体

函数声明作用是告诉编译器函数的原型

函数原型和作用

main函数

在定义一个函数时,一般给函数设计了输入和输出,函数的形参是函数的输入,返回值是函数的输出main函数很特殊,C语言规定了main函数是整个程序的入口,总是从main函数开始执行的,其他函数只有直接或间接被main函数调用才能被执行。其设计原则把函数作为程序的构成模块,main函数就像是我们搭积木的主体,函数就是一块积木。

main函数返回值

单片机的C语言代码中,main函数常常以void main(void)形式出现,单片机的编译器是允许该种形式出现,其C语言不是标准的C99,。在实际项目中,应该按照标准的形式:当你把程序从一个编译器移动另一个编译器时,照样能正常工作

通常main函数最后是return 0,0就是main函数的返回值,返回0说明程序正常执行,程序运行结束,返回非0说明程序异常。所以main函数中返回值的意义,不仅仅是一个返回值,还说明结束程序,程序是否运行正常

谁调用了main函数

在Linux系统中,使用./xxx执行一个可执行程序,也可通过shell脚本来调用执行一个程序,还可在程序中去调用一个程序(fork exec),但本质都相同。Linux中一个新程序的执行就是一个进程的创建、加载、运行和消亡。Linux中执行一个程序其实就是创建一个新的进程,然后把程序丢进这个进程中去执行知道结束。进程(除了三个特殊的进程之外)都是被它的父进程调用fork函数创建出来的。命令行本身就是一个进程,在命令行下执行./xxx方式执行一个新的程序,该新程序是作为命令行进程的一个子进程执行。main函数的返回值最终会返回给父进程,父进程判断子进程(新程序)是执行成功了还是执行失败了,做出要不要复活子进程继续运行等的决定。

main函数的传参

在Linux C中,main函数可以传参,也可以不传参,int main(void)形式就是没给main函数传参。但有时希望程序具有一定的灵活性选择在执行程序时通过传参来控制程序的运行,达到不需要重新编译程序就可以改变程序的运行结果的目的。

主函数main函数的第一个参数argc(argument count)是命令行中的字符串个数,即程序运行时非main函数传递参数的个数。第二个参数argv(argument value)是一个指向字符串的指针数组,命令行中的每一个字符都会被存储到内存中,并且分配一个指针指向它argv[0]是指给main函数的第一个传参及程序的名字argv[1]是给main函数传的第二个参数

#include<stdio.h>
#include<string.h>
#include<stdio.h>
#include<string.h>

int main(int argc,char *argv[]){
  int i = 0;
  printf("main函数传参个数是:%d.\n",argc);
  for (i=0;i<argc;i++){
    printf("第%d个参数是%s.\n",i,argv[i]);
  }

  return 0;
}

运行结果

./main_test 
main函数传参个数是:1.
第0个参数是./main_test.

./main_test 12
main函数传参个数是:2.
第0个参数是./main_test.
第1个参数是12.

./main_test "asd"
main函数传参个数是:2.
第0个参数是./main_test.
第1个参数是asd.

在main传参,需要注意一下几点,否则容易出现问题:

  1. main()函数传参都是通过字符串传进去的;
  2. main()函数只有被调用时传参各个参数(字符串)之间是通过空格来间隔的;
  3. 在程序内部如果要使用argv,那么一定要先检验argc

递归函数

函数调用机制

C语言函数的调用在x86平台一般是用栈的方式来支持其操作(CallingConvention)。当函数发生调用时,函数以入栈的方式,将函数的返回地址、参数等进行压栈。C语言默认环境下调用规范为,参数从右向左一次压栈(如printf函数),这就是函数的调用机制。同时函数每调用一次,就会进行一次压栈,其所占的空间彼此独立调用函数和被调用函数依靠传入参数和返回值彼此联系

如一个main()函数调用sub(int a, int b)简单内存图形(待替换)如下:

                int a
                int b                    入
        sub()返回地址       栈
            main参数       
        main()返回地址
                                           栈
 

递归函数

 

使用递归原则:收敛性、栈溢出

 

递归与循环区别

 

库函数

函数库本身不是C语言的一部分,是一些事先写含的函数的集合,给别人复用。函数是模块化的,因此可以被复用。常见的输入函数scanf和输出函数printf,就是所谓的库函数,即标准输入输出库函数。在stdio.h头文件中给出了相应函数声明,可直接调用。

函数库提供形式:静态链接库和动态链接库

静态库是商业公司将自己的函数库源代码经过只编译不链接形成.o的目标文件,然后用ar工具将.o文件归档成.a的归档文件,即静态链接库文件。用户通过.a库文件和.h头文件,在.c文件中直接调用这些库文件,在链接的时候链接器会去.a文件中找到对应的.o文件,链接后最终形成可执行程序。

动态链接库(共享库相比静态链接库效率更高,动态链接库不是将库函数的代码直接复制进可执行程序中,只是做个链接标记,当应用程序在内存中执行,运行时环境发现它调用了一个动态库中的库函数时,会加载该动态库到内存中,无论有多少个应用程序在同时使用该函数,该库函数在内存中只有一份

库函数使用

 

字符串库函数

字符串是由多个字符在内存中连续分布组成的字符结构,字符串的特点是指定了开头(字符串的指针)和结尾(结尾固定为字符'\0')而没有指定长度(长度由开头地址和结尾地址相减得到)。

man手则使用

 

常见字符串处理函数

C语言库中字符串处理函数的各种声明包含在string.h中,文件存放在如ubuntu系统中/usr/include中,函数有memcpy、memmove、memset....

链接是加-lm

GCC时加-lm参数是告诉链接器到libm中去查找用到的函数,使用gcc math.c -lm命令编译,此时编译则通过,并生成可执行文件。实战中发现在高版本中的GCC中,经常出现没加-lm也可编译链接,因为GCC自动寻找到了需要的库文件。

制作静态链接库

使用gcc -o只编译不连接,生成.o文件,然后使用ar工具打包成.a归档文件。库名不能随便乱起,一般是lib+库名称,后缀名.a表示是一个归档文件。

gcc demo.c -o demo.o -c
ar -rc libdemo.a demo.o
gcc 11_gab_var.c -o 11_gab_var.o -c
ar -rc lib11_gab_var.a 11_gab_var.o

制作头文件demo.h,只需声明函数即可;

void func1(void);
int func2(int a,int b);

注意:制作出静态库之后,发布时需要发布.a文件和.h文件

使用静态链接库

把.a和.h都放在需要引用的文件夹下,然后在编写.c文件中包含库的.h,然后直接使用库函数。

gcc test.c -o test -ldemo -L.

注意

默认连接库里没有我们的库文件,需要告诉gcc去哪里去找我们的库文件;

使用-lxxxx时,链接器试图去默认是链接库路径去寻找libxxx.a文件,但我们把libdemo.a放在了当前路径,不在默认链接库的目录下, 所以找不到-L是指定链接器在哪个目录下寻找库文件句点表示当前目录

制作动态链接库

Linux下动态链接库的后缀名是.so,Windows系统则为.dll

gcc demo.c -o demo.o -c -fPIC
gcc -o libdemo.so demo.o -shared

-fPIC是位置无关码,-shared是按照共享库的方式来链接。发布动态链接库时,发布libxxx.so和xxx.h即可。

使用自己制作的共享库

gcc test.c -o test -ldemo -L.

编译通过,运行时报错,error while loading shared libaries:libdemo.so :cannot open shared object file.

原因是动态链接库运行时需要被加载(运行时唤醒在执行test程序的时候发现动态链接了libdemo.so,于是回去固定目录尝试加载libdemo.so

解决方法一将libdemo.so方到固定目录下就可以了,固定目录一般为/usr/lib目录

cp libdemo.so /usr/lib

解决方法二使用环境变量LD_LIBR ARY_PATH。将libdemo.so所在目录添加到环境变量LD_LIBR ARY_PATH中即可。

如/mnt/hgfs/Winshare/object/sotest目录就是动态库lxxx.so文件所在位置,执行一下语句

export LD_LIBR ARY_PATH = $LD_LIBR ARY_PATH:/mnt/hgfs/Winshare/object/sotest

注意:添加完动态库之后,可以用ldd命令查看共享库是够能被加载并被解析

[root@comput3 test]# ldd 11_gab_var
	linux-vdso.so.1 =>  (0x00007ffeb3ada000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f5b0c647000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f5b0c279000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f5b0c863000)

由上知,11_gab_var.so文件没有被找;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值