C编程深入浅出_第1页
C编程深入浅出_第2页
C编程深入浅出_第3页
C编程深入浅出_第4页
C编程深入浅出_第5页
已阅读5页,还剩16页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、C编程深入浅出说在前头本PPT内容结合开发所遇到的一些知识的总结,并非C语言基础or高级编程何谓僵尸进程?FF017% ps elf 第二列字符为Z的即是僵尸0 Z sdmowner 13226 13223 0 0 - - 0 - - ? 0:00 退出一个进程用系统调用exit,但是这并不意味着该进程马上就消失了,事实上它还留下了一个被称为僵尸进程(Zombie)的数据结构(PCB,进程控制块,系统为了管理进程设置的一个专门的数据结构,用于描述进程情况及控制进程运行所需的全部信息 )当一个进程已退出,但其父进程还没有调用系统调用wait对其进行收集之前的这段时间里,它会一直保持僵尸状态 何谓

2、僵尸进程#include #include int main() pid_t pid, child = fork(); if ( child = 0 ) if(pid = fork()) = -1) perror(fork failed.n); exit(1); else if( pid = 0) /子进程退出 puts(This is the son.n); exit(1); else puts(“This is new father.n”); while(1); /新进程死循环 else /父进程退出 exit(1); 这个程序中有三个进程,主进程,新进程(父进程)和他的子进程。主进程创建

3、了新进程(父进程)之后立刻就退出了,他的资源会被init进程回收,现在只剩 下了新进程(父进程)和它创建的子进程,而这个新进程(父进程)创建的子进程输出了一些信息之后就立刻就退出了,就只剩下了新进程(父进程),可是这个父 进程并没有调用wait来回收它创建的子进程(虽然此时他的子进程已经退出了,但是仍在在进程表中占有一席之地,这个需要父进程用wait来回收),而此 时此刻,这个父进程仍在死循环中(while(1);),于是他的子进程便成为僵尸进程何谓僵尸进程FF017% ps -elf |grep a.out 0 O sdmowner 15606 1 49 99 20 ? 194 09:59:

4、42 pts/12 111:58 a.out 0 S sdmowner 16767 15611 0 40 20 ? 226 ? 11:53:04 pts/17 0:00 grep a.outFF017% ps -elf |grep Z F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD 0 S sdmowner 16763 15611 0 40 20 ? 226 ? 11:52:50 pts/17 0:00 grep Z 0 Z sdmowner 13226 1 0 0 - - 0 - - ? 0:00 0 Z sdmowner

5、 15607 15606 0 0 - - 0 - - ? 0:00 FF017%此时的僵尸进程要想将其杀掉,有两个方法,一个是重启机器,另一个就是变通的方法,杀掉它的父进程,当我们杀掉了它的父 进程之后,这个僵尸的父进程就会变为init(所有的进程,当它的父进程被杀掉之后,它父进程的父进程就会接管它,以此类推,直到最后由init来接管 它),而init进程是一个系统进程,它每隔一段时间就会用wait来回收这些没有父母的僵尸进程。在这里,当你用kill 15606杀掉它的父进程之后,你再用ps 命令就看不到它了 防止僵尸进程的方法就是要记得调用系统调用wait及时的将其回收。如果你不及时回收,那

6、么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序 main函数的背后solaris的可执行二进制文件的格式为ELF(Executable and Linkable Format )程序从main函数开始执行,但其实在进入main之前,还有另外一些工作要做。如果应用程序具有动态链接器的描述段,内核在完成程序段加载后,紧接着加载动态链接器,并且启动动态链接器的入口.如果没有动态链接器的描述段,就直接交给用户程序入口。 在控制权交给动态链接器的入口后,首先调用_dl_start函数获得真实的程序入口(注:该入口地址不是main的地址,

7、也就是说一般程序的入口不是main),然后循环调用每个共享object的初始化函数,接着跳转到真实的程序入口,一般为_start(程序中的_start)的一个例程,该例程压入一些参数到堆栈,就直接调用_libc_start_main函数。在_libc_start_main函数中替动态连接器和自己程序安排destructor,并运行程序的初始化函数。然后才把控制权交给main()函数。 main函数的背后FF017% cc -o hello hello.cFF017% readelf -h hello ELF Header:.Type:EXEC (Executable file)Entry po

8、int address: 0 x8048320 FF017% dbx -q hello (dbx) dis 0 x8048320Dump of assembler code for function _start:0 x8048320 : xor%ebp,%ebp0 x8048322 : pop%esi0 x804833c :call 0 x80482f8 End of assembler dump. int _libc_start_main (int argc, char *argv, char *envp, void *auxvec, void (*rtld_fini) (void), s

9、truct startup_info *stinfo, char *stack_on_entry) /* 执行了许多个步骤 */ /* Call the initializer of the program, if any.*/if (stinfo-init) stinfo-init (argc, argv, _environ, auxvec); /*运行程序main函数,到此,控制权才交给我们一般所说的程序入口*/exit (stinfo-main (argc, argv, _environ, auxvec); exit与return的区别exit是系统调用,exit运行时首先会执行由ate

10、xit()函数登记的函数,然后会做一些自身的清理工作,同时刷新所有输出流、关闭所有打开的流并且关闭通过标准 I/O函数tmpfile()创建的临时文件。exit是结束一个进程,它将删除进程使用的内存空间,同时把错误信息返回父进程return是语言级别的,它表示了调用堆栈的返回。如果在main函数中return n跟exit(n)基本是一样的效果 main函数并不是真正的可执行文件的入口点,它也是一个被调用的函数而已,如果在main里直接调用exit,那自然就直接结束了程序;而如果使用return,那么程序将返回到main被调用处,之后其实也是调用了exit来结束整个程序(见上一页的操作实例)

11、标准预定义宏 宏 名 作 用 _LINE_ 在源代码中插入当前源代码行号 _FILE_ 在源代码中插入当前源代码文件名 _DATE_ 在源代码中插入当前编译日期注意和当前系统日期区别开来 _TIME_ 在源代码中插入当前编译时间注意和当前系统时间区别开来标识符_LINE_和_FILE_通常用来调试程序;标识符_DATE_和_TIME_通常用来在编译后的程序中加入一个时间标志,以区分程序的不同版本 #include int main () printf(该输出行在源程序中的位置:%dn, _LINE_ ); printf(该程序的文件名为:%sn, _FILE_ ); printf(当前日期为:

12、%sn, _DATE_ ); printf(当前时间为:%sn, _TIME_ ); return 0;man手册的使用当不懂某个命令or库函数怎么使用时,我第一反应就是使用man手册,所以大家要习惯使用man手册,因为man手册的描述语言是最精准的,曾经有几个高难度bug都是因为没有精读man手册而导致。 FF017%man fork 所有的手册页都属于一个特定的领域,用一个字符来表示 1 用户命令, 可由任何人启动的。2 系统调用, 即由内核提供的函数。3 例程, 即库函数。4 设备, 即/dev目录下的特殊文件。5 文件格式描述, 例如/etc/passwd。7 杂项, 例如宏命令包、惯

13、例等。8 系统管理员工具, 只能由root启动l 本地文档, 与本特定系统有关的。执行man mkdir的话,缺省进入用户命令手册。直接指定特定领域内搜索手册页,man 2 mkdir 可以直接进入系统调用版的帮助指针与数组对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为0的元素的指针哪怕它们乍看上去是以数组下标进行运算的,实际上都是通过指针进行的 。c语言中只有一维数组而且数组的大小必须在编译期间就作为一个常数确定下来。c语言中数组的元素可以是任何类型的对象例子cmn_rnc_estinf2_t g_estinf2100016;cmn_rnc_estinf2_t

14、* p1;cmn_rnc_estinf2_t (*p2)16; /指针数组g_estinf24是g_t_xhf_rnc_estinf2数组的第5个元素, g_estinf24的行为也表现为一个有着16个cmn_rnc_estinf2_t元素的数组的行为。sizeof(g_estinf24) = 16sizeof(cmn_rnc_estinf2_t)P1 = g_estinf2; /非法P1 = g_estinf26; /合法P2 = g_estinf2; /合法指针与数组当数组名作为单目操作符&的操作数,取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针数组名的

15、值是一个指针常量,不能试图将一个地址赋值给数组名指针和数组并不总是相等的声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值是一个常量,指向这段空间的起始位置。声明一个指针变量时,编译器只为指针本身保留内存空间,它并不为任何整型值分配内存空间 int a5;int *b;表达式*a是合法的,*b却是非法的。*b将访问内存中某个不确定的位置,或者导致程序终止 例子a b指针与数组外部数组的声明如extern char a; /* 不能改写为指针的形式 */字符串常量char *p = this is a example; /char *pi = 3.14;

16、 /这样定义是错误的,无法通过编译 /p0 = T; /修改该字符串常量时,编译是没问题,但是运行时会出现异常 char a = gooseberry; strncpy( a, black, 5 );内存申请void GetMemory(char *p, int num) p = (char *)malloc(sizeof(char) * num);void Test(void) char *str = NULL; GetMemory(str, 100); / str 仍然为 NULL strcpy(str, hello); / 运行错误void GetMemory2(char *p, int

17、 num) *p = (char *)malloc(sizeof(char) * num);void Test2(void) char *str = NULL; GetMemory2(&str, 100); / 参数是 &str不是str strcpy(str, hello); printf(“%sn”,str); free(str);错对编译器总是要为函数的每个参数制作临时副本,指针参数p 的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p 的内容,就导致参数p 的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请了新的内存,只是把_p 所指的内存地址

18、改变了,但是p 丝毫未变。所以函数GetMemory并不能输出任何东西。每执行一次GetMemory 就会泄露一块内存,因为没有用free static用法static 局部变量,局部量声明为static的目的是保留它的值,当程序反复进入这个局部域(例如一个子程序),上次(调用子程序)的static量的结果,还保存在那里,而不像动态分配的量,退出局部域(退出子程序)它的量就消失 static声明了的局部,它的生命期与程序运行存在时间一样长。 static 全局变量,表示该变量只在本文件内使用static 函数,表示该函数只在被文件内使用char * commI_Get_coldata2( ch

19、ar *src,int position )int i;int len=0;int clm_len=0;int clm_cnt=1;static char buf256+1; const用法const 常量,该变量不允许被改变,定义时就要初始化const 函数形参,表示该形参是个输入形参,在函数体内不能被改变int FindNum(const int array, int num, int conut) array0 = 1 ; /编译器就会报错会报错const 指针,让指针本身为const const int *p = a;/a是一个数组的首地址.p是指向常量的指针int * const p

20、 = a; /a是一个数组的首地址.p是指针常量;const int * const p = a;/a是一个数组的首地址。p是指向常量的指针常量 const 指针太繁杂,不解释,不鼓励使用strlen/sizeof求字符串长度char str = 0123456789; printf( sizeof(str) = %dn, sizeof(str) ); /长度为11,包括字符串后面的0 char *str1 = hello; char str2=01234567; if(strlen(str1) - strlen(str2) = 0) printf(“yes!n”); /会输出yes! else printf(NO!n); if语句的结果将永远是真的。strlen的结果是无符号数,所以操作符 = 左边的表达式也将是无符号数,而无符号数决不可能是负的。 所以下面两个if语句不等同:if( strlen(str1) = 10 )if( strlen(str1) - 10 = 0 ) 解决方法就是显式类型转换。(int)strlen()、(int)sizeof() sizeof()也是返回无符号整数,要注意!段错误例外() 可能导致段错误的常见编程错误是:1、坏指针错误:

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论