指针这一节是本书中最难的一节,尤其是二级指针和二维数组直接的关系。
本节知识点:
第二个意义是 数组名 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的地址0x40138c
p();
(*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] 函数指针 数组指针 也不好看 就放里面了。