分析
(一)
(1)一维数组
int a[]={1,2,3,4}; char arr[]={'a','b','c','d','e','f'};//sizeof int main() { //数组名是首元素地址 //除了 //1.sizeof(数组名)-数组名表示整个数组 //2.&数组名-数组名表示整个数组 // //一维数组 int a[]={1,2,3,4}; printf("%d\n",sizeof(a));//sizeof(数组名)-计算的是数组总大小-单位是字节-16 printf("%d\n"sizeof(a+0));//32位平台,4个字节,64位平台8个字节 printf("%d\n",sizeof(*a));//4,a是首元素地址,首元素的解引用4个字节 printf("%d\n",sizeof(a+1));//4,与sizeof(a+0)相同,只是a+1是第二个元素地址 printf("%d\n",sizeof(&a));//取地址a取出的是数组的地址,数组的地址也是地址,地址的大小都是4个/8个字节 printf("%d\n",sizeof(*&a));//整个数组的地址解引用,返回的是整个数组,sizeof计算的是数组的大小,单位是字节,所以为16,等价于sizeof(a) printf("%d\n",sizeof(&a+1));//跳过了整个数组,但是(&a+1)还是一个地址,补充,若是a+1,则只是跳过一个元素 printf("%d\n",sizeof(&a[0]));//是地址就是4/8个字节 printf("%d\n",sizeof(&a[0]+1));//4/8个字节 } char arr[]={'a','b','c','d','e','f'}; printf("%d\n",sizeof(arr));//sizeof计算的是整个数组的大小,6*1=6个字节 printf("%d\n",sizeof(arr+0));arr没有单独放在sizeof中,所以arr表示的是首元素的地址(arr+0)也是首元素的地址,是地址就是4/8个字节 printf("%d\n",sizeof(*arr));//arr是首元素地址,*arr就是首元素,首元素是一个字符,所以是一个字节 printf("%d\n",sizeof(&arr));//取地址arr,虽然是数组的地址,但是还是地址4/8 printf("%d\n",sizeof(&arr+1));//与下图的含义相同,跳过整个数组后的地址,但是还是地址4/8 printf("%d\n",sizeof(&arr[0]+1));//取第一个元素地址+1就是第二个元素的地址,如果是arr[0]+1则跳过一个元素
char arr[]={a,b,c,d,e,f};//strlen
char arr[]={a,b,c,d,e,f}; printf("%d\n",strlen(arr));//遇到'\0'才会停,这里不知道在哪里会遇到'\0'所以是随机值 printf("%d\n",strlen(arr+0)); //数组名是首元素地址,+0还是首元素地址,所以和第一个答案相同,随机值 printf("%d\n",strlen(*arr));//arr是首元素地址,*arr是首元素,%d表示是97,那么strlen想要的是地址,就会把97当作地址,向后取元素,那么就是非法访问 printf("%d\n",strlen(arr[1]));//也是错误的 printf("%d\n",strlen(&arr));// 虽然取的是数组的地址,但是也是从数组首元素开始取,所以也是随机值 printf("%d\n",strlen(&arr+1));//跳过的是一个数组,也就是从‘f’之后开始找,与&arr的差值是6,随机值-6 printf("%d\n",strlen(&arr[0]+1));//从'b'开始取地址,比之前的地址(&arr)少1,随机值-1
char arr[]="abcdef"; //sizeof
int main() { //char arr[]={'a','b','c'}; char arr[]= "abcdef"; printf("%d\n",sizeof(arr));//数组名单独放在sizeof中,这时候的数组表示整个数组,整个数组大小,那这个数组是7个元素,"abcdef\0",7*1=7 printf("%d\n",sizeof(arr+0));//首元素地址+0还是首元素地址,4/8个字节 printf("%d\n",sizeof(*arr));//既没有取地址也没有单独放在sizeof后面,所以是首元素地址,首元素地址解引用就是首元素,那么他的大小就是1 printf("%d\n",sizeof(arr[1]));//第二个元素,大小也是1 printf("%d\n",sizeof(&arr));//取地址arr,虽然是数组的地址,但是也是地址,地址大小是4/8 printf("%d\n",sizeof(&arr+1));//跳过了一整个数组,但是还是一个数组的地址,4/8 printf("%d\n",sizeof(&arr[0]+1));//取出第一个元素的地址+1,指向第二个元素的地址,还是一个地址4/8 return 0; }
char arr[]="abcdef";//strlen
int main() { char arr[]="abcdef"; printf("%d\n",strlen(arr));//遇到'\0'就停止,所以长度为6 printf("%d\n",strlen(arr+0));//首元素地址+0,还是首元素地址,那么依然是从'a'向后数,得到6个元素 printf("%d\n",strlen(*arr));//strlen接收的是一个地址,而这里是解引用arr,*arr表示传进去的是字符a,a的ascll码是97,97作为一个地址使用就是非法访问 printf("%d\n",strlen(arr[1]));//与上面问题相同,非法访问 printf("%d\n",strlen(&arr));//相当于strlen(arr),取地址arr,从a开始向后取,直到遇到'\0' //但是这里会警告,因为&arr ---数组的地址---数组指针 char(*p)[7]=&arr //&arr的类型是char(*)[7] //而strlen的地址是const char * //强制赋值也能输出,输出长度是6 printf("%d\n",strlen(&arr+1));//与上面相同,也会发出警告,&arr+1,跳过这个数组后还是数组的地址,但是这里是随机值,因为跳过整个字符数组了 printf("%d\n",strlen(&arr[0]+1));//遍历bcdef这5个字符,所以长度是5 }
char *p="abcdef";//sizeof
char *p="abcdef";//其实里面只放了a的地址 printf("%d\n",sizeof(p));//计算指针变量p的大小,4/8个字节 printf("%d\n",sizeof(p+1));//p里面存放的是a的地址,那么p+1就是b的地址,4/8个字节 printf("%d\n",sizeof(*p));//*p其实就是字符串的第一个字符,就是字符a,大小为1 printf("%d\n",sizeof(p[0]));//int arr[10]; arr[0]==*(arr+0) p[0]=*(p+0),就是第一个元素字符a,大小为1 printf("%d\n",sizeof(&p));//&p,取地址p,是地址就是4/8个字节 printf("%d\n",sizeof(&p+1));//取地址还是4/8个字节 printf("%d\n",sizeof(&p[0]+1));//&p[0]='a'的地址,&p[0]+1就是p的地址 char *p="abcdef";//strlen char *p="abcdef";//其实里面只放了a的地址 printf("%d\n",strlen(p));//p中包含的是a的地址,就是相当于将a的地址传过去,从a向后数6个字符 printf("%d\n",strlen(p+1));//从b向后数就是5 printf("%d\n",strlen(*p));//报错,把97作为地址传过去 printf("%d\n",strlen(p[0]));//也报错
特殊说明
printf("%d\n",strlen(&p));
这里是从p往后数,不知道什么时候会遇到’\0‘,而不是从a往后数,所以是随机值
例如a这块空间的地址是0x0012ff44,将其存放到p中,小端倒着存(44 ff 12 00);在00前出现了3个字符,strlen=3;
printf("%d\n",strlen(&p+1));//跳过存放a的地址的空间后,继续往后数,也是随机值
printf("%d\n",strlen(&p[0]+1));//取第一个元素地址+1,就是第二个元素的地址,长度是5
(2)二维数组
int a[3][4]={0}; printf("%d\n",sizeof(a));//计算的是数组总大小,3*4*4=48 printf("%d\n",sizeof(a[0][0]));//一个整型元素的大小,4 printf("%d\n",sizeof(a[0]));//a[0]相当于第一行作为一维数组的数组名 //sizeof(arr[0])把数组名单独放在suzeof()内,计算的是第一行的大小 printf("%d\n",sizeof(a[0]+1));//a[0]没有单独放在sizeof内部,前面也没有&符号,所以a[0]代表首元素地址,第一行第一个元素地址+1,就是a[0][1]的地址,地址大小是4/8个字节 printf("%d\n",sizeof(*(a[0]+1)));//a[0][1]的地址解引用就是第二个元素的地址 printf("%d\n",sizeof(a+1));//a是二维数组的数组名,没有sizeof(数组名),也没有&(数组名),所以a是首元素地址,而二维数组的首元素是其第一行(把二维数组看成一维数组),a+1就是第二行数组的地址,4/8 printf("%d\n",sizeof(*(a+1)));//第二行的地址解引用,就是4*4=16 printf("%d\n",sizeof(&a[0]+1));//&a[0]表示第一行的地址,第一行的地址+1,就是第二行地址,4/8 printf("%d\n",sizeof(*(&a[0]+1)));//第二行的地址解引用,就是4*4=16 printf("%d\n",sizeof(*a));//a是首元素地址,就是第一行地址,*a就是第一行大小,16 printf("%d\n",sizeof(a[3]));//虽然没有第四行的数据,a[3]不会进行真的访问,所以和a[0]的大小相同,16
以下代码输出结果
解析
(&a+1)是跳过整个数组,但是代表数组的地址,在进行强制类型转换得到int *ptr=(int *)(&a+1)
(ptr-1)指向的是5的地址,解引用5的地址,就是5
(a+1)中的a表示的是第一个元素的地址,所以*(a+1)就是解引用2的地址
答案
2,5
解析
p+0x1,p代表整个结构体的地址,p+1代表跳过整个结构体,0x100000+20->转换为16进制,%p打印地址,需要显示前面的00,即0x00100014
(unsigned long)p,转换成10进制--1048576+1=1048577---再转换为16进制,即0x100001
(unsigned int*)p,p强转为无符号整型,无符号整型是4个字节,所以0x100000+4=0x100004
答案
0x00100014
0x00100001
0x00100004
解析
对于(&a+1)
&a表示整个数组的地址加1
对于(int *)(int)a+1
假设首元素的地址是0x00 00 00 05,强制类型转化为int,就是5,5+1=6
再强转化为地址0x00 00 00 06
ptr1[-1]=*(ptr1+(-1))=*(ptr1-1),向前挪动一个整型指针,所以打印0x4
*ptr2解引用可以向后访问4个字节
对于上图来说就是02 00 00 00
答案
4
02 00 00 00
解析
(0,1),(2,3),(4,5)这些逗号表达式的结果只算最后一个即1,3,5
a[0]是第一行的数组,表示首元素的地址,就是1的地址,p[0]=*(p+0)=*p,所以就是1
答案
1
解析
int(*p)[4]:p是指向4个整形元素的指针
p=a;a代表首元素的地址,就是第一行的地址,则a的类型是int(*)[5],p的类型是int(*),会出现警告,这里强行将a赋给了p,所以a的结果赋给p,类型不赋给p
p[4][2]:*(*(p+4)+2),其位置表示下图绿色方块
地址相减,得到的是指针和指针之间的元素,&p[4][2]小,&a[4][2]大,所以%d,是-4
对于%p(地址),表示的是无符号数,其地址就是-4的补码的值fffc
答案
0x FF FF FF FC
-4
解析
整个二维数组的地址+1,相当于跳过整个二维数组
&aa+1是一个数组指针,不能直接赋给 int * ptr
对于*(aa+1)中的aa代表的是一维数组的地址,就是第一行的地址,第一行的地址+1,就指向第二行的地址,*(aa+1)对(aa+1)解引用操作,就相当于找到了第二行
*(aa+1)=aa[1],aa[1]是第二行的数组名,就是第二行的首元素地址,他的类型就是int *
虽然写了(int *)(*(aa+1)):但是这里的强制类型转换是多余的
*(ptr1-1),ptr1本来指向的是10后面的位置,(ptr1-1)就跳到了10的位置,*(ptr1-1)就是10
*(ptr2-1),ptr2-1指向5的地址,解引用得到5
补充
在进行arr[2]计算的时候,arr是首元素地址,arr的地址+2,就是向后偏移两个整型,再解引用,即*(arr+2)
答案
10
5
提示
这里是将常量字符串“abcdef”首字符a的地址放到指针变量p中
回到题中,a[i]是字符指针,那么里面存的是w,a,a(三个字符串首字符的地址)
上图描述的是,a数组的每一个元素是char *,数组名表示的是首元素地址,对于a来说首元素的地址是绿色框框的地址,pa的类型是char**,即char *的地址
char* *pa:*pa表示pa是一个指针,char *表示pa指向的元素是char类型的指针,pa++跳过的是char*这样的变量
对于
int * p:* p表示p是一个指针,int表示指向的变量是整型
p+1:跳过一个人int型变量
同理
char **pa指向的是char *的变量
pa++跳过的是一个char *
数组里面的第一个元素是char *,那么+1就是指向第二个元素,pa指向的是第二个元素的地址,*pa就是第二个元素的内容,就是char *的地址,即a的地址,a的地址用%s打印,打印的就是at类型的字符串
解析
数组c
上图表示,将E的地址传入第一个char *,将N的地址传入第二个char,以此类推
数组cp
c是数组名,表示首元素的地址,cp指向的是c数组的第四个元素的地址,以此类推
cpp
cpp指向的是cp数组首元素的地址
第一个 **++cpp
++cpp,cpp指向数组的第二个元素,*++p,相当于就是对第二个元素的地址解引用,就是拿到c+2,c+2又是第三个char*地址,**++p就是char*地址解引用,即char *,就是p的地址,再用%s形式打印,那么就会打印“point”
第二个 *--*++cpp+3
++cpp,cpp指向了cp数组第三个元素的地址,就是c+1的地址解引用,得到c+1,再--,得到c,而c指向的是c数组的第一个元素的地址,再*解引用,得到第一个元素的内容,即char *的内容,就是E的地址,再+3,就得到第二个E,以%s的形式打印就是ER
第三个*cpp[-2]+3---->**(cpp+(-2))+3---->**(cpp-2)+3
cpp-2得到cp第一个元素的地址,*(cpp-2)得到第一个元素的内容,即c+3,就是c数组第四个元素的地址,再**(cpp-2),得到char *,就是F的地址,+3就是S的地址
第四个 cpp[-1][-1]+1---->*(*(cpp-1)-1)+1
cpp-1指向cp第二个元素的地址,解引用操作得到c+2,c+2是c数组第3个元素的地址,这时已经进行到*(cpp-1),接下来再-1,得到c数组第2个元素的地址,再解引用,得到N的地址,N的地址+1,就是E的地址,再用%s打印,得到的是‘EW’