C语言深度解剖读书笔记(4.指针的故事)
来源:互联网 发布:sql server数据库教程 编辑:程序博客网 时间:2024/06/04 23:22
指针这一节是本书中最难的一节,尤其是二级指针和二维数组直接的关系。
本节知识点:
第二个意义是 数组名 sizeof(a) 为整体数组有多少个字节
#include <stdio.h>#include <stdlib.h>int main(int argc, char *argv[]) {/*int a[20]={1,2,4};printf("%d\n",sizeof(a));printf("%p\n",a);printf("%p\n",&a);printf("%p\n",&a[0]);*//*int a[5]={1,2,3,4,5};int (*p)[5]=&a;printf("%d\n",*((int *)(p+1)-1));*/int a[5]={1,2,3,4,5};int* p=(int *)(&a+1);//int *p=&a+1; //这个条语句是 把&a这个数组指针 进行了指针运算后 的那个地址 强制类型转换成了 int *指针 printf("%d\n",*(p-1));return 0;}5.访问指针和访问数组的两种方式:
数组是数组,指针是指针,根本就是两个完全不一样的东西。当然要是在宏观的内存角度看,那一段相同类型的连续空间,可以说的上是数组。但是你可以尝试下,定义一个指针,在其他地方把他声明成数组,看看编译器会不会把两者混为一谈,反过来也不会。
char a[5]={'a','b','c','d','e'}; char (*p)[3]=&a;上面的代码是错误的,为什么?因为数组指针和数组不是一个类型,数组指针是指向一个数组元素为char 长度为3的类型的数组的,而这个数组的类型是数组元素是char长度是5,类型不匹配,所以是错的。
#include <stdio.h>#include <assert.h>int strlen(const char* s){ return ( assert(s), (*s ? (strlen(s+1) + 1) : 0) );}int main(){ printf("%d\n", strlen( NULL)); return 0;}d.自己动手实现strcpy,代码如下:
#include <stdio.h>#include <assert.h>char* strcpy(char* dst, const char* src){ char* ret = dst; assert(dst && src); while( (*dst++ = *src++) != '\0' ); return ret;}int main(){ char dst[20]; printf("%s\n", strcpy(dst, "hello!")); return 0;}e.推荐使用strncpy、strncat、strncmp这类长度受限的函数(这些函数还能在字符串后面自动补充'\0'),不太推荐使用strcpy、strcmpy、strcat等长度不受限仅仅依赖于'\0'进行操作的一系列函数,安全性较低。
f.补充问题,为什么对于字符串char a[256] = "hello";,在printf和scanf函数中,使用a行,使用&a也行?代码如下:
#include <stdio.h>int main(){char* p ="phello";char a[256] = "aworld";char b[25] = {'b','b','c','d'};char (*q)[256]=&a;printf("%p\n",a); //0022fe48//printf("%p\n",&a);//printf("%p\n",&a[0]);printf("tian %s\n",(0x22fe48)); printf("%s\n",q); //q就是&a printf("%s\n",*q); //q就是a printf("%s\n",p);printf("%s\n",a);printf("%s\n",&a);printf("%s\n",&a[0]);printf("%s\n",b);printf("%s\n",&b);printf("%s\n",&b[0]);}
#include <stdio.h>#include <stdlib.h>#include <stdbool.h>int main(int argc, char *argv[]) {int a[3][3]={1,2,3,4,5,6,7,8,9};printf("%d\n",sizeof(a[0]));printf("%d\n",*a[2]);printf("%d\n",*(a[0]+1));printf("%p\n",a[0]);printf("%p\n",a[1]);printf("%p\n",&a[0]+1); //&a[0]+1 跟 a[1]不一样 指针类型不一样 &a[0]+1这个是数组指针 a[1]是&a[1][0] 是int*指针 printf("%d\n",*((int *)(&a[0]+1)));printf("%d\n",*(a[1]+1));printf("%p\n",a);printf("%p\n",&a);printf("%p\n",&a[0]);printf("%d\n",sizeof(a)); //这是a当作数组名的时候printf("%d\n",*((int *)(a+1))); //此时 a是数组首元素的地址 数组首元素是a[0] //首元素地址是&a[0] 恰巧a[0]是数组名 &a[0]就变成了数组指针 return 0;}总结:对于a和a[0]、a[1]等这些即当作数组名,又当作数组首元素地址,有时候还当作数组元素(即使当作数组元素,也无非就是当数组名,当数组首元素地址两种),这种特殊的变量,一定要先搞清它现在是当作什么用的。
int *q; q = (int *)a; printf("%d\n",*(q+6));
int (*p)[3]; p = a; printf("%d\n",*(*(p+1)+1));
#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){int a[3][3]={1,2,3,4,5,6,7,8,9};int (*p)[3];int *q; printf("%d\n",*(*(a+1)+1)); //a *(&a[0]+1)p = a;q = (int *)a;printf("%d\n",*(*(p+1)+1));printf("%d\n",*(a[1]+1));printf("%d\n",a[1][1]);printf("%d\n",*(q+6));}
#include <stdio.h>int main(){ int a[5][5]; int(*p)[4]; p = a; printf("%d\n", &p[4][2] - &a[4][2]);}
#include <stdio.h>#include <malloc.h>int reset(char**p, int size, int new_size){ int ret = 1; int i = 0; int len = 0; char* pt = NULL; char* tmp = NULL; char* pp = *p; if( (p != NULL) && (new_size > 0) ) { pt = (char*)malloc(new_size); tmp = pt; len = (size < new_size) ? size : new_size; for(i=0; i<len; i++) { *tmp++ = *pp++; } free(*p); *p = pt; } else { ret = 0; } return ret;}int main(){ char* p = (char*)malloc(5); printf("%0X\n", p); if( reset(&p, 5, 3) ) { printf("%0X\n", p); } return 0;}
(2)函数中传递指针数组的时候,实参(指针数组)要退化成形参(二级指针)。
#include <stdio.h>#include <stdlib.h>#include <stdbool.h>int main(int argc, char *argv[]) {char* p[4]={"afje","bab","ewrw"};char* *d=p; printf("%s\n",*(p+1)); printf("%s\n",*(d+1)); //d &p[0] p[0]是"afje"的地址,所以&p[0]是保存"afje"字符串的char*指针的地址return 0;}
d.子函数malloc,主函数free,这是可以的(有两种办法,第一种是利用return 把malloc的地址返回。第二种是利用二级指针,传递一个指针的地址,然后把malloc的地址保存出来)。记住不管函数参数是,指针还是数组, 当改变了指针的指向的时候,就会出问题,因为子函数中的指针就跟主函数的指针不一样了,他只是一个复制品,但可以改变指针指向的内容。这个知识点可以看<在某培训机构的听课笔记>这篇文章。
13.数组作为函数参数:数组作为函数的实参的时候,往往会退化成数组元素类型的指针。如:int a[5],会退化成int* ;指针数组会退化成二级指针;二维数组会退化成一维数组指针;三维数组会退化成二维数组指针(三维数组的这个是我猜得,如果说错了,希望大家帮我指出来,谢谢)。如图:
二维数组作为实参的例子:
#include <stdio.h>#include <stdlib.h>#include <stdbool.h>int fun(int (*b)[3]) //此时的b为 &a[0] {printf("%d\n",*(*(b+1)+0));printf("%d\n",b[2][2]);// b[2][2] 就是 (*(*(b+2)+2))printf("%d\n",*(b[1]+2));}int main(int argc, char *argv[]) {int a[3][3]={1,2,3,4,5,6,7,8,9}; fun(a);//与下句话等价 fun(&a[0]);return 0;}
数组当作实参的时候,会退化成指针。指针当做实参的时候,就是单纯的拷贝了!
14.函数指针与指针函数:
a.对于函数名来说,它是函数的入口,其实函数的入口就是一个地址,这个函数名也就是这个地址。这一点用汇编语言的思想很容易理解。下面一段代码说明函数名其实就是一个地址,代码如下:
#include <stdio.h>#include <stdlib.h>#include <stdbool.h>void abc(){printf("hello fun\n");}int main(int argc, char *argv[]) {void (*d)();void (*p)();p = abc;abc();printf("%p\n",abc);printf("%p\n",&abc);//函数abc的地址0x40138cp();(*p)(); d = ((unsigned int*)0x40138c); //其实就算d= 0x40138c这么给赋值也没问题 d();return 0;}
可见函数名就是一个地址,所以函数名abc与&abc没有区别,所以p和*p也没有区别。
b.我觉得函数指针最重要的是它的应用环境,如回调函数(其实就是利用函数指针,把函数当作参数进行传递)代码如下,还有中断处理函数(同理)详细见<
ok6410学习笔记(16.按键中断控制led)>中的 中断注册函数,request_irq。还有就是函数指针数组,第一次见到函数指针数组是在zigbee协议栈中。
回调函数原理代码:
#include <stdio.h>typedef int(*FUNCTION)(int);int g(int n, FUNCTION f){ int i = 0; int ret = 0; for(i=1; i<=n; i++) { ret += i*f(i); } return ret;}int f1(int x){ return x + 1;}int f2(int x){ return 2*x - 1;}int f3(int x){ return -x;}int main(){ printf("x * f1(x): %d\n", g(3, f1)); printf("x * f2(x): %d\n", g(3, &f2)); printf("x * f3(x): %d\n", g(3, f3));}
注意:可以使用函数名f2,函数名取地址&f2都可以,但是不能有括号。
c.所谓指针函数其实真的没什么好说的,就是一个返回值为指针的函数而已。
15.赋值指针的阅读:
a.char* (*p[3])(char* d); 这是定义一个函数指针数组,一个数组,数组元素都是指针,这个指针是指向函数的,什么样的函数参数为char* 返回值为char*的函数。
分析过程:char (*p)[3] 这是一个数组指针、char* p[3] 这是一个指针数组 char* 是数组元素类型、char* p(char* d) 这个是一个函数返回值类型是char* 、char (*p)(char* d)这个是一个 函数指针。可见char* (*p[3])(char* d)是一个数组 数组中元素类型是 指向函数的指针,char* (* )(char* d) 这是函数指针类型,char* (* )(char* d) p[3] 函数指针数组 这个不好看 就放里面了。(PS:这个看看就好了~~~当娱乐吧)
b.函数指针数组的指针:char* (*(*pf)[3])(char* p) //这个就看看吧 我觉得意义也不大 因为这个逻辑要是一直下去 就递归循环了。
分析过程:char* (* )(char *p) 函数指针类型,char* (*)(char *p) (*p)[3] 函数指针 数组指针 也不好看 就放里面了。
- C语言深度解剖读书笔记(4.指针的故事)
- C语言深度解剖读书笔记(4.指针的故事)
- C语言深度解剖读书笔记(4.指针的故事)
- C 语言深度解剖 读书笔记
- 《C语言深度解剖》读书笔记
- 《C语言深度解剖》读书笔记
- C语言深度解剖读书笔记
- C语言深度解剖读书笔记
- C语言深度解剖读书笔记
- C语言深度解剖读书笔记
- C语言深度解剖读书笔记
- C语言深度解剖读书笔记
- C语言深度解剖读书笔记
- C语言深度解剖读书笔记
- C语言深度解剖——读书笔记(数组和指针)
- C语言深度解剖读书笔记(1.关键字的秘密)
- C语言深度解剖读书笔记(2.字符的技巧)
- C语言深度解剖读书笔记(1.关键字的秘密)
- Linux搜索文件及目录命令
- 搭建本地Ubuntu 镜像服务器
- 淘宝开发:Nginx模块Nginx-Http-Footer-Filter
- <服务器端>OCR识别 .条形码识别,解析条形码并返回商品信息
- 读过的一些deep learning文章
- C语言深度解剖读书笔记(4.指针的故事)
- Windows SVN变更发送邮件通知(JAVA实现)
- Sqlite内存数据库
- 你真的了解中英文字符么--str.length()与str.getBytes().length的区别
- 图像处理入门
- EditText---android 中EditText加入图标 更改边框颜色 设置透明 代码 .
- 第二篇 光纤衰耗
- linux备份mysql数据库与恢复mysql备份数据
- Linux ftp:530 perssion denied