9.指针和数组练习题
sizeof 是一个操作符,计算的是对象所占的内存大小,单位是字节,返回类似是soze_t
(unsigned int),不在乎内存里面放的是什么。
strlen 是个库函数,求字符串长度,参数是一个地址(char*类型的地址)(类型不匹配会被强制类型转换为(char*)类型的)
从给定的地址向后访问字符,统计\0之前出现的字符个数。
1,练习:sizeof和int a[] = { 1,2,3,4 };
#include<stdio.h>
int main()
{
//数组名一般是数组首元素的地址
//但是也有两个例外 1,sizeof(arr); 2,&数组名
//sozeof计算的是对象所占内存的大小
//一维数组
int a[] = { 1,2,3,4 };//[1,2,3,4]
printf("%d\n", sizeof(a));//16
printf("%d\n", sizeof(a + 0));//4或者8
//这里的a+0首元素的地址。a+1就是第二个元素的地址
//地址占4或者8个字节
printf("%d\n", sizeof(*a));//4
//这里是a是首元素的地址,*a就是第一个元素,int类型的数据
printf("%d\n", sizeof(a + 1));//4或者8
//上面讲到a+1 就是第二个元素的地址,
//地址占4或者8字节
printf("%d\n", sizeof(a[1]));//4
//这里a[1]就是第二个元素,int类型的数据
printf("%d\n", sizeof(&a));//4或者8
//&a取出的是整个数组的地址,但是也是一个地址
//是地址都占4或者8字节
printf("%d\n", sizeof(*&a));//16
//&a取出整个数组的地址,对数组的地址解引用就拿到整个数组
//所以他就是16//相当于sizeof(a)
printf("%d\n", sizeof(&a + 1));//4或者8
//&a就是整个数组的地址,整个数组的地址加一跳过整个数组,就是加了16
//但是还是一个地址 地址就占4或者8个字节
printf("%d\n", sizeof(&a[0]));//4或者8
//这个就是数组第一个元素的地址啊
//地址就是4或者8个字节。
printf("%d\n", sizeof(&a[0] + 1));//4或者8
//这个就是第一个元素的地址加一,第二个元素的地址
return 0;
}
2,练习sizeof,strlen和char arr[ ] = { 'a','b','c','d','e','f' };
int main()
{
//strlen功能就是给他一个地址,他会计算从这个地址到'\0'之间的字节大小。
char arr[] = { 'a','b','c','d','e','f' };//[a,b,c,d,e,f]
printf("%d\n", strlen(arr));//随机数>=6
//arr数组首元素的地址
//这个数组里面是放了那几个字符 没有'\0'
//strlrn在计算字符串的大小的时候遇到'\0'才会停止
//这里的虽然也构成的越界访问,但是报错与否是是编译器决定的。
printf("%d\n", strlen(arr + 0));//随机数>=6
//arr数组名 表示首元素的地址
//arr+0也是数组首元素的地址
//这里的虽然也构成的越界访问,但是报错与否是是编译器决定的。
printf("%d\n", strlen(*arr));//错误
//arr是首元素地址,
//*arr就是数组的首元素也就是字符a,ascll就是97
//也就是97 传给strlen ,strlen会把97看成char*类型的地址
//这样就出现的野指针,会报出非法访问
printf("%d\n", strlen(arr[1]));//错误
//和上面一样非法非法访问
printf("%d\n", strlen(&arr));//随机数>=6
//&arr取出整个数组的地址,传给我的strlen
//最终也会被看成一个(char*)类型的指针
//这个和直接传arr是没有什么区别的
//arr和&arr的意义虽然不一样但是值都是一样的
//警告强制类型转换都的char*类型的指针
//这里的虽然也构成的越界访问,但是报错与否是是编译器决定的。
printf("%d\n", strlen(&arr + 1));//随机值>=0
//&arr取出整个数组的地址+1就跳过整个数组
//就是整个数组后面的那个地址。同样什么时候遇到'\0'是不知道的
//这里的虽然也构成的越界访问,但是报错与否是是编译器决定的。
printf("%d\n", strlen(&arr[0] + 1));//随机值>=5
//&arr[0]取出首元素的地址,
//&arr[0]+1是第二个元素的地址
//这里的虽然也构成的越界访问,但是报错与否是是编译器决定的。
return 0;
}
//编译器也是人写的 也会有一些错误情况是不会报错的
int main()
{
//字符数组1
char arr[] = { 'a','b','c','d','e','f' };//[a,b,c,d,e,f]
printf("%d\n", sizeof(arr));//6
//sizeof只是计算目标所占内存的大小
//没有'\0'一说
printf("%d\n", sizeof(arr + 0));//4或者8
//这个就是arr是首元素的地址+0也是首元素的地址
//地址就是占4或者8个字节
printf("%d\n", sizeof(*arr));//1
//arr是首元素的地址 *arr就是首元素 char类型的数据
//相当于arr[0]
printf("%d\n", sizeof(arr[1]));//1
//第二个元素 char类型的数据
printf("%d\n", sizeof(&arr));//4或者8
//是数组的地址
//地址就是4或者8个字节
printf("%d\n", sizeof(&arr + 1));//4或者8
//数组的地址+1就是跳过整个数组,相当于加6
//但是还只是一个地址
//地址就是4或者8字节
printf("%d\n", sizeof(&arr[0] + 1));//4或者8
//是第二个元素的地址
//地址就是4或者8字节
}
3,练习sizeof,strlen和 char arr[] = "abcdef";
int main()
{
char arr[] = "abcdef"; //[a,b,c,d,e,f,\0]
//默认会带一个\0
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr + 0));//4或者8
//这个就是arr是首元素的地址+0也是首元素的地址
//地址就是占4或者8个字节
printf("%d\n", sizeof(*arr));//1
//arr是首元素的地址 *arr就是首元素 char类型的数据
//相当于arr[0]
printf("%d\n", sizeof(arr[1]));//1
//数组的第二个元素
printf("%d\n", sizeof(&arr));//4或者8
//是数组的地址
//地址就是4或者8个字节
printf("%d\n", sizeof(&arr + 1));//4或者8
//是整个数组最后一个元素后面的地址
//地址就是4或者8个字节
printf("%d\n", sizeof(&arr[0] + 1));//4或者8
//这个就是第一个元素的地址加一,第二个元素的地址
return 0;
}
int main()
{
char arr[] = "abcdef"; //[a,b,c,d,e,f,\0]
printf("%d\n", strlen(arr));//6
//arr首元素地址
printf("%d\n", strlen(arr + 0));//6
//arr+0是首元素的地址
printf("%d\n", strlen(*arr));//错误
//非法访问
printf("%d\n", strlen(arr[1]));//错误
//非法访问
printf("%d\n", strlen(&arr));//6
//&arr取出整个数组的地址,传给我的strlen
//最终也会被看成一个(char*)类型的指针
//这个和直接传arr是没有什么区别的
//arr和&arr的意义虽然不一样但是值都是一样的
//警告强制类型转换都的char*类型的指针
printf("%d\n", strlen(&arr + 1));//随机数
//&arr取出整个数组的地址+1就跳过整个数组
//就是整个数组后面的那个地址。同样什么时候遇到'\0'是不知道的
printf("%d\n", strlen(&arr[0] + 1));//5
//从第二个元素地址开始的
return 0;
}
4,sizeof ,strlen 和char* p = "abcdef";
int main()
{
//"abcdef"是一个常量字符串
char* p = "abcdef";//p是一个指针变量
//里面放的是a的地址,p指向的是a
printf("%d\n", sizeof(p));//4或者8
//p是指针变量,
//存的是地址,地址占4或者8
printf("%d\n", sizeof(p + 1));//4或者8
//p+1是b的地址
//是地址就是4或者8个字节
printf("%d\n", sizeof(*p));//1
//*p就是a char类型的
printf("%d\n", sizeof(p[0]));//1
//p[0]==*(p+0) 就是 a
printf("%d\n", sizeof(&p));//4或者8
//p是a的地址
//&p是指针变量p的地址(二级指针)
printf("%d\n", sizeof(&p + 1));//4或者8
//&p是a地址的二级指针
//p是一个指针,&p+1就是二级指针+1
//&p是p的地址,&p+1就是p后面的地址
//跳过p之后的地址
printf("%d\n", sizeof(&p[0] + 1));//4或者8
//p[0]==*(p+0) 就是 a
//&a就是p
//a的地址+1,就是b的地址
return 0;
}
int main()
{
//"abcdef"是一个常量字符串
char* p = "abcdef";//p是一个指针变量
//里面放的是a的地址,p指向的是a
printf("%d\n", strlen(p));//6
//p里面是a的地址,遇到\0就停止
printf("%d\n", strlen(p + 1));//5
//p+1就是b的地址
printf("%d\n", strlen(*p));//错误
//*p就是'a',会构成非法访问,
printf("%d\n", strlen(p[0]));//错误
//p[0] == *(p+0)就是'a'
//和上面一样会构成非法访问
printf("%d\n", strlen(&p));//随机数
//&p就是a的二级指针,就是从p处开始向后数计数
//并不知道什么时候遇到\0
printf("%d\n", strlen(&p + 1));//随机数
//&p+1就是,跳过p地址开始向后访问,开始计数
//也是不知道什么时候遇到\0
printf("%d\n", strlen(&p[0] + 1));//5
//&p[0]就是a的地址,+1就是b的地址
return 0;
}
5,二维数组和sizeof
#include<stdio.h>
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//48
//这里的a就是整个数组,3*4*4
printf("%d\n", sizeof(a[0][0]));//4
//这个是第一行第一个元素,int类型的
printf("%d\n", sizeof(a[0]));//16
//a[0]就是第一行的数组名,sizeof(a[0]) 就是第一行的数组名单独放在sizeof内部,
//计算的是第一行的大小
//第一行元素,第一行有4个元素。
printf("%d\n", sizeof(a[0] + 1));//4或者8
//a[0]是第一行的数组名,但是+1就不是数组名单独放在sizeof内部
//这里a[0]就是数组首元素的地址。就是第一行第一个元素的地址。
//这里单独值的是第一行第二个数的地址。
printf("%d\n", sizeof(*(a[0] + 1)));//4
//上面分析道()里面的是第一行第二个元素的地址
//*()就是第一行第二个元素。
printf("%d\n", sizeof(a + 1));//4或者8
//a表示首元素的地址,二维数组首元素就是第一行的地址(行指针)
//a+1就是第二行的地址,
printf("%d\n", sizeof(*(a + 1)));//16
//上面说到()他是第二行1的地址,
//*()就是第二行的所有元素。
//这里*(a+1)->a[1]
printf("%d\n", sizeof(&a[0] + 1));//4或者8
//a[0]是第一行的数组名,&()取出的是第一行的地址
//&a[0]+1 -> 就是函数第二行的地址
printf("%d\n", sizeof(*(&a[0] + 1)));//16
//上面讲到()就是第二行的地址,
//*()就是第二行的所有元素
printf("%d\n", sizeof(*a));//16
//代表首元素的地址,第一行的地址
//*()就是第一行的所有元素
//*(a)->*(a+0)->a[0]
printf("%d\n", sizeof(a[3]));//16
//这里a[0]是第一行的数组名,a[1]是第二行的数组名
//但是sizeof 不会去访问他的类型,
//例如:sizeof(a)和sizeof(int),,a为int类型的数据
//
//所以a[3]和a[0]的类型是一样的。
return 0;
}
10. 指针练习题
练习1:
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
//答案:2,5
练习2:
#include<stdio.h>
struct Test
{//已知,结构体Test类型的变量大小是20个字节(x86环境)(32位)
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x10 00 00
int main()
{
p = (struct Test*)0x100000;
printf("%#p\n", p + 0x1);
printf("%#p\n", (unsigned long)p + 0x1);//强制转换为一个整数了
printf("%#p\n", (unsigned int*)p + 0x1);
return 0;
}
//答案: //0x00100014
//0x00100001
//0x00100004(x86环境/32位机)
练习3:
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
//&a为整个数组的地址+1跳过整个数组。
//被强制转换位int*类型的 存入ptr1
int* ptr2 = (int*)((int)a + 1);
//首元素地址a先被强转位(int)是一个整数,
//在被强转为int*类型的指针
//这里要具体到每一个字节,(小端字节序存储)
printf("%x,%x", ptr1[-1], *ptr2);
//*(ptr1-1) 就是最后一个元素
//*ptr2
return 0;
}
//答案:4,2000000
练习4:
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//注意这里是(0,1)是逗号表达式
int* p;
p = a[0];
//第一行的数组名,是首元素的地址
printf("%d", p[0]);
return 0;
}
//答案:1
练习5:
int main()
{
int a[5][5];
int(*p)[4];//这个是一维数组指针
p = a;//
//a是数组首元素的地址//也就是第一行的地址,a的类型应该是 int(*)[5]类型的。
//但是这里的地址指向的数组长度是[4],
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//这里两个结果都是-4
//-4的补码是1111 1111 1111 1111 1111 1111 1111 1100
//地址打印就是十六进制打印(X86环境下是地址4个字节)FFFFFFFC
//整型打印就是-4
return 0;
}
//答案:FFFFFFFC,-4
练习6:
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&aa + 1);
//&aa就是整个数组的地址,&aa+1就是跳过整个数组的地址,指向数组最后元素的后面的那个地址
//(int*)就是强制类型转换。
int* ptr2 = (int*)(*(aa + 1));
//aa是数组首元素的地址,也就是第一行的地址,aa+1就是第二行的地址,
//*(aa+1)就是指向第二行的元素,数组名也能代表整个元素,也就相当于数组名
//强制类型转化为(int*)类型的
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//ptr1-1就指向了最后一个元素,
//ptr2-1指向第一行最后一个元素。
return 0;
}
//答案:10,5
练习7:
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
//这是一个字符串首字符的地址数组,里面的每一个元素就是字符串首字母的地址。
//a数组有三个元素,分别存了w,a,a的地址。
char** pa = a;
//a是数组首元素的地址,char*类型,
//pa是a的地址,char**类型
pa++;
//pa++,就a++,就是第二个元素。
//就a的地址,打印出来就是at
printf("%s\n", *pa);
return 0;
}
//答案为:at
练习8:
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };
//这是一个字符串首字符的地址数组,里面的每一个元素就是字符串首字母的地址。
//a数组有四个元素,分别存了E,N,P, F的地址。
char** cp[] = { c + 3,c + 2,c + 1,c };
//
char*** cpp = cp;
//
printf("%s\n", **++cpp);//POINT
printf("%s\n", *-- * ++cpp + 3);//ER
//这里的优先级++>-- 那个+是最后计算的。
printf("%s\n", *cpp[-2] + 3);//ST
printf("%s\n", cpp[-1][-1] + 1);//EW
return 0;
}