目录
数组名的意义:
1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
2.&数组名,这里的数组名表示整个数组,取出的的整个数组的地址。(&arr(arr是二维数组)也表示是的整个二维数组的地址)
3.除此之外所有的数组名都表示首元素的地址,在二维数组中数组名表示的是第一行的地址
指针和数组练习题:
一维数组:
int arr[]={1,2,3,4};
printf("%d\n", sizeof(arr)); //整个数组的地址:int->4——4*4=16
printf("%d\n", sizeof(arr + 0));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr + 1));
printf("%d\n", sizeof(*&arr));
printf("%d\n", sizeof(arr + 0)); //sizeof里只有arr才表示整个数组,arr+0表示一个元素的地址,所以是int* 就是4/8;
printf("%d\n", sizeof(&arr)); //表示整个数组的地址,也是地址,所以是4/8;
printf("%d\n", sizeof(&arr + 1)); //&arr,取出的是整个数组的地址,+1表示跳过4个整形 ,相当于arr[4]的地址(int*),所以是4/8。
printf ("\d\n",sizeof(*&arr)); //&arr表示取出整个数组的地址,在解引用,所以还是相当于sizeof(arr),所以是16;
字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n\n", sizeof(arr));
//6
printf("%d\n\n", sizeof(arr+0));
//4/8
printf("%d\n\n", sizeof(*arr));
//1
printf("%d\n\n", sizeof(arr[1]));
//1
printf("%d\n\n", sizeof(&arr));
//4/8
printf("%d\n\n", sizeof(&arr + 1));
//4/8
printf("%d\n\n", sizeof(&arr[0]+1));
//4/8错误(重点):
1.printf("%d\n\n", sizeof(arr));这里计算的是整个数组的大小,所以是1*6=6。
2.printf("%d\n\n", sizeof(arr+0));这里的arr就表示的是首元素的地址了,所以就是4/8;
3. printf("%d\n\n", sizeof(&arr));表示是整个数组的地址,所以地址就是4/8;4
strlen型:
printf("%d\n\n", strlen(arr)); //19
//随机值,因为没有'/0'结尾
printf("%d\n\n", strlen(arr+0)); //19
//随机值,因为没有'/0'结尾
printf("%d\n\n", strlen(*arr));
//报错,因为stlen是以指针传参,他会把a当成地址来进行计算
printf("%d\n\n", strlen(arr[1]));
//报错,因为stlen是以指针传参,他会把a当成地址来进行计算
printf("%d\n\n", strlen(&arr)); //19
//整个数组的地址,是一个数组指针,会出现警告,但也可以运行,这里的这个数组指针指向的地址也是首元素的地址,所以是随机值
printf("%d\n\n", strlen(&arr+1)); //19-6,
//随机值-6;表示跳过了这一个整个数组
printf("%d\n\n", strlen(&arr[0]+1)); //19-1
//随机值-1;从第二个数的地址开始计算;
字符串数组:
char arr[] = "abcdef";
printf("%d\n\n\n", sizeof(arr));
//7 6+1,还有一个'\0'
printf("%d\n\n\n", sizeof(arr+0));
//4/8, 这是一个地址
printf("%d\n\n\n", sizeof(*arr));
//1 *arr表示字符a
printf("%d\n\n\n", sizeof(arr[1]));
//1 *arr表示字符b
printf("%d\n\n\n", sizeof(&arr));
//4/8 &arr表示整个数组的地址
printf("%d\n\n\n", sizeof(&arr+1));
// 4/8 表示一个地址
printf("%d\n\n\n", sizeof(&arr[0]+1));
//4/8 表示的是字符b的地址
printf("%d\n\n\n", strlen(arr));
//6
//字符串长度
printf("%d\n\n\n", strlen(arr+0));
//6
//字符串长度
printf("%d\n\n\n", strlen(*arr));
//错误
//strlen把a当成地址来计算
printf("%d\n\n\n", strlen(arr[1]));
//错误
//strlen把a当成地址来计算
printf("%d\n\n\n", strlen(&arr));
//6
//&arr表示的是数组指针,会报警告,因为地址类型不同,但地址指向相同,所以也是6
printf("%d\n\n\n", strlen(&arr+1));
//随机值
//&arr+1跳过了这个数组。&arr表示的是这整个数组的地址
printf("%d\n\n\n", strlen(&arr[0]+1));
//5
//arr[0]表示a,&arr[0]表示a的地址,a的地址+1;
二维数组(重点):
int a[3][4]={0};
printf("%d\n\n", sizeof(a));
//虽然a是二维数组,但是在sizeof中,这里a就是整个数组,所以是3*4*4=48;
printf("%d\n\n", sizeof(a[0][0]));
//计算的是首元素的大小 4;**************************重点!!!!()**********************
printf("%d\n\n", sizeof(a[0]));
//数组名单独放在sizeof内部计算的是整个数组,所以在二维数组中,arr[0]表示第一行的数组名,所以sizeof(a[0])这表示计算这一行的大小,所以是4*4=16。
printf("%d\n\n,", sizeof(a[0] + 1));
//sizeof中a[0]没有单独放到sizeof内部,所以a[0]这行首元素的地址,即第一行第一个元素的地址,所以a[0]+1就是首元素+1,所以a[0]+1表示第一行第二个元素的地址,所以是4/8。
printf("%d\n\n", sizeof(*(a[0] + 1)));
//a[0]+1,表示第一行第二个元素的地址,所以再解引用,就是第一行第二个元素,所以是4。
printf("%d\n\n", sizeof(a + 1));
//a表示第一行的地址,+1就是下一行,所以sizeof测的是第二行的地址,所以是4/8;
printf("%d\n\n", sizeof(*(a + 1)));
//对第二行的地址解引用,就表示的是整个第二行!!!!所以是4*4=16;
printf("%d\n\n", sizeof(&a[0] + 1));
//a[0]表示第一行首元素的地址,再&表示这一整行的地址,+1表示第二整行的地址,所以是4/8;
printf("%d\n\n", sizeof(*(&a[0] + 1)));
//对第二整行解引用,所以是整个第二行的地址,所以是4*4=16
printf("%d\n\n", sizeof(*a));
//a表示整个第一行的地址,解引用就拿到了这整个数组,所以是4*4=16
printf("%d\n\n", sizeof(a[3]));
//a[3]单独放在sizeof中,计算整个数组的大小,所以是4*4=16;sizeof根本不会去访问 他,只是去会计算。
2.指针笔试题
int main() { int a[5] = { 1,2,3,4,5 }; int* ptr = (int*)(&a + 1); printf("%d %d", *(a + 1), *(ptr - 1)); }
这里&a拿出整个数组,是一个数组指针,+1表示跳过这一整个数组,再将int(*)[5]型的指针强行转化为整形指针类型赋给ptr,最后输出的结果为2,5。a表示首元素的地址,+1指向第二个元素;ptr不是数组指针,所以-1是指向5;
struct Test { int num; char* pcname; short sDate; char cha[2]; short sBa[4]; }*p; //这个结构体大小是20个字节。 //p的初始值为0x000000; int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
下面中注释我假设是100000,其实实际上是0x000000,但影响不大。
16行:结构体指针+1,跳过一个此般大小的结构体,这个结构体大小是20字节,所以+1是跳过20个字节,因为%p是以地址的形式打印,所以是以16进制显示,那20就是14(16进制)
17行:将结构体指针转化为无符号整形,所以无符号整形+1,就是+1,即输出100000+1.
18行:这里将p转化为无符号整形指针,整形指针+1跳过4个字节,所以这里+1,实际上是+4。
int main() { int a[4] = { 1,2,3,4 }; int* ptr1 = (int*)(&a + 1); int* ptr2 = (int*)((int)a + 1); printf("%x,%x\n", ptr1[-1], *ptr2); return 0; }
ptr1应该没什么问题,我们来主要看看ptr2,我们将a数组首元素的地址强制类型转换为整形,我们进行+1,就是整形+1,我们再转换为int*型给ptr,所以ptr指向的是a首元素地址加上相当于一个char指针大小的地址,就看作是ptr2是a首元素向后指向了一个字节的大小,上图绿色的部分就是ptr2指向的内容再内存中的存放形式,我们再解引用,就打印出结果了。
int main() { int a[3][2] = { (0,1),(2,3),(4,5) }; int* p; p = a[0]; printf("%d\n\n", p[0]); return 0; }
这是一个逗号表达式,不要被蒙骗了。
%p打印:
%p是打印地址的形式,他将地址以16进制的形式打印出来并在前面补上0,给一个地址就打打印出来,%p只是打印的一种方式,他不会主动去内存中找一个数的地址。
%x也是也是打印地址,但是他不会补齐前面多余的零。%#x可以在打印的时候加上0x;
int main() { int a[5][5]; int(*p)[4]; p = a; printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); return 0; }
这里我们知道,a[4][2]和p[4][2]不相同,我们可以模拟数组的存放样式来分析他们的对应关系。指针减指针求的是元素的个数,所以&p[4][2] - &a[4][2]会得出4,又因为数组中地址随下标的增长而增长,即p[4][2]的地址低于a[4][2],所以&p[4][2] - &a[4][2]会最终得出结果为-4,内存中-4会以补码的形式存放在内存中存放,所以%p形式打印会以为内存中存的是地址,会直接打印补码的内容。
而%d形式打印-4,则会将补码转化为原码进行打印,所以会正常打印出-4;
int main() { int aa[2][5] = { 1,2,3,4,5,6,7,8,9,10 }; int* ptr1 = (int*)(&aa + 1); int* ptr2 = (int*)(*(aa + 1)); printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); return 0; }
这里&arr,表示取出整个aa数组的地址,+1即跳过整个数组。
这里aa表示第一行地址,+1指向第二行,再解引用表示整个第二行,我们再强行转换为int*型赋给ptr2,所以ptr2指向aa第二行的首元素。
int main() { char* a[] = { "work","at","alibaba" }; char** pa = a; pa++; printf("%s\n", *pa); return 0; }
a里面存放的是3串字符串的首元素的地址,我们再用ppa这个二级指针指向a数组的首元素,所以我们将ppa+1后ppa指向a数组的第二个元素,我们再将ppa解引用后即找到了a数组第二个元素的地址,再用%s打印,即可打印at这个单词。
int main() { char* c[] = { "ENTER","NEW","POINT","FIRST" }; char** cp[] = { c + 3,c + 2,c + 1,c }; char*** cpp = cp; printf("%s\n", **++cpp); printf("%s\n", *--*++cpp + 3); printf("%s\n", *cpp[-2] + 3); printf("%s\n", cpp[-1][-1] + 1); }
这道题只要把图画好,每条语句从变量名开始下手,基本就可以把这题写出来,我们先来了解一些优先级顺序,++、--的优先级大于*号,【】的优先级大于*号和++ 。
这些笔试题就分享到这了,希望能加深大家对指针的理解,指针的篇章也就到这就结束了,接下来准备开始更新字符串函数和结构体的相关内容