目录
一维数组练习
一维整形数组与sizeof、strlen
#include<stdio.h>
int main()
{
//一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
//只有数组名放到sizeof中,是代表整个数组 值=(数组内)元素个数*元素类型 4*4=16字节
printf("%d\n", sizeof(a + 0));
//不只有数组名了,此时a为数组首元素地址,首元素地址+0还是首元素地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(*a));
//不只有数组名,此时数组名为首元素地址,*首元素地址-->首元素 1是整形,所以为4个字节
printf("%d\n", sizeof(a + 1));
//不只有数组名,此时数组名为首元素地址,首元素地址+1,是下一个元素的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(a[1]));
//a[1]是元素2,类型为整形,整形为4个字节
printf("%d\n", sizeof(&a));
//&数组名:取出的是整个数组的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(*&a));
//先取出整个数组的地址,在解引用,那就是整个数组了
//(数组内)元素个数*元素类型 4*4=16字节
//直观理解为*&抵消了,只剩a
printf("%d\n", sizeof(&a + 1));
//先取出整个数组的地址,再+1,表示跳过整个数组,指向了下一个区域的地址
//但本质上还是地址,地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(&a[0]));
//a[0]是首元素,&a[0]为取出首元素的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(&a[0] + 1));
//a[0]是首元素,&a[0]为取出首元素的地址,地址+1,表示指到下一个元素的地址
// //地址是指针变量,在不同平台下大小不一样,4或8个字节
}
一维字符数组与sizeof、strlen
#include<stdio.h>
#include<string.h>
int main()
{
//字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));
//sizeof(数组名),代表计算整个数组的大小 6*1=6字节
printf("%d\n", sizeof(arr + 0));
//不只有数组名,arr便是首元素地址。首元素地址+0还是首元素地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(*arr));
//arr为首元素地址,*首元素地址,就是字符 'a'
//'a'类型为char,为1个字节
printf("%d\n", sizeof(arr[1]));
//arr[1]<==>*(a+1)<==>'b'
//'b'类型为char,为1个字节
printf("%d\n", sizeof(&arr));
//&数组名,取出的是整个数组的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(&arr + 1));
//&数组名,取出的是整个数组的地址,然后+1是跳过arr这个数组,指向了arr后面的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(&arr[0] + 1));
//&arr[0]是取出首元素的地址,地址+1下一个元素的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
//strlen和sizeof不一样,它是计算从起始地址到'\0'之前所出现的字符个数的
printf("%d\n", strlen(arr));
//arr为首元素地址,但是该数组没有'\0',所以会是随机值
printf("%d\n", strlen(arr + 0));
//arr为首元素地址,+0还是指向首元素地址,同上还是随机值
printf("%d\n", strlen(*arr));
//strlen内应该传其实地址,这里arr为首元素地址,又解引用,就是字符'a'
//也就是strlen(97),它就是野指针
printf("%d\n", strlen(arr[1]));
//与上一种情况一样,为野指针
printf("%d\n", strlen(&arr));
//&arr是取出了整个数组的地址,整个数组的地址与首元素地址相同
//又因为没有'\0',所以会是随机值
printf("%d\n", strlen(&arr + 1));
//&arr取出整个数组的地址,+1跳过整个数组
//其实地址有了,但是还是不知道'\0'在哪,所以还是随机值
printf("%d\n", strlen(&arr[0] + 1));
//取出首元素的地址+1,得到了第二个元素的地址
//但是还是不知道'\0',所以还是随机值
return 0;
}
一维字符串数组与sizeof与strlen
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdef";
//与上面的数组不一样的地方是,这个字符串后面默认隐藏了'\0'
//相当于上面char arr[] = {'a','b','c','d','e','f','\0'};
printf("%d\n", sizeof(arr));
//sizeof(数组名),算整个数组的大小 7*1=7字节
printf("%d\n", sizeof(arr + 0));
//不只有数组名,arr代表首元素地址,+0还是首元素地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(*arr));
//arr为首元素地址,解引用为首元素'a'
//'a'的类型为char,大小为1个字节
printf("%d\n", sizeof(arr[1]));
//arr[1]<==>*(a+1)<==>'b' 大小为1个字节
printf("%d\n", sizeof(&arr));
//&数组名,取出的是整个数组的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(&arr + 1));
//&数组名,取出的是整个数组的地址,然后+1是跳过arr这个数组,指向了arr后面的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(&arr[0] + 1));
//&arr[0]是取出首元素的地址,地址+1下一个元素的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", strlen(arr));
//arr为首元素地址,同时也有'\0',它们之间又6个字符
printf("%d\n", strlen(arr + 0));
//arr为首元素地址,+0没有跳过任何地址,所以还是六个字符
//printf("%d\n", strlen(*arr));
//arr为首元素地址,解引用为首元素'a'
//strlen(97),97为野指针
//printf("%d\n", strlen(arr[1]));
//同上,是第二个元素'b'
//也是野指针
printf("%d\n", strlen(&arr));
//&arr为取出整个数组的地址,整个数组的地址与首元素的地址一样
//所以找到'\0'之间的元素个数为6
printf("%d\n", strlen(&arr + 1));
//&arr为取出整个数组的地址,+1就是跳过整个数组的地址
//地址有了,但是不知道'\0'在哪里,所以为随机值
printf("%d\n", strlen(&arr[0] + 1));
//&arr[0]是取出首元素的地址,+1就是往后跳过一个元素
//从第二个字符的地址开始,到'\0'之间的元素个数为5
return 0;
}
常量字符串与sizeof与strlen
#include<stdio.h>
#include<string.h>
int main()
{
char* p = "abcdef";
//这里是一个常量字符串,常量字符串的内容是不可修改的
//且这里的末尾也隐藏了'\0'
//p只是保存了首元素'a'的地址
printf("%d\n", sizeof(p));
//p是保存字符串首元素地址的
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(p + 1));
//p是个字符指针,+1跳过一个字符,那就是b的地址
//地址是指针变量,在不同平台下大小不一样,4或8个字节
printf("%d\n", sizeof(*p));
//对于首元素地址解引用,那就是访问'a',为1字节
printf("%d\n", sizeof(p[0]));
//p[0]<==>*(p+0) 还是对首元素地址解引用,同上为1字节
printf("%d\n", sizeof(&p));
//p为char*类型,&p为char**,二级指针的大小依旧是4或8个字节
printf("%d\n", sizeof(&p + 1));
//&p+1为二级指针往后跳过刚才一级指针的位置,也就是跳过了p的地址
//最后还是指向了一个地址,所以还是4或8个字节
printf("%d\n", sizeof(&p[0] + 1));
//&p[0]+1<==>&*(p+0)+1<==>p+1就是b的地址,所以为4或8个字节
printf("%d\n", strlen(p));
//p为首元素的地址,找到了地址,也有'\0',之间的元素个数为6
printf("%d\n", strlen(p + 1));
//首元素地址+1是跳过了第一个元素,所以此时计算的元素个数为5
printf("%d\n", strlen(*p));
//解引用首元素地址,那就是'a',写法不对
printf("%d\n", strlen(p[0]));
//p[0]<==>*(p+0),还是相当于解引用首元素地址,'a',写法不对
printf("%d\n", strlen(&p));
//取出存储首元素地址的地址,这就是另外一个空间了
//有地址,但不知道'\0'何时出现,那就是随机值
printf("%d\n", strlen(&p + 1));
//同上还是随机值,但二者的随机值没有联系
printf("%d\n", strlen(&p[0] + 1));
//从'b'的地址开始计算,所以有5个字符。
return 0;
}
总结:
一维数组数组名大多数情况下都代表首元素地址。
但是有两个例外,数组名代表整个数组
a.sizeof(数组名) 注意:()中必须只有数组名才能代表整个数组
b.&数组名 注意:虽然&数组名和首元素地址值一样。但是对于指针,我们要关注类型,类型能够决定指针+-整数跳过的步长(单位字节)和解引用访问的权限。&数组名类型就是数组指针类型。
二维数组
#include<stdio.h>
#include<string.h>
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));
//sizeof()只有数组名,代表整个数组的大小 3*4*4=48字节
printf("%d\n", sizeof(a[0][0]));
//a[0][0]访问的元素为0,sizeof(0)=4
printf("%d\n", sizeof(a[0]));
//类比
//一维数组 二维数组
//a1[i] a2[i][j]
//这里的a1是数组名 二维数组的a2[i]不就是恰好对应上一维数组的a1,所以a[i]就相当于一维数组名
//a[0]就是这个二维数组的第一行数组名
//数组名单独放到sizeof()就是计算第一行数组的大小
//4*4=16
printf("%d\n", sizeof(a[0] + 1));
//sizeof()不仅仅有数组名,那就不是整个数组的大小了
//此时a[0]为第一行首元素的地址,+1那就是第一行第二个元素的地址,地址为4或8个字节
//a[0]相当于&a[0][0],&a[0][0]+1相当于&a[0][1]
printf("%d\n", sizeof(*(a[0] + 1)));
//a[0]+1同上,解引用那就是访问a[0][1]的元素0,整形的字节大小为4个字节
printf("%d\n", sizeof(a + 1));
//a没有单独放在sizeof内部,也没有&a
//此时这里的a就是二位数组首元素地址,二维数组首元素地址就是二维数组第一行地址
//a+1就是跳过第一行的地址,表示第二行的地址,所以为4或8个字节
printf("%d\n", sizeof(*(a + 1)));
//a+1同上,解引用那就是第二行的数组,大小为4*4=16
//还可以这样理解*(a+1)-->a[1]
//sizeof(a[1])就是计算第二行数组的大小
printf("%d\n", sizeof(&a[0] + 1));
//a[0]为第一行数组名,&数组名就是取出整个第一行数组
//+1就是跳过第一行,取出第二行整个数组的地址,为4或8个字节
printf("%d\n", sizeof(*(&a[0] + 1)));
//&a[0]+1同上
//在解引用就是整个第二行的元素,4*4=16
printf("%d\n", sizeof(*a));
//a未放在sizeof()内部,也没有&a
//a就是二维数组首元素地址,二维数组首元素地址就是二维数组第一行地址
//在解引用那就是第一行的元素 4*4=16
printf("%d\n", sizeof(a[3]));
//a[3]应该是整个第四行数组,4*4=16
//不存在越界访问,因为只是使用了其数组名,并未访问内部元素
return 0;
}
总结:
二位数组的数组名与一维数组有相同之处
以int a[3][4]这个二维数组举例
对于数组名a来说,如果单独放在sizeof()或者&a的话,那就是代表整个数组
其他情况就是代表二维数组首元素地址
二维数组首元素地址就是二维数组第一行的地址,即&a[0]
对于a[0]来说,如果单独放在sizeof()或者&a[0]的话,那就是代表第一行的整个地址
其他情况就是代表二维数组第一行首元素地址,即&a[0][0]
指针笔试题
1.
#include<stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d\n", *(a + 1), *(ptr - 1));
return 0;
}
小结:
指针变成另一种指针时,虽然还是指向相同的地址,但是其解引用权限和步长都会发生改变
2.
#include<stdio.h>
//x86环境下,以下结构体的大小是20个字节
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}* p = 0x00100000;
//假设p 的值为0x00100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);
//p为结构体类型指针变量,+1就是跳过一个结构体Test
//已知题目变量大小为20字节,而题目当中是以 %p 的形式打印,是以十六进制打印
//十进制20转十六进制为14
//0x00100000+0x00000014=0x00100014
printf("%p\n", (unsigned long)p + 0x1);
//p的类型被强制转换成无符号长整型
//此时0x00100000为16进制的整数,再+16进制的整数0x1
//那就是0x001000001
printf("%p\n", (unsigned int*)p + 0x1);
//先把p强制转化成无符号整形指针类型
//再+1,就相当于跳过一个无符号整形的长度(4个字节)
//0x00100000+0x00000004=0x00100004
return 0;
}
小结:加减看类型,整数加减与我们平时计算的没差
指针加减整数就是以不同指针类型的意义跨距离
3.
#include<stdio.h>
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);
//ptr1[-1] --> *(ptr1-1)
//%x 以十六进制的形式打印
return 0;
}
4.
#include<stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//注意对比int a[3][2]={ {0,1}, {2,3}, {4,5} };
//这个才是二位数组的赋值,而 (,)是逗号表达式
//实际相当于 int a[3][2]={1,3,5};
int* p;
p = a[0];
//a[0]第一行数组名
//a[0]既没有放到sizeof内部也没有取地址
//a[0]就是二维数组第一行首元素的地址,即&a[0][0]
printf("%d", p[0]);
//p[0] --> *(p+0)
return 0;
}
5.
#include<stdio.h>
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;
}
6.
#include<stdio.h>
int main()
{
int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)(*(a + 1));
printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
7.
#include<stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
8.
#include<stdio.h>
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);
return 0;
}
有什么错误希望评论区可以指出来。