&a[0]:数组名a分别和运算符&和运算符[]结合,我们知道,运算符[]的运算优先级比运算符&高,所以数组名a先和运算符[]结合,此时表示首元素,在与运算符&结合表示首元素地址,就是数组内第一个元素的地址。
&a:数组名与运算符&结合表示数组的地址,就是数两者在数字层面上的值是一样的,主要差别是体现在类型上。我们知道两者的数组整体都是地址,而地址就是指针,对此进行指针+1,即&a[0]+1和&a+1时;我们发现指针移动的步长不一样,对&a[0]+1,指针移动的步长为该元素保存的数据的类型大小;对&a+1,指针移动的步长为该数组中元素保存的数据类型大小乘上元素个数。
当一个数组名表示整个数组只有俩种情况:
&a或者sizeof(a)
多维数组的内存存储
由于[]的优先级高于*,所以先和[]结合,但是加上()之后可以改变优先级的结合顺序,形成数组指针。
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int (*p)[10]=&arr;
int i=0;
for(i=0;i<10;i++)
{
printf("%d",(*p)[i]);
printf("%d",*(p+i));//上下俩种形式的结果一致
}
char* arr[5];
char* (*pa)[5]=&arr;
return 0;
}
int main()
{
char arr[]="abcdef";
char* pc=arr;//arr为首元素地址,字符指针存放数组名,也就是把首元素a的地址放到pc里
char*p="abcdef" (""为常量字符串,实际上是把a的地址赋给p)
printf("%c",*p); //得到a
printf("%s",p);//从p存的地址处出发开始打印一个字符串
}
面试题讲解:
int main()
{
char a1[]="abcdef";
char a2[]="acbdef";//a1和a2是俩块不同的内存单元
// (a1!=a2) {a1和a2分别为数组首元素地址,自然不同}
char*p1="abcdef"
char*p2="abcdef"//俩个字符串一模一样,常量字符串不可修改,在内存中不能存俩份,所以相同 (a1==a2)
}
int main()
{
int a=10; int b=20; int c=30; int d=40;
int* arr[4]={&a,&b,&c,&d};// arr[i]是每个元素的地址
int a1={1,2,3,4,5};
int a2={2,3,4,5,6};
int a3={3,4,5,6,7};
int* prr[]={a1,a2,a3};存入首元素地址
for(i=0;i<3;i++)
{int j=0;
for(j=0;j<5;j++)
{
*(prr[i]+j);
}
}
实战演练1:
void print1(int arr[3][5],int x,int y)
{
int i=0;
int j=0;
for(i=0;i<x;i++)
{
for(j=0;j<y;j++)
{printf("%d",arr[i][j]); }
printf("\n");}/*把二维数组想像为一维数组再去讨论首元素地址,每行为一个元素,则arr有三个元素。即arr代表第一行的地址 */
}
void print2(int(*p)[5],int x,int y)//参数为数组指针
{
int i=0;
for(i=0;i<x;i++)
{
int j=0;
for(j=0;j<y;j++)
{
*(p+i);//找到这一行
*(*(p+i)+j)//找到这一行第j个元素
}
}
}
/*a[i]==*(a+i)==*(p+i) ==p[i] p与arr可以互换
[j]==*(a+j)*/
一维数组传参:
int arr1[10];
void canshu(int a[]);
void canshu(int a[10]);
void canshu(int*arr);
int* arr2[10];
void canshu(int*a[10]);
void canshu(int**a);//一级指针的地址放在二级指针中
二维数组传参:
int a[10][20];
void test(int arr[10][20]);
void test(int arr[][20]);
void test(int *arr)//err 传入的首元素地址是一维数组的地址,不能用一级指针和二级指针接受
void test(int (*arr)[5]);//指向某一行有5个元素
函数指针是一个指针,是指向函数的指针,是存放函数地址的指针。
int (*pa)(int,int)=add;
printf("%d".(*pa) (2,3));
printf("%d".(**pa) (2,3));
printf("%d".(pa) (2,3));//结果都一致。*不产生影响。
实战演练:
(* (void(*)()) 0) (); //0强制转换为void(*)()后解引用调用
void ( *signal( int , void(*)(int)) ) (int);
//函数名 signal 参数有2个,一个是int,一个是函数指针.函数返回类型void(*) (int)
typedef void(*mama)(int);给函数返回类型重新取个名字
则上面的可以写成 mama signal(int,mama);
//有一个数组可以存放多个函数的地址---存放函数指针的数组
int(*pa[4]) (int,int)={Add,sub,mul,dlv};
//pa先与[]结合说明是数组,类型为int(*)(int,int) ;
for(i=0;i<4;i++)
{
pa[i](2,3);
}
面试例题:
例:charstrcpy(chara,const char*b);
写一个函数指针pf,指向strcpy
char* (*pf)(char*a,const char*b);
写一个函数指针数组
char* (*pf[4]) (char*a,const char*b);
int main()
{
int arr={0};
int (*p)[10]=&arr;
int (*parr[4])(int,int);// parr是数组,函数指针的数组
int(*(*pp)[4]) (int,int)=&parr; //pp是一个数组指针,指向一个数组
//每个元素的类型是函数指针,元素类型int(*)(int,int)
}
通过一个函数指针调用的函数 ,把函数的地址作为参数传递给另一个函数,当这个指针被用来指向所调用的函数时称为回调。
void text(void(*p)(char*))
{
printf("text\n");
p("bit");
}
void print(char*str)
{
printf("hehe%s",str);
}
int main()
{
text(print);
}
1.百练printf
char arr[]="abcdef";`//存入abcdef\0
printf("%d",sizeof(arr));//7
printf("%d",sizeof(arr+0));//4或者8 计算出来的是首元素的地址
printf("%d",sizeof(*arr));//1 计算首元素的大小
printf("%d",sizeof(arr[1]));//1 计算的是第二个元素的大小
printf("%d",sizeof(&arr));//4或者8 计算数组地址的大小
printf("%d",sizeof(&arr+1));//4或者8 跳过整个数组后面的地址
printf("%d",sizeof(&arr[0]+1));//4或者8 第二个元素的地址
char a[]={'a'.'b'.c','d','e','f'};
printf("%d",sizeof(arr));//6
printf("%d",sizeof(arr+0));//6
printf("%d",sizeof(*arr));//非法访问内存 传入的是97
printf("%d",sizeof(arr[1]));//err
printf("%d",sizeof(&arr));//6 数组的地址——>数组指针char(*p)[7]=&arr;
printf("%d",sizeof(&arr+1));//随机值
printf("%d",sizeof(&arr[0]+1));//45
char *p="abcdef";//常量字符串abcdef\0
printf("%d",sizeof(p));//4或者8 计算指针变量的大小
printf("%d",sizeof(p+1));//4 8 p+1得到字符b的地址
printf("%d",sizeof(*p));//1 计算首元素的大小
printf("%d",sizeof(p[0]));//1 p[0]=*(p+0)
printf("%d",sizeof(&p));//4或者8 计算数组地址的大小
printf("%d",sizeof(&p+1));//4或者8 跳过整个数组后面的地址
printf("%d",sizeof(&p[0]+1));//4或者8 第二个元素的地址
char *p="abcdef";
printf("%d",strlen(p));//6
printf("%d",strlen(p+1));//5
printf("%d",strlen(*p));//err 传入a的97 报错
printf("%d",strlen(p[0]));//err
printf("%d",strlen(&p));//随机值 从地址的起始值开始往后面数 后面不可知
printf("%d",strlen(&p+1));//随机值
printf("%d",strlen(&p[0]+1));//5
int a[3][4]={0};
printf("%d",sizeof(a));//3*4*4=48
printf("%d",sizeof(a[0][0]));//4
printf("%d",sizeof(a[0]));//16
printf("%d",sizeof(a[0]+1));//4 第一行的第一个元素 a[0]是第一行的数组名,数组名表示第一行第一个元素的地址
printf("%d",sizeof(*(a[0]+1)));//4
printf("%d",sizeof(a+1));//16 第二行一维数组的地址
//a是二维数组的数组名,没有sizeof(数组名),也没有&(数组名),所以a是首元素的地址。二维数组的首元素是第一行,则a是第一行的地址
printf("%d",sizeof(*(a+1)));//16 计算第二行的大小
printf("%d",sizeof(&a[0]+1));//4 第二行的地址
printf("%d",sizeof(*(&a[0]+1));//16
printf("%d",sizeof(*a));//16 a是首元素地址 *a就是第一行
printf("%d",sizeof(a[3]));//16
2.编程运算题
int main()
{
int a[5]={1.2.3.4.5};
int *ptr=(int*)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));//2,5
return 0;
}
struct test{
int Num;
char *pc;
short sd;
char cha[2];
short sb[4];
}*p;
//假设p的值是0x100000 则表达式的值为多少
int main()
{//可知结构体的大小为20个字节
printf("%d",p+0x1);//0x100000+20=0x100014
printf("%d",(unsigned long)p+0x1);//转换为十进制整数+1==0x100001
printf("%d",(unsigned int*)p+0x1);//0x100004
}
int main()
{
int a[4]={1,2,3,4};
int *ptr1=(int*)(&a+1);
int *ptr2=(int*)((int)a+1);
printf("%x%x",ptr1[-1],*ptr2);//4 2000000
}
int main()
{
int a[3][2]={(0,1),(2,3),(4,5)};
int *p;
p=a[0];
printf("%d",p[0]);//*(p+0)=1
}
int main()
{
int a[5][5];
int(*p)[4];
p=a;//类型:int (*)[4]-----int (*)[5]
printf("%p,%d",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);0xfffffff04 -4
return 0;
}