一维数组
int main(){
char arr[]="abcdef";
// arr[]存放的是abcdef\0 7个字符 每个字符为1
printf("%d\n",sizeof (arr));
//7 计算数组的大小7
printf("%d\n",sizeof (arr+ 0));
// 8 计算地址大小 4或8 跟具体是64位机器还是32位机器有关
printf("%d\n",sizeof (*arr));
//1 char类型的数组的 步长 解引用只取一个字节的大小 这里如果输出*arr 则应该是a 即首元素地址
printf("%c\n",*arr);
printf("%d\n",sizeof (arr[1]));
// 1
printf("%d\n",sizeof (&arr));
//8 数组的地址
printf("%d\n",sizeof (&arr+1));
//8 数组地址的下一个地址(跳过整个数组后的地址)
printf("%d\n",sizeof (&arr[0]+1));
//8 第二个元素b的地址
//以上这三个最后算出来的都是一个地址
printf("%d\n",&arr[0]+1);
return 0;
}
int main(){
char arr[]="abcdef";
printf("%d\n",strlen(arr));
//6
printf("%d\n",strlen(arr+0));
//6 同上 效果一样的
printf("%d\n",strlen(*arr));
//报错这里只有一个字符a 并不是一个数组 没办法计算
printf("%d\n",strlen(arr[1]));
//报错 同上 传入一个b字符
printf("%d\n",strlen(&arr));
//6 &arr 数组的地址 从第一个元素开始数 数到6个字符
printf("%d\n",strlen(&arr+1));
//0下个数组的地址 随机
printf("%d\n",strlen(&arr[0]+1));
//5 元素b的地址从b的地址往后数 能数五个字符 那长度就是5
}
int main(){
char *p="abcdef";//p存放的事a的地址
printf("%d\n",sizeof (p));//8
// 这里是个地址 64位系统 地址是8个字节 其实就是计算的指针变量p的大小
printf("%d\n",sizeof (p+1));//8
// p+1就是字符b的地址 本质还是个地址 所以还是8个字节
printf("%d\n",sizeof (*p));//1
// *p其实就是字符串的第一个字符a 那就是字符类型所占大小为一个字节所以是1
printf("%d\n",sizeof (p[0]));//1
// int arr[10]; arr[0]==*(arr+0) 实际是字符a这个变量所占空间大小 字符类型占1个字节
//p[0]==*(p+0)
printf("%d\n",sizeof (&p));//8
//实际是p的地址 是地址所占字节数还是4/8
printf("%d\n",sizeof (&p+1));//8
//跳过p的下一个地址 但还是个地址 所以还是4/8
printf("%d\n",sizeof (&p[0]+1));//8
//&p[0]是a的地址 在+1 其实就是字符b的地址所以还是地址还是4/8
}
int main(){
char * p="abcdef";//p 代表a的地址
printf("%d\n", strlen(p));//6
//从a开始数直到数完这个字符串 6个字符所以是6
printf("%d\n", strlen(p+1));//5
//从b开始数直到数完这个字符串 5个字符 所以是5
printf("%d\n", strlen(*p));//报错
//*p就是a 不是个字符穿 所以汇报错
printf("%d\n", strlen(p[0]));//报错
//同上
printf("%d\n", strlen(&p));//未知
//p代表a的地址 这里拿到的是p的地址 随机值
printf("%d\n", strlen(&p+1));//未知
// &p+1 拿到的事p的下一个地址 随机值
printf("%d\n", strlen(&p[0]+1));//5
//p[0]是a 取地址p[0]就是拿到a的地址 再+1 其实就是b的地址
// 传入b的地址 往后数5个字符所以长度为5 同strlen(p+1)
}
二维数组
int main(){
int a[3][4]={ 0 };
printf("%d\n",sizeof (a));//3X4X4=48 单独放一个数组名计算数组的大小
printf("%d\n",sizeof (a[0][0]));//4
printf("%d\n",sizeof (a[0]));//16
// a[0]其实是一个第一行4个元素的一维数组的数组名
//数组名单独放在sizeof()内计算第一行的大小 所以大小是4X4=16
printf("%d\n",sizeof (a[0]+1));//8
// 数组名表示首元素地址+1 其实就是第二个元素的地址
//注意不是第二行的地址 而是第一行的第二个元素的地址
printf("%d\n",sizeof (*(a[0]+1)));//4
// 第一行第二个元素 int类型 4个字节
printf("%d\n",sizeof (a+1));//8
// a 是二维数组的数组名,没有sizeof(数组名)
// 也没有&(数组名) 所以a是首元素地址
// 而二维数组堪称一维数组时 二维数组首元素就是第一行
// 那么a+1 就是第二行这个一位数组的地址
printf("%d\n",sizeof (*(a+1)));//16
// 第二行的地址 本质上是个数组
// 所以计算这个数组的大小其实就是4X4=16
printf("%d\n",sizeof (&a[0]+1));//8
//这里毫无疑问是个地址 大小肯定是4/8 但是具体是谁的地址呢?
//第二行的地址 &a[0]取出第一行数组的地址 +1就是第二行的地址
printf("%d\n",sizeof (*(&a[0]+1)));//16
//第二行数组的地址解引用其实就是算第二行这个一维数组的大小 其实就是4X4=16
printf("%d\n",sizeof (*a));//16
//a是首元素地址 其实就是第一行地址
//*a就是第一行数组 大小为4X4=16
printf("%d\n",sizeof (a[3]));//16
//sizeof 并不会真的去访问a[3]
//所以 a[3]在sizeof中其实是等价于a[0] 、a[1]、a[2]
}
指针
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000,如下表达式的值分别为多少?
//已知,结构体Test类型的变量大小为20个字节
int main(){
p = (struct Test*)0x100000;
//0x1 就是数字1 等价于+1
printf("%p\n",p+0x1);//0x100020 就是加20个字节
//转成16进制为0x100014但是在某些电脑上是0x100020
printf("%p\n",(unsigned long )p+0x1);//0x100001
//强转p为整数 然后加1 就是0x100001
printf("%p\n",(unsigned int* )p+0x1);//0x100004
//强转p为无符号整型 +1其实就是跳过一个无符号整型
//其实就是四个字节 那也就是+4
//0x100004
return 0;
}
指针+ - 整数,取决于指针类型。
int main(){
int a[4]= {1,2,3,4};
int *ptr1=(int *)(&a+1);//
int *ptr2=(int *)((int) a+1);
//地址转换int类型 再加1其实就是地址只加了1
//如果地址是0x00000011 那么转换成int 再加1
//就是17 +1 =18 再转换为地址类型 16进制0x00000012 其实就是偏移了一个字节
//把数组表示出来
//01000000 02000000 03000000 04000000
//偏移一个字节其实就是00
printf("%x,%x",ptr1[-1],*ptr2);// 32位操作系统输出结果4,2000000
//64位操作系统*ptr2会报错
return 0;
}
int main(){
int a[3][2]={(0,1),(2,3),(4,5)};
//1 3 a[0] 代表首元素地址 就是1的地址
//5 0 a[1]
//0 0 a[2]
//这里主要需要注意这种类型的二维数组初始化 圆括号里面放的是个逗号表达式,逗号表达式的结果其实是最后一个数
//所以实际上只存了1 3 5 并且 1 3 5 是按照顺序依次放入的
int *p;
p=a[0];
printf("%d",p[0]);//1
//p[0] 等价于*(p+0) p是1的地址 +0还是1的地址所以结果是1
return 0;
}
稍微难一点的
//困难***
int main()
{
int a[5][5];
int (*p)[4];//*p是个int类型指针
// 后面跟了[4]代表它是个指向了含有4个整型元素的数组的指针 也就是数组指针
p = a;//a是数组名也就是首元素地址 也就是前第一行数组的地址
//这里赋值 两个数组的角标是不一样的 会有警告 a的值强行赋值给了p
printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);//0xfffffffc,4
//p[4][2]等价于 *(*(p+4)+2)
return 0;
}
这是a[5][5]这个二维数组
我们上面分析过了*p是个数组指针,指向了一个数组 数组里有4个整型元素。
强行将a赋值给p后其实是数组a[0]的地址赋值给p其实就是
根据上面代码注释中的分析 p[4][2] 等价于*(*(p+4)+2)
但是我们要清楚的是 p是个指向整型数组的指针 p+1 就是跳过四个元素
所以
p+4的位置如下
我们能够拿到如上图所示的红色方框四个方框其实是一个数组
(p+4)其实就是整型了那么这里再让(p+4)+2 再对其解引用 就是往后偏移两个格子
最后我们找到了p[4][2] ,a[4][2]很简单便可以得到
地址-地址 其实就是看两个地址之前有多少个元素 也就是求其中的元素个数很明显有4个元素
p[4][2] 在前 a[4][2]在后 所以应该是-4
%d 就正常打印-4即可
但是%p要注意 地址在存储时候 它不关心有无符号 ,我们按照计算机组成原理中的存储原理
-4的原码10000000 00000000 0000000 00000100
反码(符号位不变其他位置按位取反)
11111111 11111111 11111111 11111011
补码(反码+1)
11111111 11111111 11111111 11111100
%p打印的最后就是补码
其实就是 0xfffffffc
最终的结果为 0xfffffffc,-4
(注:如果是64位的操作系统 结果为0xfffffffffffffffc,-4 多了前面八个f)
int main(){
int aa[2][5]={1,2,3,4,5,6,7,8,9,10};
int *ptr1=(int*)(&aa+1);//ptr1是跳过整个数组的下一个数组首地址
int *ptr2=(int*)(*(aa+1));//aa+1指向第二行的首元素地址 解引用其实是 元素6 在转为int类型指针 实际上
//ptr2就是元素6的地址 ptr2-1往前移动一格 也就是元素5的地址 再解引用就是5。
printf("%d,%d",*(ptr1-1),*(ptr2-1));//10 5
//ptr1-1 往前移动一格 在解引用就是10
}
把这个二维数组画出来
找到ptr1的位置 其实就是aa+1
aa+1指向第二行的首元素地址 解引用其实是 元素6 在转为int类型指针 实际上
ptr2就是元素6的地址 ptr2-1往前移动一格 也就是元素5的地址 再解引用就是5。
最后答案位10,5
int main(){
char *a[]={"work","at","alibaba"};
//char *p = "abcdef"; 这里指的是把a的地址放在p中
// a是个字符指针数组,里面存的都是字符类型的指针
//a是数组名表示首元素地址 第一个元素类型位char* 首元素地址就应该是 char**
//pa++ 指的是跳过一个char*类型 则此时pa指向at的a的地址
char **pa = a;
pa++;
printf("%s\n",*pa);
//打印出来其实应该是 a开头的一个字符串 "at"
return 0;
}
最难的一道题
int main(){
char* c[]={"ENTER","NEW","POINT","FIRST"};//c是数组名 是个指针数组 里面存的都是char类型的指针
char* *cp[]={c+3,c+2,c+1,c}; //C是个char*类型 地址就应该是 char**
//cp同样是个指针数组 里面存放的都是 指向char*类型地址的指针
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;
}
把上面printf前的三行代码用图画出来如下
printf("%s\n",**++cpp);//POINT
cpp指向C+3的地址 ++cpp指向 C+2的地址
解引用一次 拿到POINT的地址 再解引用一次得到字符串“POINT”
那么此时cpp指向的是C+2
printf("%s\n",*--*++cpp+3);// ER
上面算过之后 cpp已经指向C+2的地址 ++cpp 就是指向C+1的地址
解引用一次拿到C+1 也就是NEW的地址
再-- 就是指向NEW的上一个字符串地址也就是ENTER的地址
再解引用一次得到字符串ENTER 然后再加三 注意此时已经是字符串首字母E的地址了 +3指的是 往后数3个char类型的空间 也就是到字母E 的地址 然后输出就是ER 后面\0停止
printf("%s\n",*cpp[-2]+3);// ST
cpp[-2]等价于 (cpp-2) 所以原式子cpp[-2]+3等价于 *(*(cpp-2))+3
注意 这个式子cpp并没有发生变化 上面两个cpp都有自增这里并cpp[-2]并没有改变cpp指向的地址 为了方便理解才写成*(*(cpp-2))+3
根据上面cpp指向C+1的地址 可知 cpp-2指向C+3的地址如上图
解引用一次得到C+3 也就是FIRST的地址
然后再解引用一次得到字符串FIRST首字母F的地址 再加3即往后数三个字符就得到ST
printf("%s\n",cpp[-1][-1]+1);//EW
cpp[-1][-1]+1等价于* (*(cpp-1)-1)+1
根据上面cpp指向C+1的地址 可知 cpp-1如上图指向C+2的地址
解引用一次 得到C+2也就是POINT的地址 -1 则往上移动指针指向NEW的地址
再解引用一次得到字符串NEW 然后+1 往后移动一个字符得到EW
标签:混淆,题目,int,地址,数组,printf,cpp,相关,sizeof From: https://blog.51cto.com/u_16160587/8506560