C语言—指针注意项

在讲解指针之前,需要理解一下内存空间。内存是随机存取器,计算机上电后便利用内存进行运转。其有一定的容量,为了标识每个存储单元的位置,我们为内存设置了内存地址。内存的具体组织结构可以参考计算机组成原理。 指针是一种指向某种类型的特殊的型别。一般用*定义。如int *p,这样就定义了一个指向int类型的指针。指针用于指向某块内存空间,该内存空间里面存放了其所指向的内存地址。所以,在使用指针之前,必须明白这指针指向了什么内存空间,给指针赋值可以使用取地址符(&),如p = &a;每次需要访问指针所指向的内容时,便使用解引用符号*进行访问。如:printf(“%d”,*p); 上面简单介绍了下指针的定义,赋值等操作。下面介绍下一些新手容易迷糊的地方吧。 1. char *str1=”abcd”; char str2[]=”abcd”;的区别 C标准没有规定这两种定义字符串的方式的差别。但是,指针类型的字符串一般不允许修改。如:str1[0]=’c’;这样的语句会导致运行时错误。错误类型:不允许写入什么的。据说,在某些编译器中可以设置成可以修改的。 2.指针/数组作为参数进行传递 在C语言中,参数是使用值传递的。 int func(int a );当调用者调用该函数的时候将传递一个值给a,这个a只是你传递进去的参数的一个副本。而数组传递的时候,会退化为指针,其将数组的首地址复制给形参。看下面的一个例子。  void fun(char str[])     {        printf("After transform:%d\n", sizeof(str));     }     int main(){      char strs[]="abcdefg";      printf("Before transform:%d\n",sizeof(strs));     fun(strs);     return 0; } 输出: Before transform:8 After transform:4在传递之前,我们可以获得数组所占用的内存空间的大小;而传递后,我们只能获得一个指针的大小了。 许多初学C指针的同学想在函数内部修改作为参数传递进来的指针的值。看以下代码。 typedef struct Link_Node{     int Elem;     struct Link_Node *next; }LinkNode,*PLinkList; void CreateList(LinkNode *header) {     int i=0;     header = (LinkNode *)malloc(sizeof(LinkNode));     header->Elem = 10;     header->next = NULL; } int main() {     PLinkList head=NULL;     CreateList(head);     if(head!=NULL)         printf("%d\n",head->Elem);         free(head);     return 0; }   许多人疑惑为什么没有输出呢?请各位谨记:C语言使用值传递进行参数传递。这就是问题所在。在传递指针的时候,调用者传递了原指针所指向的内存地址给形参。也就是说,在传递参数的时候,系统定义了另外一个指针,也就是我们在此定义的header指针。我们把原指针的值传递给了header指针。所以header指向了本例中的null。然后我们去修改header所指向的值。当函数结束后,这个指针的生命周期也结束了。所以对main函数中的head没有任何修改。 下面我们再看一例。 void Trans(int* Arr,int nLength) {     for(int i=0;i<nLength;i++)            Arr[i] += i+20; } int main() {     int nArr[5]={0};     int  i;     printf("Before:\n");     for(i=0;i<5;i++)         printf("%d  ",nArr[i]);     Trans(nArr,5);     printf("\nAfter\n");     for(i=0;i<5;i++)         printf("%d  ",nArr[i]);     return 0; } 输出: Before: 0 0  0  0  0 After 20  21  22  23  24我们发现我们在函数里面修改了数组的值。结合上面的例子,有新同学开始迷糊了。为什么上面的例子不能修改,为什么下面的例子就能修改了?由于指针存储的是地址值,所以初学比较迷惑。函数传递的是指针变量的值---即该指针所指向的变量的地址。所以,我们可以修改其指向的变量的值,而不能修改指针本身的内容了。为了能够修改指针本身的内容,我们需要传递指针本身的地址。所以在上面那例中,需要传递head指针本身的地址。代码如下: void CreateList(LinkNode **header) {     int i=0;     (*header) = (LinkNode *)malloc(sizeof(LinkNode));     (*header)->Elem = 10;     (*header)->next = NULL; } int main() {     PLinkList head=NULL;     CreateList(&head);     if(head!=NULL)         printf("%d\n",head->Elem); free(head);     return 0; } 动态开辟数组 指针可以用来分配内存,作为数组来使用。 开辟一维数组:int *pArr = (int*)malloc(10*sizeof(int));释放空间:free(pArr); 开辟二维或者多维数组需要分级申请内存:int **pArr; pArr = (int **)malloc(sizeof(int*)*3); for(i=0;i<3;i++) *(pArr+i) = (int *)malloc(sizeof(int)*5); 当然释放空间的时候也需要分级释放咯; for(i=0;i<3;i++) free(*(pArr+i));函数指针和指针函数 在计算机内存中,所有的数据都有唯一的地址,当然可运行的程序也是有地址的。函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。定义形式为:函数类型 (*指针变量名)(形参列表);如:int (*func)(char a[],int nlength)。不过,我们经常会遇到这样的定义方式:typedef int (*func)(char a[],int nlength);其实使用typedef主要是为了定义函数指针方便,不需要每次都敲那么的代码。(其实懒也是技术驱动的一种动力啊!)我这里就简单的介绍点东西。在搞算法的时候,函数指针大有用处,可以用它实现很多beautiful算法。比如那些自动机等等。 typedef int (*func)(char a[],int nLength); int total(char a[],int nLength) {     int nTotal = 0;     for(int i=0;i<nLength;++i)         nTotal += a[i];     return nTotal; } int main() {     char a[]="abcde";     int nLength = strlen(a);     func fp;     fp = total;     printf("%d\n",fp(a,nLength));     return 0; } 一个函数不仅可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。在这里,我就不多说什么了。仅提醒各位初学者一下:返回的地址必须不能是该函数的栈空间里的。那样待函数返回后,获得到的地址将是一个错误的地址。

gcc,gdb安装和gdb基本调试命令

一.查看gcc版本 $gcc –v 二.卸载gcc 1.通过pkginfo找到gcc包 $ pkginfo |grep gcc system      SUNWgcc                          gcc - The GNU C compiler system      SUNWgccruntime                   GCC Runtime libraries 2.卸载 $pkgrm SUNWgcc $pkgrm SUNWgccruntime 其实安装高版本的gcc没必要卸载低版本的gcc. 三.安装gcc3.3.2 $gzip –d gcc-3.3.2-sol10-sparc-local.gz $pkgadd -d gcc-3.3.2-sol10-sparc-local 四.安装gdb-6.6-sol10-sparc 1.先安装gdb-6.6-sol10-sparc需要的库文件ncurses, libiconv, expat。 2.安装gdb-6.6-sol10-sparc 五.修改PATH变量,以增加/usr/local/bin #PATH= $PATH :/usr/local/bin #export PATH 六.gdb最基本的调试命令. 以调试程序esmmi为例: 1.gdb /export/home/ies600/esmmi 进入调试环境 2.b mmiview.cpp:12 在文件mmiview.cpp的第12行设置断点。 3.r –d 执行当前被调试的程序.-d是运行参数 4.p variable 打印数据内容 5.l 列出产生执行文件的源代码的一部分. 6.n 执行一行源代码但不进入函数内部。 7.s 执行一行源代码而且进入函数内部。 8.c 继续执行程序,直至下一中断或者程序结束。 9.k 终止正在调试的程序. 10.q 终止 gdb. 附注:基本gdb命令: ------------------------------------------------------------- 命令 简写 功能 file   装入想要调试的可执行文件. kill k 终止正在调试的程序. list l 列出产生执行文件的源代码的一部分. next n 执行一行源代码但不进入函数内部. step s 执行一行源代码而且进入函数内部. continue c 继续执行程序,直至下一中断或者程序结束。 run r 执行当前被调试的程序. quit q 终止 gdb. watch   使你能监视一个变量的值而不管它何时被改变. catch   设置捕捉点. thread t 查看当前运行程序的线程信息. break b 在代码里设置断点, 这将使程序执行到这里时被挂起. make   使你能不退出 gdb 就可以重新产生可执行文件. shell   使你能不离开 gdb 就执行 UNIX shell 命令. print p 打印数据内容。 examine x 打印内存内容。 backtrace bt 查看函数调用栈的所有信息。

php如何调用c语言编写的接口(转)

php程序写的时间长了,自然对他所提供的功能了如指掌,他所提供的一大堆功能,真是觉得很好用,但有时候会发现php也缺少一些功能,自己总是会产生为php添加一些自定义的功能的想法。久而久之,终于今天憋不住了,开始动手研究如何添加。 下载一个php的源代码包,这里使用的是php 4.0.5版,解压后会看到php的根目录下会有README.EXT_SKEL这样一个文件,打开详细阅读了一下,发现了一个非常好用的工具,这个工具可以帮你构建一个空的php扩展,然后你向里面添加相应的代码就可以完成你自己的功能扩展了。下面我们就来介绍如何使用这个工具。 首先转移你的目录到php的目录下的ext目录,如果你只需要一个基本的扩展框架的话,执行下面的命令: ./ext_skel --extname=module_name module_name是你自己可以选择的扩展模块的名字,例如我选择的my_module。执行工具后会自动在ext目录下建立你选择的module_name名字的目录,里面已经生成了相关的代码,这些代码中只需要调整config.m4文件中的三行注释就可以正常的编译带这个自定义扩展模块的php了。在php的根目录执行下列操作就可以得到。 ./buildconf ./configure --enable-module_name make 下面我来演示建立my_module扩展框架的全过程,为了更有效果,我们来完成一个php的扩展功能,在php中调用这个功能可以在web页面中显示hello world这个经典单词。 在php目录下的ext目录中,执行下面的命令 ./ext_skel --extname=my_module 得到反馈结果: Creating directory my_module Creating basic files: config.m4 Makefile.in .cvsignore my_module.c php_my_module.h tests/001.phpt my_module.php [done]. To use your new extension, you will have to execute the following steps: 1. $ cd .. 2. $ vi ext/my_module/config.m4 3. $ ./buildconf 4. $ ./configure --[with|enable]-my_module 5. $ make 6. $ ./php -f ext/my_module/my_module.php 7. $ vi ext/my_module/my_module.c 8. $ make Repeat steps 3-6 until you are satisfied with ext/my_module/config.m4 and step 6 confirms that your module is compiled into PHP. Then, start writing code and repeat the last two steps as often as necessary. 如果你能看懂上面的东西,那就照着去做。如果不是太明白的话,按照我下面的提示来做也可以。 Cd my_module 首先进入my_module目录 vi config.m4 使用文本编辑器打开config.m4文件,文件内容大致如下: dnl $Id$ dnl config.m4 for extension my_module dnl don't forget to call PHP_EXTENSION(my_module) dnl Comments in this file start with the string 'dnl'. dnl Remove where necessary. This file will not work dnl without editing. dnl If your extension references something external, use with: dnl PHP_ARG_WITH(my_module, for my_module support, dnl Make sure that the comment is aligned: dnl [ --with-my_module Include my_module support]) dnl Otherwise use enable: dnl PHP_ARG_ENABLE(my_module, whether to enable my_module support, dnl Make sure that the comment is aligned: dnl [ --enable-my_module Enable my_module support]) if test "$PHP_MY_MODULE" != "no"; then dnl If you will not be testing anything external, like existence of dnl headers, libraries or functions in them, just uncomment the dnl following line and you are ready to go. dnl Write more examples of tests here... PHP_EXTENSION(my_module, $ext_shared) Fi 根据你自己的选择将 dnl PHP_ARG_WITH(my_module, for my_module support, dnl Make sure that the comment is aligned: dnl [ --with-my_module Include my_module support]) 修改成 PHP_ARG_WITH(my_module, for my_module support, Make sure that the comment is aligned: [ --with-my_module Include my_module support]) 或者将 dnl PHP_ARG_ENABLE(my_module, whether to enable my_module support, dnl Make sure that the comment is aligned: dnl [ --enable-my_module Enable my_module support]) 修改成 PHP_ARG_ENABLE(my_module, whether to enable my_module support, Make sure that the comment is aligned: [ --enable-my_module Enable my_module support]) 一般我会选择后者,然后保存退出。如果你对vi文本编辑器的操作有困难的话,请参考相应的说明文章,这里就不再详细描述了。 Vi my_module.c 将文件其中的下列代码进行修改 /* Every user visible function must have an entry in my_module_functions[]. */ function_entry my_module_functions[] = { PHP_FE(say_hello, NULL) /* ß添加着一行代码 */ PHP_FE(confirm_my_module_compiled, NULL) /* For testing, remove later. */ {NULL, NULL, NULL} /* Must be the last line in my_module_functions[] */ }; 在文件的最后添加下列代码 PHP_FUNCTION(say_hello) { zend_printf("hello world\n"); } 保存文件退出 vi php_my_module.h 在文件中PHP_FUNCTION(confirm_my_module_compiled);一行前面添加下面的代码 PHP_FUNCTION(say_hello); 保存文件退出 退回到php的根目录下,执行下面的命令 ./buildconf ./configure --enable-my_module make 如果一切顺利的话,我们现在已经将扩展模块my_module编译到php里面了。我们编写下面的代码进行测试 <? Say_hello(); ?> 保存文件为say_hello.php 在php的根目录下运行 ./php –q say_hello.php 正常情况下会显示 hello world 表示我们的第一个扩展正常的运行了! 解释一下上面做的操作,ext_skel生成一些框下文件,我们需要修改以下文件 my_module.c 扩展模块的主程序 php_my_module.h 扩展模块的头文件 config.m4 配置文件 主程序中描述了php扩展模块的声明,模块中含有多少个函数,各个函数的作用,在phpinfo函数中显示什么内容,模块初始化做些什么,结束做些什么都会在这个文件里进行描述。我们在上面只是添加了一个函数say_hello,并且描述了say_hello函数的具体内容,调用zend_printf系统函数在php中打印字符串。 在对应的头文件中声明了say_hello这个函数,从而完成了我们预期的功能。下面我们会编写一个更复杂的扩展,创造一个带参数的php扩展函数,根据给入的参数,显示hello world, xxxx。Xxxx代表输入的字符串内容,例如我的名字yorgo。 Vi my_module.c 修改最后的say_hello函数内容如下: PHP_FUNCTION(say_hello) { zval **yourname; if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &yourname) == FAILURE) { WRONG_PARAM_COUNT; } zend_printf("hello world, %s\n", Z_STRVAL_PP(yourname)); } 存盘退出。 退回php的根目录,运行 make 修改say_hello.php为 <? Say_hello(“yorgo”); ?> 保存退出后运行 ./php –q say_hello.php 得出结果 hello world, yorgo 表示我们这次的修改也成功了,可以改变say_hello中的参数,看看动态的效果。 这里主要解释上面修改的函数内容,由于say_hello函数需要有参数引入,所以在my_module.c中的say_hello函数主要在进行参数的处理,将php中引用say_hello时所填写的参数内容正确的传递到my_module.c中的say_hello处理函数中。为此,程序中添加了这么几行。 zval **yourname; if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &yourname) == FAILURE) { WRONG_PARAM_COUNT; } zend_printf("hello world, %s\n", Z_STRVAL_PP(yourname)); 代码解释如下: zval **yourname; 初始化一个参数的指针 ZEND_NUM_ARGS() 得到传递过来得参数数量,并且判断如果不为1的时候表示有问题,报错。 zend_get_parameters_ex(1, &yourname) 将刚刚初始化的指针指向传递过来的参数,如果不成功则报错。 Z_STRVAL_PP(yourname) 处理指针指向的参数并获得实际存储的值。