user-0 提问: 关于结构体函数的调用问题（有些难度）

回复:
user: user-1
content: 引用 24 楼 weiran2009 的回复:[quote=引用 21 楼 bear234 的回复:] "我自认为自己大学时C语言还不错，然而工作才发现自己是多么的天真" "还有对cleanup函数的引用怎么那么复杂 (void (*)(void*))cleanup ，完全没见过啊"    呃，如果再告诉你我大学时计算机2、3、4级一次性通过年年辅导同学通过2级通过率100%阁下一定觉得更可笑了……[/quote]   计算机等级考试 == 0  计算机等级考试就是写99乘法口诀表的水平....... 我没有嘲笑你的意思   只不过你这两句话放在一起真的让我笑了~~~~~~~  如果你不是专门搞代码的  算我没说  否则，你还不需要看一些书然后亲自编一些东西才行

user: user-2
content: 引用 24 楼 weiran2009 的回复:[quote=引用 21 楼 bear234 的回复:] "我自认为自己大学时C语言还不错，然而工作才发现自己是多么的天真" "还有对cleanup函数的引用怎么那么复杂 (void (*)(void*))cleanup ，完全没见过啊"    呃，如果再告诉你我大学时计算机2、3、4级一次性通过年年辅导同学通过2级通过率100%阁下一定觉得更可笑了……[/quote] 计算机几级通过之后能写出如下程序： #include <stdio.h> char s[]="123 ab 4"; char *p; int v,n,k; void main() {     p=s;     while (1) {         k=sscanf(p,"%d%n",&v,&n);         printf("k,v,n=%d,%d,%d\n",k,v,n);         if (1==k) {             p+=n;         } else if (0==k) {             printf("skip char[%c]\n",p[0]);             p++;         } else {//EOF==k             break;         }     }     printf("End.\n"); } //k,v,n=1,123,3 //k,v,n=0,123,3 //skip char[ ] //k,v,n=0,123,3 //skip char[a] //k,v,n=0,123,3 //skip char[b] //k,v,n=1,4,2 //k,v,n=-1,4,2 //End.

user: user-0
content: 引用 21 楼 bear234 的回复:"我自认为自己大学时C语言还不错，然而工作才发现自己是多么的天真" "还有对cleanup函数的引用怎么那么复杂 (void (*)(void*))cleanup ，完全没见过啊"    呃，如果再告诉你我大学时计算机2、3、4级一次性通过年年辅导同学通过2级通过率100%阁下一定觉得更可笑了……

user: user-3
content: 《c和指针》里讲到怎么解析函数指针，你查阅下相关章节！

user: user-2
content: 引用 15 楼 weiran2009 的回复:[quote=引用 10 楼 zhao4zhong1 的回复:] 计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 多用小脑和手，少用大脑、眼睛和嘴，会更快地学会编程！ 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步Debug版对应汇编一行！ 单步Debug版对应汇编千行不如单步Release版对应汇编一行！  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  想要从本质上理解C指针，必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针，原本看似复杂的东西就会变得非常简单！ 指针即地址。“地址又是啥？”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认：  有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物；  而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。  不要企图依赖输出指针相关表达式...的值【比如printf("%p\n",...);或者cout<<...】来理解指针的本质， 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=(void *)(...);】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。  这辈子不看内存地址和内存值；只画链表、指针示意图，画堆栈示意图，画各种示意图，甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗？本人深表怀疑！ 这辈子不种麦不收麦不将麦粒拿去磨面；只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头，压面条，烤面包，……能从本质上理解面粉、理解面食吗？本人深表怀疑！！  提醒： “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 （Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。） 想要从本质上理解C指针，必须学习C和汇编的对应关系。” 不是一回事！  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！  电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  十字链表交换任意两个节点C源代码（C指针应用终极挑战）http://download.csdn.net/detail/zhao4zhong1/5532495 感谢前辈的指点，不过工作中还是要以解决问题为主，我现在采取的方法是：不理解就背下来，我看了网上推荐王爽《汇编语言》不错，我空余时间会看到。但不知道您说的多用小脑、少用大脑没有理解……有什么暗喻吗？[/quote] 你只需要实际按Alt+8进入汇编窗口，再按几次F11单步过一遍任何一个带参数的C函数调用过程对应的几条汇编指令，并理解这几条汇编指令都对哪些地址处的哪些字节值做了哪些修改一次，相信你以后就不会再到论坛来问有关函数参数传递的问题而是自己通过这个方法观察了。 理解和讨论之前请先学会如何观察！

user: user-4
content: 引用 10 楼 zhao4zhong1 的回复:计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 多用小脑和手，少用大脑、眼睛和嘴，会更快地学会编程！ 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步Debug版对应汇编一行！ 单步Debug版对应汇编千行不如单步Release版对应汇编一行！  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  想要从本质上理解C指针，必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针，原本看似复杂的东西就会变得非常简单！ 指针即地址。“地址又是啥？”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认：  有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物；  而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。  不要企图依赖输出指针相关表达式...的值【比如printf("%p\n",...);或者cout<<...】来理解指针的本质， 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=(void *)(...);】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。  这辈子不看内存地址和内存值；只画链表、指针示意图，画堆栈示意图，画各种示意图，甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗？本人深表怀疑！ 这辈子不种麦不收麦不将麦粒拿去磨面；只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头，压面条，烤面包，……能从本质上理解面粉、理解面食吗？本人深表怀疑！！  提醒： “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 （Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。） 想要从本质上理解C指针，必须学习C和汇编的对应关系。” 不是一回事！  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！  电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  十字链表交换任意两个节点C源代码（C指针应用终极挑战）http://download.csdn.net/detail/zhao4zhong1/5532495  Can't agree more!  我现在正在看CSAP， 从汇编的角度来理解，学到的东西比书上讲到的还多。

user: user-5
content: 引用 7 楼 weiran2009 的回复:[quote=引用 1 楼 luciferisnotsatan 的回复:] 如果是(int)cleanup， 知道这种是强转么？  (void (*)(void*))cleanup 这个是吧cleanup像转为一个指向一个参数为void*, 返回值为void的函数指针。  void anim_and_exit( void (*f)(void*,int), void(*e)(void*) ) 接受两个函数指针。  google下函数指针你就知道了 我看了C Primer Plus第14章讲到了函数指针，但问题在于：不仅这个例程比书上要复杂，并且函数调用的参数不明：您看： 函数原型： void cleanup( DataBlock *d )  void generate_frame( DataBlock *d, int ticks )  而调用是这样的： bitmap.anim_and_exit( (void (*)(void*,int))generate_frame,   (void (*)(void*))cleanup ); 如此一来，函数cleanup的形参 DataBlock *d， 以及generate_frame函数的形参 DataBlock *d, int ticks都怎么传递值的呢？这和书里面和网上讲解的相差很大……[/quote] bitmap.anim_and_exit( (void (*)(void*,int))generate_frame,                             (void (*)(void*))cleanup );这个调用中，相当于执行了c]bitmap.fAnim = generate_frame, c]bitmap.animExit = cleanup, 你想要知道generate_frame函数的形参 DataBlock *d, int ticks怎么传递值，你只需要查看 CPUAnimBitmap::fAnim 是怎么使用的就行了，例如CPUAnimBitmap*   bitmap = *(get_bitmap_ptr());         bitmap->fAnim( bitmap->dataBlock, ticks++ );

user: user-0
content: 引用 10 楼 zhao4zhong1 的回复:计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 多用小脑和手，少用大脑、眼睛和嘴，会更快地学会编程！ 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步Debug版对应汇编一行！ 单步Debug版对应汇编千行不如单步Release版对应汇编一行！  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  想要从本质上理解C指针，必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针，原本看似复杂的东西就会变得非常简单！ 指针即地址。“地址又是啥？”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认：  有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物；  而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。  不要企图依赖输出指针相关表达式...的值【比如printf("%p\n",...);或者cout<<...】来理解指针的本质， 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=(void *)(...);】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。  这辈子不看内存地址和内存值；只画链表、指针示意图，画堆栈示意图，画各种示意图，甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗？本人深表怀疑！ 这辈子不种麦不收麦不将麦粒拿去磨面；只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头，压面条，烤面包，……能从本质上理解面粉、理解面食吗？本人深表怀疑！！  提醒： “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 （Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。） 想要从本质上理解C指针，必须学习C和汇编的对应关系。” 不是一回事！  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！  电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  十字链表交换任意两个节点C源代码（C指针应用终极挑战）http://download.csdn.net/detail/zhao4zhong1/5532495 感谢前辈的指点，不过工作中还是要以解决问题为主，我现在采取的方法是：不理解就背下来，我看了网上推荐王爽《汇编语言》不错，我空余时间会看到。但不知道您说的多用小脑、少用大脑没有理解……有什么暗喻吗？

user: user-6
content: <span style="color: #FF0000;">dataBlock = d;</span>  放[code]里没标红，你看<span style="color: #FF0000;"> </span>就是我说的标红部分

user: user-6
content: 引用 8 楼 weiran2009 的回复:完整的CPUAnimBitmap结构体 struct CPUAnimBitmap {     unsigned char    *pixels;     int     width, height;     void    *dataBlock;     void (*fAnim)(void*,int);     void (*animExit)(void*);     void (*clickDrag)(void*,int,int,int,int);     int     dragStartX, dragStartY;      CPUAnimBitmap( int w, int h, void *d = NULL ) {         width = w;         height = h;         pixels = new unsigned char[width * height * 4];         dataBlock = d;         clickDrag = NULL;     }           void anim_and_exit( void (*f)(void*,int), void(*e)(void*) ) {         CPUAnimBitmap**   bitmap = get_bitmap_ptr();         *bitmap = this;         fAnim = f;         animExit = e;         // a bug in the Windows GLUT implementation prevents us from         // passing zero arguments to glutInit()         int c=1;         char* dummy = "";         glutInit( &c, &dummy );         glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA );         glutInitWindowSize( width, height );         glutCreateWindow( "bitmap" );         glutKeyboardFunc(Key);         glutDisplayFunc(Draw);         if (clickDrag != NULL)             glutMouseFunc( mouse_func );         glutIdleFunc( idle_func );         glutMainLoop();     }   从标红的这几处代码猜测，在回调animExit时，会把初始化CPUAnimBitmap对象时传的 d，作为参数传给cleanup。

user: user-0
content: 引用 5 楼 ri_aje 的回复:这是 cuda 改过的 c 语言，(void (*)(void*))cleanup 就是强制类型转换，(...) 里面的是目标类型。 __global__ 还有 <<<...>>> 等都是 cuda 自己的私货，根本不是标准 c 的东西。  想必阁下也是看过《CUDA实战》的GPU前辈吧，我看到main函数开头两句话很有意思：     CPUAnimBitmap  bitmap( DIM, DIM, &data );     //结构体实例data里面有CPUAnimBitmap的数据类型        data.bitmap = &bitmap; // data.bitmap又引用了自己含有引用自己地址的CPUAnimBitmap数据类型bitmap的地址  感觉绕来绕去挺麻烦的，前辈可以指点一二吗？

user: user-6
content: 引用 7 楼 weiran2009 的回复:[quote=引用 1 楼 luciferisnotsatan 的回复:] 如果是(int)cleanup， 知道这种是强转么？  (void (*)(void*))cleanup 这个是吧cleanup像转为一个指向一个参数为void*, 返回值为void的函数指针。  void anim_and_exit( void (*f)(void*,int), void(*e)(void*) ) 接受两个函数指针。  google下函数指针你就知道了 我看了C Primer Plus第14章讲到了函数指针，但问题在于：不仅这个例程比书上要复杂，并且函数调用的参数不明：您看： 函数原型： void cleanup( DataBlock *d )  void generate_frame( DataBlock *d, int ticks )  而调用是这样的： bitmap.anim_and_exit( (void (*)(void*,int))generate_frame,   (void (*)(void*))cleanup ); 如此一来，函数cleanup的形参 DataBlock *d， 以及generate_frame函数的形参 DataBlock *d, int ticks都怎么传递值的呢？这和书里面和网上讲解的相差很大……[/quote] 所以用了强转，就和memset一样，void *memset(void *s, int ch, size_t n); 第一个参数是void *，只不过用这个函数你不用显示强转第一个参数。  (void (*)(void*))cleanup bitmap.anim_and_exit( (void (*)(void*,int))generate_frame,   (void (*)(void*))cleanup );  cleanup就是个函数的起始地址，然后强转就是把这个地址看成(void (*)(void*))来用，至于anim_and_exit能不能用对这个cleanup，就是具体问题具体分析了。对于这个anim_and_exit，它回调cleanup时是不是传个DataBlock结构给你，你就要自己去看说明文档/相关代码了。   对于编译器来说，你强转什么都没问题。至于能不能运行，会不会有问题，那是另一件事了 bitmap.anim_and_exit( (void (*)(void*,int))generate_frame,   (void (*)(void*))main ); bitmap.anim_and_exit( (void (*)(void*,int))generate_frame,   (void (*)(void*))0 );

user: user-2
content: 计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 多用小脑和手，少用大脑、眼睛和嘴，会更快地学会编程！ 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步Debug版对应汇编一行！ 单步Debug版对应汇编千行不如单步Release版对应汇编一行！  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  想要从本质上理解C指针，必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针，原本看似复杂的东西就会变得非常简单！ 指针即地址。“地址又是啥？”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认：  有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物；  而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。  不要企图依赖输出指针相关表达式...的值【比如printf("%p\n",...);或者cout<<...】来理解指针的本质， 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=(void *)(...);】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。  这辈子不看内存地址和内存值；只画链表、指针示意图，画堆栈示意图，画各种示意图，甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗？本人深表怀疑！ 这辈子不种麦不收麦不将麦粒拿去磨面；只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头，压面条，烤面包，……能从本质上理解面粉、理解面食吗？本人深表怀疑！！  提醒： “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 （Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。） 想要从本质上理解C指针，必须学习C和汇编的对应关系。” 不是一回事！  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！  电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  十字链表交换任意两个节点C源代码（C指针应用终极挑战）http://download.csdn.net/detail/zhao4zhong1/5532495

user: user-0
content: 引用 6 楼 user-9 的回复:楼主可以看一下c陷阱与缺陷。。。 以我现在的能力，感觉还是看看基础的好，是不是《C和指针》更适合我呢？

user: user-0
content: 完整的CPUAnimBitmap结构体 struct CPUAnimBitmap {     unsigned char    *pixels;     int     width, height;     void    *dataBlock;     void (*fAnim)(void*,int);     void (*animExit)(void*);     void (*clickDrag)(void*,int,int,int,int);     int     dragStartX, dragStartY;      CPUAnimBitmap( int w, int h, void *d = NULL ) {         width = w;         height = h;         pixels = new unsigned char[width * height * 4];         dataBlock = d;         clickDrag = NULL;     }      ~CPUAnimBitmap() {         delete [] pixels;     }      unsigned char* get_ptr( void ) const   { return pixels; }     long image_size( void ) const { return width * height * 4; }      void click_drag( void (*f)(void*,int,int,int,int)) {         clickDrag = f;     }      void anim_and_exit( void (*f)(void*,int), void(*e)(void*) ) {         CPUAnimBitmap**   bitmap = get_bitmap_ptr();         *bitmap = this;         fAnim = f;         animExit = e;         // a bug in the Windows GLUT implementation prevents us from         // passing zero arguments to glutInit()         int c=1;         char* dummy = "";         glutInit( &c, &dummy );         glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA );         glutInitWindowSize( width, height );         glutCreateWindow( "bitmap" );         glutKeyboardFunc(Key);         glutDisplayFunc(Draw);         if (clickDrag != NULL)             glutMouseFunc( mouse_func );         glutIdleFunc( idle_func );         glutMainLoop();     }      // static method used for glut callbacks     static CPUAnimBitmap** get_bitmap_ptr( void ) {         static CPUAnimBitmap*   gBitmap;         return &gBitmap;     }      // static method used for glut callbacks     static void mouse_func( int button, int state,                             int mx, int my ) {         if (button == GLUT_LEFT_BUTTON) {             CPUAnimBitmap*   bitmap = *(get_bitmap_ptr());             if (state == GLUT_DOWN) {                 bitmap->dragStartX = mx;                 bitmap->dragStartY = my;             } else if (state == GLUT_UP) {                 bitmap->clickDrag( bitmap->dataBlock,                                    bitmap->dragStartX,                                    bitmap->dragStartY,                                    mx, my );             }         }     }      // static method used for glut callbacks     static void idle_func( void ) {         static int ticks = 1;         CPUAnimBitmap*   bitmap = *(get_bitmap_ptr());         bitmap->fAnim( bitmap->dataBlock, ticks++ );         glutPostRedisplay();     }      // static method used for glut callbacks     static void Key(unsigned char key, int x, int y) {         switch (key) {             case 27:                 CPUAnimBitmap*   bitmap = *(get_bitmap_ptr());                 bitmap->animExit( bitmap->dataBlock );                 //delete bitmap;                 exit(0);         }     }      // static method used for glut callbacks     static void Draw( void ) {         CPUAnimBitmap*   bitmap = *(get_bitmap_ptr());         glClearColor( 0.0, 0.0, 0.0, 1.0 );         glClear( GL_COLOR_BUFFER_BIT );         glDrawPixels( bitmap->width, bitmap->height, GL_RGBA, GL_UNSIGNED_BYTE, bitmap->pixels );         glutSwapBuffers();     } };

user: user-0
content: 引用 1 楼 luciferisnotsatan 的回复:如果是(int)cleanup， 知道这种是强转么？  (void (*)(void*))cleanup 这个是吧cleanup像转为一个指向一个参数为void*, 返回值为void的函数指针。  void anim_and_exit( void (*f)(void*,int), void(*e)(void*) ) 接受两个函数指针。  google下函数指针你就知道了 我看了C Primer Plus第14章讲到了函数指针，但问题在于：不仅这个例程比书上要复杂，并且函数调用的参数不明：您看： 函数原型： void cleanup( DataBlock *d )  void generate_frame( DataBlock *d, int ticks )  而调用是这样的： bitmap.anim_and_exit( (void (*)(void*,int))generate_frame,   (void (*)(void*))cleanup ); 如此一来，函数cleanup的形参 DataBlock *d， 以及generate_frame函数的形参 DataBlock *d, int ticks都怎么传递值的呢？这和书里面和网上讲解的相差很大……

user: user-1
content: "我自认为自己大学时C语言还不错，然而工作才发现自己是多么的天真" "还有对cleanup函数的引用怎么那么复杂 (void (*)(void*))cleanup ，完全没见过啊"

user: user-7
content: 引用 12 楼 weiran2009 的回复:[quote=引用 5 楼 ri_aje 的回复:] 这是 cuda 改过的 c 语言，(void (*)(void*))cleanup 就是强制类型转换，(...) 里面的是目标类型。 __global__ 还有 <<<...>>> 等都是 cuda 自己的私货，根本不是标准 c 的东西。  想必阁下也是看过《CUDA实战》的GPU前辈吧，我看到main函数开头两句话很有意思：     CPUAnimBitmap  bitmap( DIM, DIM, &data );     //结构体实例data里面有CPUAnimBitmap的数据类型        data.bitmap = &bitmap; // data.bitmap又引用了自己含有引用自己地址的CPUAnimBitmap数据类型bitmap的地址  感觉绕来绕去挺麻烦的，前辈可以指点一二吗？[/quote] 我没看过 cuda 的东西，一直用的 opencl。不过 bitmap <--> data 互指的这种结构很常见，根 cuda 没有直接关系，普通的 c 编程中也能用，这种对偶关系就好像你和你朋友互相知道对方的地址一样，这样找到任何一个就能够比较容易的找到另一个了。

user: user-8
content: 才查两本书就不查了？

user: user-9
content: 楼主可以看一下c陷阱与缺陷。。。