赞
踩
关于数组名:
数组名是首元素的地址
但是有两个例外:
1.sizeof
(数组名)-数组名表示的是整个数组,计算的是整个数组的大小,单位是字节
2.&数组名数组名也表示整个数组,取出的是整个数组的地址
除了以上两个例外之外所有的数组名都是首元素的地址
#include<stdio.h> int main() { int a[] = {1,2,3,4}; printf("%d\n",sizeof(a));//16 printf("%d\n",sizeof(a + 0));//4/8 printf("%d\n",sizeof(*a));//4 printf("%d\n",sizeof(a + 1));//4/8 printf("%d\n",sizeof(a[1]));//4 printf("%d\n",sizeof(&a));//4/8 printf("%d\n",sizeof(*&a));//16 printf("%d\n",sizeof(&a + 1));//4/8 printf("%d\n",&a[0]);//4/8 printf("%d\n",&a[0] + 1);//4/8 return 0; }
一个一个理解:
sizeof(a)
,a作为一个数组名单独放在sizeof
内部,计算的是数组的大小,单位是字节,16
sizeof(a+0)
,a并非单独放在sizeof
内部,也没有&,所以说数组名a就是首元素的地址,a+0
还是首元素的地址,是地址大小就是4/8
sizeof(*a)
a是首元素的地址,*a
就是首元素,sizeof(*a)
就算的是首元素的大小,4
sizeof(a+1)
,a是首元素的大小,(a+1)
是第二个元素的地址,sizeof(a+1)
计算的是指针的大小4/8
sizeof(a[1])
,a[1]就是数组的第二个元素,sizeof计算的大小是4.
sizeof(&a)
,&a取出的是数组的地址,数组的地址,也是地址,sizeof(&a)
就是4/8
sizeof(*&a)
,&a是数组的地址,是数组指针类型,*&a是对数组解引用,访问的是一个数组的大小,16
sizeof(&a+1)
,&a是数组的地址,&a+1 是跳过整个数组,&a+1还是地址,是4/8
sizeof(&a[0])
,a[0],是数组第一个元素的地址,&a[0]是第一个元素的地址,是4/8
sizeof(&a[0]+1)
,&a[0]是第一个元素的地址,&a[0]+1就是第二个元素的地址,是4/8
#include <stdio.h> #include <string.h> //字符数组 int main() { char arr[] = {'a','b','c','d','e','f'}; printf("%d\n",sizeof(arr));//6 printf("%d\n",sizeof(arr+0));//4/8 printf("%d\n",sizeof(*arr));//1 printf("%d\n",sizeof(arr[1]));//1 printf("%d\n",sizeof(&arr));//4/8 printf("%d\n",sizeof(&arr + 1));//4/8 printf("%d\n",sizeof(&arr[0] + 1));//4/8 printf("%d\n",strlen(arr));//随机值 printf("%d\n",strlen(arr + 0));//随机值 printf("%d\n",strlen(*arr));//err printf("%d\n",strlen(arr[1]));//err printf("%d\n",strlen(&arr));//随机值 printf("%d\n",strlen(&arr + 1));//随机值 printf("%d\n",strlen(&arr[0] + 1));//随机值 return 0; }
还是一个一个理解:
sizeof是计算对象或者类型创建的对象所占内存空间的大小,单位是字节
sizeof 是一个操作符,不是函数
sizeof(arr)
, arr 是数组名,并且是单独放在sizeof
内部,计算的是数组的总大小,单位是字节,6
sizeof(arr+0)
,arr是数组名,并非单独放在sizeof内部,arr表示首元素的地址,arr+0还是首元素的地址, 是地址大小就是4/8
sizeof(*arr)
,arr是首元素的地址,*arr就是首元素,sizeof计算的是首元素的大小,1
sizeof(arr[1])
,arr[1]是数组的第二个元素,sizeof(arr[1]),计算的是第二个元素的大小, 1
sizeof(&arr)
,&arr-取出的是数组的地址,sizeof(&arr)计算的是数组的地址的大小,是地址就是4/8
sizeof(&arr+1)
, &arr是数组的地址,&arr+1跳过整个数组,指向’f’的后边,本质还是一个地址,是地址就是4/8
sizeof(&arr[0]+1)
,&arr[0]是’a’的地址,&arr[0]+1是’b’的地址,是地址就是 4/8
接下来是strlen
的理解:
strlen求字符串长度的,计算的是字符串中\0之前出现的字符的个数,统计到\0为止,如果没有\0,会继续往后找
strlen 是一个库函数
strlen(arr)
,arr是数组名,但是没有放在sizeof内部,也没有取地址;arr就是首元素的地址,strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到\0,但是arr数组中没有\0,arr内存的后边是否有\0,在什么位置是不确定的,所以\0之前出现了多少个字符是随机的. 随机的
strlen(arr+0)
,arr是首元素的地址,arr+0还是首元素的地址, 随机值
strlen(*arr)
,arr 是首元素的地址,*arr是首元素’a’,97,strlen会把’a’的ASCII值97当成了地址,err会非法访问内存 err
strlen(arr[1])
,arr[1]- ‘b’ - 98 err和上一个情况一样 err
strlen(&arr)
,&arr是数组的地址,数组的地址也是指向数组起始位置,和第一个一样 随机值
strlen(&arr+1)
,&arr+1是跳过整个数组,只是起始位置变了,也是随机值 随机值
strlen(&arr[0]+1)
, &arr[0]是取出第一个地址,然后加1跳过一个元素,也是随机值 随机值
#include <stdio.h> #include <cstring> //字符串 int main() { char p[] = "abcdef"; printf("%d\n",sizeof(p));//7 printf("%d\n",sizeof(p+1));//4/8 printf("%d\n",sizeof(*p));//1 printf("%d\n",sizeof(p[1]));//1 printf("%d\n",sizeof(&p));//4/8 printf("%d\n",sizeof(&p+1));//4/8 printf("%d\n",sizeof(&p[0]+1));//4/8 printf("%d\n",strlen(p));//6 printf("%d\n",strlen(p+0));//6 printf("%d\n",strlen(*p));//err printf("%d\n",strlen(p[1]));//err printf("%d\n",strlen(&p));//6 printf("%d\n",strlen(&p+1));//随机值 printf("%d\n",strlen(&p[0]+1));//5 return 0; }
还是一个一个的理解:
对于sizeof的理解
sizeof(p)
,p是数组名,直接放在sizeof中,这里计算的是整个数组的大小,单位是字节 7
sizeof(p+1)
,这里的p表示首元素的地址,p+1表示第二个元素的地址,所以是4/8
sizeof(*p)
,*p是第一个元素,所以此处的大小是 1
sizeof(p[1])
,p[1]访问的是第二个元素,此处表示具体的第二个元素是’b’这个字符 1
sizeof(&p)
,&p还是整个数组的地址,其实也就是第一个元素的地址,是地址就是4/8
sizeof(&p+1)
,这里和也是一个地址,是地址就是 4/8
sizeof(&[0]+1)
,这里还是一个地址,是地址就是 4/8
对于strlen的理解
strlen(p)
, p表示首元素的地址,所以从第一个元素可是找,找到\0为止,所以结果是 6
strlen(p+0)
,这个和第一个一样的情况6
strlen(*p)
,*p表示的是具体的元素, strlen接收的是地址,所以这个地方 err
strlen(p[1])
,这个地方穿进去的也是具体的元素,所以还是 err
strlen(&p)
,取出的是整个数组的地址,也就是第一个元素的地址所以是 6
strlen(&p+1)
,这个地方是直接跳过整个数组,所以还是随机值
strlen(&p[0]+1)
,这个地方是将第二个元素的地址放进去,所以是 5
#include <stdio.h> #include <cstring> int main() { char *p = "abcdef"; printf("%d\n",sizeof(p));//4/8 printf("%d\n",sizeof(p+1));//4/8 printf("%d\n",sizeof(*p));//1 printf("%d\n",sizeof(p[0]));//1 printf("%d\n",sizeof(&p));//4/8 printf("%d\n",sizeof(&p+1));//4/8 printf("%d\n",sizeof(&p[0]+1));//4/8 printf("%d\n",strlen(p));//6 printf("%d\n",strlen(p+0));//6 printf("%d\n",strlen(*p));//1 printf("%d\n",strlen(p[0]));//1 printf("%d\n",strlen(&p));//随机值 printf("%d\n",strlen(&p+1));//随机值 printf("%d\n",strlen(&p[0]+1));//5 return 0; }
还是一个一个理解,先理解sizeof
sizeof(p)
,这里的p是存的是第一个元素的地址,是地址就是4/8
sizeof(p+1)
,这里的p+1指向的是第二个元素的地址,是地址就是4/8
sizeof(*p)
,*p表示的是第一个元素,是char类型的,所以是 1
sizeof(p[0])
,p[0]还是表示第一个元素,所以是 1
sizeof(&p)
,这里的&p是取出p的地址p中本来存的就是一个地址,所以这是一个二级指针,也是一个地址,是地址就是4/8
sizeof(&p+1)
,这里存的还是一个二级指针,是一个地址,地址就是4/8
sizeof(&p[0]+1)
,p[0]表示第一个元素,&p[0]表示的还是第一个元素的地址,加1之后表示的是第二个元素的地址
对于strlen的理解:
strlen(p)
, 这里是将首元素的地址存进去,所以是 6
strlen(p+0)
,这个和第一个一样,也是6
strlen(*p)
,p本来表示首元素的地址,这个地方*p表示的是第一个元素,所以 err
strlen(p[0])
,和上一个的情况一样
strlen(&p)
,p本来就是一个指针变量,这里有&,所以这是一个二级指针,也是一个地址,但是这个地址具体从哪里开始的就不知道了,所以这是一个随机值
strlen(&p+1)
,这个地方由于我们不知道&p–>&p+1,之间有无\0,所以这也是一个随机值
`strlen(&p[0]+1),&p[0],表示的是第一个元素的地址,加1之后表示的是第二个元素的地址,所以这里的结果是 5
#include <stdio.h> //二维数组 int main() { int a[3][4] = {0}; printf("%d\n",sizeof(a));//48 printf("%d\n",sizeof(a[0][0]));//4 printf("%d\n",sizeof(a[0]));//16 printf("%d\n",sizeof(a[0]+1));//4/8 printf("%d\n",sizeof(*a[0]+1));//4 printf("%d\n",sizeof(a+1));//4/8 printf("%d\n",sizeof(*(a+1));16 printf("%d\n",sizeof(&a[0]+1));4/8 printf("%d\n",sizeof(*(&a[0]+1)));16 printf("%d\n",sizeof(*a));//16 printf("%d\n",sizeof(a[3]));//16 return 0; }
sizeof(a)
,a是二维数组的数组名,数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节 48
sizeof(a[0][0])
,a[0][0]是一个整型元素,大小是四个字节, 4
sizeof(a[0])
, 把二维数组的每一行看做一维数组的时候,a[0]是第一行的数组名,第一行的数组名单独放在sizeof内部计算的是第一行的总大小,单位是字节 16
sizeof(a[0]+1)
,a[0]虽然是第一行的数组名,但是并非单独放在sizeof内部,a[0]作为第一行的数组名并非表示整个第一行这个数组,a[0]就是第一行首元素的地址,a[0]–>&a[0][0],a[0]+1跳过一个int,是a[0][1]的地址 4/8
sizeof(*(a[0]+1))
,根据上个例子可以知道,那是一个地址,所以解引用后访问的是第一行第二个元素,是 4
sizeof(a+1)
,a是二维数组的数组名,没单独放在sizeof内部,也没有&,所以a就是数组首元素的地址,二维数组我们把它想象成一维数组,它的第一个元素就是二维数组的第一行,a就是第一行的地址,a+1就是第二行的地址,是地址就是4/8
sizeof(*(a+1))
,a+1是第二行的地址,*(a+1)找到的就是第二行,所以这个表达式计算的就是第二行的大小 16
sizeof(&a[0]+1)
,&a[0]是第一行的地址,&a[0]+1就是第二行的地址,计算的是第二行的数组的大小 4/8
sizeof(*(&a[0]+1))
,根据上一个可知这个访问的是第二行,所以是 16
sizeof(*a)
,a表示首元素的地址,就是第一行的地址,访问的就是第一行,所以大小是 16
sizeof(a[3])
,代码没有问题,a[3]是二维数组的第四行,虽然没有第四行,但是类型能够确定,大小就是确定的大小就是一行的大小,单位是字节 16
任何一个表达式都有两个属性:值属性+类型属性
总结:
1. sizeof计算的时候只要遇见地址就是4/8
2. sizeof(数组名)表示的是整个数组的大小
3. &arr,也表示的是整个数组的大小
4.除了2和3之外的情况所有的情况数组名表示的都是首元素的大小
4.sizeof是一个操作符
5.strlen是一个库函数,形参是一个指针,所以传进去的应该是地址,这个库函数的作用就是看改地址到\0之间有几个字符.
#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;
}
#include <stdio.h> struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p; int main() { p = (struct Test*)0x100000; printf("%p\n", p+0x1); printf("%p\n",(unsigned long)p + 0x1); printf("%d\n",(unsigned int*)p + 0x1); return 0; }
由于p里面存的是一个地址,第一个打印的时候是一个地址,如果让地址加1就直接跳过20个字节,第二个把p这个变量强转为数字,加1的话就是加1, 第三个将p强转为int*类型,加1就是跳过4个字节.
所以最后的结果是00100014, 00100001, 00100004;
#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", ptr1[-1], *ptr2);
return 0;
}
假设下图是整个数组在内存中的存储方式:
我们可以看到最后的打印,ptr1先是&a这个是整个数组的地址,加1之后变为最后一个元素的后一个地址,然后将它强转为int类型,ptr2是现将这个地址转为为整数,然后加1最后转换为int类型造成的结果就是移动一个字节
ptr1[-1]--> *(ptr1-1)
ptr1[2]--> *(ptr1+2)
所以ptr1最后的指向就是
然后根据小端存储,结果是00 00 00 04,前面的0全部省略是4
根据理解可以知道ptr2最后的地址是如图所示:
访问4个字节就是02 00 00 00
#include <stdio.h>
int main() {
//数组的初始化内容有逗号表达式,实际上数组初始化的是1,3,5
int a[3][2] = {(0,1),(2,3),(4,5)};
int* p;
p = a[0];
printf("%d\n",p[0]);
return 0;
}
这个比较简单,a[0]代表的第一行的地址,第一行的地址又和第一个元素的地址一样,所以p中存的就是第一个元素的地址,
p[0]--->*(p+0)
,所以这个地方打印的就是第一个元素
注意:逗号表达式的结果,就是最后一个运算的结果
#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;
}
假设这个数组在内存中是这样存储的:
a[4][2]的位置在如图所示
这里a是首元素的地址,也就是第一行,将第一行的地址给p,虽然类型不匹配,但是这只是一个警告,我们无视即可,所以刚开始p指向的是第一个元素的地址,这里注意p的类型,所以p每次加1只能访问4个元素.
指针相减,求得是之间有几个元素,这里还要打印地址也就是-4,
地址在打印的时候打的是补码所以最后的结果是 FF FF FF FC,最后这里还有一个要注意的地方
#include <stdio.h>
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;
}
这个题比较简单,&aa是整个数组的地址,加1之后是最后一个元素的后一个地址
ptr2 里面实际存的是第二行的第一个地址
#include <stdio.h>
int main() {
char* a[] = {"work", "at", "alibaba"};
char** pa = a;
pa ++;
printf("%s\n", *pa);
return 0;
}
a是一个字符指针数组,因为a是首元素的地址,首元素就是一个地址,又要来保存首元素,所以要用一个二级指针,pa++,之后访问的就是第二个元素的地址(*pa)就是第二个元素的首地址,打印的自然是at
#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;
}
根据前3行我们可以画出如下的图:
接着就来一行一行的理解:
根据上图可以知道,cpp刚开始指向c+3位置的地址,加1之后指向c+2位置的地址,然后第一解引用找到c+2里面的元素是一个地址,然后在解引用还是一个地址,地址指向POINT
接着是*--*++cpp+3
由于刚才的++cpp已经影响到了cpp的指向,所以到这一行的时候cpp已经指向的不是首元素了
cpp先加加,指向c+1,然后解引用拿到c+1处的元素,然后再减减,指向c处的元素
,c处的元素指向的是存放ENTER的地址的地址,然后在解引用指向的是首元素,最后加3指向的是第二个E处的地址.所以最后打印ER
接着是*cpp[-2]+3
,
像上图那样,
*cpp[-2]+3 -- > **(cpp-2)+3
,cpp刚开始的指向如上图所示,然后减2 指向c+3处,然后经过两次解引用指向FIRST,接着加3 所以最后打印ST
最后就是cpp[-1][-1]+1
cpp刚开始的指向还是如上图所示,
cpp[-1][-1]--> *(*(cpp-1)-1)
,cpp先减1指向的是c+2处的地址解引用,拿到的是一块地址,然后减1 变成c+1, 之后在解引用,之后在加1所以最后打印的是EW
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。