文章目录
1.sizeof和strlen的对比
1.1 sizeof
在学习操作符的时候,我们学习了 sizeof
, sizeof
计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。
sizeof
只关注占用内存空间的大小,不在乎内存中存放什么数据。
#inculde <stdio.h>
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);
printf("%d\n", sizeof(int));
return 0;
}
打印:
4
4
4
sizeof
是操作符不是函数。
1.2 strlen
strlen
是C语言库函数,功能是求字符串长度。
函数原型如下:
size_t strlen ( const char * str );
统计的是从
strlen
函数的参数str
中这个地址开始向后,\0
之前字符串中字符的个数。
strlen
函数会一直向后找\0
字符,直到找到为止,所以可能存在越界查找。
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[3] = {'a', 'b', 'c'};
char arr2[] = "abc";
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
printf("%zd\n", sizeof(arr1));
printf("%zd\n", sizeof(arr2));
return 0;
}
打印:
35
3
3
4
printf("%d\n", strlen(arr1));
:因为arr1
里面没有\0
,所以会往后找到\0
才结束,打印的是随机值。
printf("%d\n", strlen(arr2));
:因为arr2
里面有\0
,所以打印3。
printf("%zd\n", sizeof(arr1));
:因为arr1
里面有3个元素,打印3。
printf("%zd\n", sizeof(arr2));
:因为arr2
里面有4个元素,打印4。
1.3 sizeof 和 strlen的对⽐
sizeof
:
sizeof
是操作符sizeof
计算操作数所占内存的大小,单位是字节- 不关注内存中存放什么数据
strlen
:
strlen
是库函数,使用需要包含头文件string.h
srtlen
是求字符串长度的,统计的是\0
之前字符的个数- 关注内存中是否有
\0
,如果没有\0
,就会持续往后找,可能会越界
2.数组和指针笔试题解析
2.1 ⼀维数组
数组名是数组首元素地址,但是有两个例外:
sizeof(数组名)
&数组名
int main(){
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));
return 0;
}
打印:
16//sizeof(a)
8//sizeof(a+0)
4//sizeof(*a)
8//sizeof(a+1)
4//sizeof(a[1])
8//sizeof(&a)
16//sizeof(*&a)
8//sizeof(&a+1)
8//sizeof(&a[0])
8//sizeof(&a[0]+1)
printf("%d\n",sizeof(a));
:16数组名
a
单独放在sizeof
内部,a
表示整个数组,计算的是整个数组的大小,单位是字节。
printf("%d\n",sizeof(a+0));
:8这里的
a
是数组名表示数组首元素地址,a+0
还是首元素的地址。这里
sizeof
计算的是首元素地址的大小,也就是4/8。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n",sizeof(*a));
:4这里的
a
是数组名表示数组首元素地址,*a
是对首元素地址的解引用,也就是首元素,就是a[0]
,所以是4个字节。
*a
就是*(a+0)
就是a[0]
。
printf("%d\n",sizeof(a+1));
:8这里的
a
是数组名表示数组首元素地址,a+1
是第二个元素的地址,也就是a[1]
的地址。这里
sizeof
计算的是第二个元素地址的大小,也就是4/8。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n",sizeof(a[1]));
:4这里的
a[1]
就是数组第二个元素,所以是4个字节。
printf("%d\n",sizeof(&a));
:8
&a
:这里的a
表示整个数组,&a
就是整个数组的地址。数组的地址也是地址,和首元素地址大小一样。是地址就是4/8个字节的长度。
因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n",sizeof(*&a));
:16这里用两种角度讲一下:
*&a
,这里的*
和&
抵消了,所以sizeof(*&a) == sizeof(a)
,计算的是整个数组的大小,单位是字节,也就是16。
&a
这是数组的地址,类型是:int(*)[4]
,也就是数组指针类型。而指针类型,决定了它解引用操作能访问多大空间。
一个整型指针解引用访问4个字节,一个字符指针解引用访问1个字节,而一个数组指针解引用访问1个数组。
所以
*&a
是对一个数组的地址的解引用,访问的是一个数组。这个数组的大小是16个字节。
printf("%d\n",sizeof(&a+1));
:8
&a
是数组的地址,&a+1
是跳过整个数组后的那个地址。既然
&a+1
是地址,那么就是4/8个字节。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n",sizeof(&a[0]));
:8
&a[0]
是数组首元素地址,是地址,那么就是4/8个字节。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n",sizeof(&a[0]+1));
:8
&a[0]
是数组首元素地址,&a[0]+1
是数组第二个元素地址,是地址,那么就是4/8个字节。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
2.2 字符数组
代码1:
char arr[] = {'a','b','c','d','e','f'};//6个字符
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
打印:
6
8
1
1
8
8
8
printf("%d\n", sizeof(arr));
:6里面有6个字符元素,所以是6。
printf("%d\n", sizeof(arr+0));
:8这里的
arr
是数组名表示数组首元素地址,arr+0
还是首元素的地址。这里
sizeof
计算的是首元素地址的大小,也就是4/8。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(*arr));
:1这里的
arr
是数组名表示数组首元素地址,*arr
是对首元素地址的解引用,也就是首元素,就是arr[0]
,所以是1个字节。
printf("%d\n", sizeof(arr[1]));
:1这里的
arr[1]
就是字符数组第二个元素,所以是1个字节。
printf("%d\n", sizeof(&arr));
:8
&arr
:这里的arr
表示整个数组,&arr
就是整个数组的地址。数组的地址也是地址,和首元素地址大小一样。是地址就是4/8个字节的长度。
因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(&arr+1));
:8
&arr
是数组的地址,&arr+1
是跳过整个数组后的那个地址。既然
&arr+1
是地址,那么就是4/8个字节。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(&arr[0]+1));
:8
&arr[0]
是数组首元素地址,&arr[0]+1
是数组第二个元素地址,是地址,那么就是4/8个字节。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
代码2:
char arr[] = {'a','b','c','d','e','f'};
printf("%zd\n", strlen(arr));
printf("%zd\n", strlen(arr+0));
printf("%zd\n", strlen(*arr));
printf("%zd\n", strlen(arr[1]));
printf("%zd\n", strlen(&arr));
printf("%zd\n", strlen(&arr+1));
printf("%zd\n", strlen(&arr[0]+1));
printf("%d\n", strlen(arr));
:随机值因为
arr
是首元素地址,我们不知道里面有没有\0
,也不知道\0
之前有多少个字符
printf("%d\n", strlen(arr+0));
:随机值因为
arr+0
是首元素地址,我们不知道里面有没有\0
,也不知道\0
之前有多少个字符而且这个打印的随机值,应该是和上面打印的随机值一样的
printf("%d\n", strlen(*arr));
:这个地方程序直接就崩了这个
*arr
是对首元素地址的解引用,得到的是arr[0]
的值。
arr[0]
是'a'
,a
的ASCII码值是97。就相当于把97传给了
strlen
,strlen
就会把97当成地址去访问。这个地方程序直接就崩了。
这里的61是16进制,换成10进制就是97。
printf("%d\n", strlen(arr[1]));
:这个地方程序直接就崩了
arr[1]
是'b'
,b
的ASCII码值是98。就相当于把98传给了
strlen
,strlen
就会把98当成地址去访问。这个地方程序直接就崩了。
printf("%d\n", strlen(&arr));
:随机值
&arr
是数组的地址,我们不知道里面有没有\0
,也不知道\0
之前有多少个字符而且这个打印的随机值,应该是和上面打印的随机值一样的
printf("%d\n", strlen(&arr+1));
:随机值
&arr
是数组的地址,&arr+1
是跳过整个数组后的那个地址。我们不知道里面有没有
\0
,也不知道\0
之前有多少个字符而且这个打印的随机值,应该比上面打印的随机值少6。因为跳过了这个数组,这个数组 里面有6个元素。
printf("%d\n", strlen(&arr[0]+1));
:随机值
&arr[0]
是数组首元素地址,&arr[0]+1
是数组第二个元素地址。我们不知道里面有没有
\0
,也不知道\0
之前有多少个字符而且这个打印的随机值,应该比上面打印的随机值少1。因为跳过了第一个数组元素。
代码3:
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
打印:
7
8
1
1
8
8
8
数组名是数组首元素地址,但是有两个例外:
sizeof(数组名)
&数组名
printf("%d\n", sizeof(arr));
:7数组名单独放在sizeof内部,计算的是数组的大小。(满足上面的第一个例外)
printf("%d\n", sizeof(arr+0));
:8
arr+0
是数组首元素地址。是地址就是4/8个字节的长度。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(*arr));
:1
*arr
是首元素 ,sizeof(*arr)
就是1。
printf("%d\n", sizeof(arr[1]));
:1
arr[1]
是数组第二个元素,sizeof(arr[1])
就是1。
printf("%d\n", sizeof(&arr));
:8
&arr
是数组的地址。是地址就是4/8个字节的长度。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(&arr+1));
:8
&arr+1
是跳过整个数组,指向数组后的那个位置的地址。是地址就是4/8个字节的长度。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(&arr[0]+1));
:8
&arr[0]
是首元素地址,&arr[0]+1
是数组第二个元素的地址。是地址就是4/8个字节的长度。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
代码4:
char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
printf("%d\n", strlen(arr));
:6
arr
是数组首元素地址,strlen
是从第一个元素开始统计\0
之前的字符个数。
printf("%d\n", strlen(arr+0));
:6
arr+0
是数组首元素地址,strlen
是从第一个元素开始统计\0
之前的字符个数。
printf("%d\n", strlen(*arr));
:这个地方程序直接就崩了
*arr
是数组第一个元素,也就是'a'
,a
的ASCII码值是97。就相当于把97传给了
strlen
,strlen
就会把97当成地址去访问。这个地方程序直接就崩了。
printf("%d\n", strlen(arr[1]));
:这个地方程序直接就崩了
arr[1]
是数组第二个元素,也就是'b'
,b
的ASCII码值是98。就相当于把98传给了
strlen
,strlen
就会把98当成地址去访问。这个地方程序直接就崩了。
printf("%d\n", strlen(&arr));
:6
&arr
是数组的地址,数组的地址和数组首元素的地址是指向同一个位置的。那么strlen也是从第一个元素的位置开始向后访问的。
printf("%d\n", strlen(&arr+1));
:随机值
&arr
是数组的地址,&arr+1
是跳过整个数组后的那个地址。我们不知道里面有没有
\0
,也不知道\0
之前有多少个字符而且这个打印的随机值,应该比上面打印的随机值少6。因为跳过了这个数组,这个数组 里面有6个元素。
printf("%d\n", strlen(&arr[0]+1));
:5
&arr[0]
是首元素地址,&arr[0]+1
是数组第二个元素的地址。那么strlen是从第二个元素的位置开始向后访问的。
代码5:
char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));
打印:
8
8
1
1
8
8
8
数组名是数组首元素地址,但是有两个例外:
sizeof(数组名)
&数组名
printf("%d\n", sizeof(p));
:8
p
是指针变量。sizeof(p)
计算的是指针变量p
的大小,4/8个字节。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(p+1));
:8
p
是指针变量。我们假设p
的地址是:0x0012ff40
,p+1
的地址是:0x0012ff41
也就是第二个元素的地址。
sizeof(p+1)
计算的是二个元素的地址的大小,4/8个字节。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(*p));
:1
p
的类型是char*
,*p
解引用只能访问一个字符,所以sizeof(*p)
为1。
printf("%d\n", sizeof(p[0]));
:1
p[0]
就是*(p+0)
也就是*p
,*p
解引用只能访问一个字符,所以sizeof(*p)
为1。
printf("%d\n", sizeof(&p));
:8
&p
是指针变量p
的地址。是地址就是4/8个字节的长度。因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(&p+1));
:8
&p
的类型是char* *
,也就相当于char* *ptr = &p;
前面的
char*
说明ptr指向的对象是char*
,这里的*ptr
说明ptr
是一个指针。
&p+1
相当于ptr+1
,也就相当于它跳过了一个char*
的指针。
跑到了图中橙色箭头的地方。和b没什么关系。
&p
是p
的地址,&p+1
是跳过p
变量,指向了p
的后面。是地址就是4/8个字节的长度。
因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n", sizeof(&p[0]+1));
:8
p[0]
就是*(p+0)
就是*p
&p[0]
就是*&p[0]
,实际上就是p
。
&p[0]+1
就是b
的地址。是地址就是4/8个字节的长度。
因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
代码6:
char *p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
printf("%d\n", strlen(p));
:6
p
是指针变量。p
里面存的是a
的地址,指向的是字符串首元素的地址。
printf("%d\n", strlen(p+1));
:5
p
是指针变量。p
里面存的是a
的地址,p+1
里面存的是b
的地址,指向的是字符串第二个元素的地址。
printf("%d\n", strlen(*p));
:程序崩溃
*p
是数组第一个元素,也就是'a'
,a
的ASCII码值是97。就相当于把97传给了
strlen
,strlen
就会把97当成地址去访问。这个地方程序直接就崩了。
printf("%d\n", strlen(p[0]));
:程序崩溃
p[0]
也就是*(p+0)
也就是*p
。
*p
是数组第一个元素,也就是'a'
,a
的ASCII码值是97。就相当于把97传给了
strlen
,strlen
就会把97当成地址去访问。这个地方程序直接就崩了。
printf("%d\n", strlen(&p));
:随机数
&p
是指针变量p
的地址。如果
p
的地址是0x0012ff40
,那么就是去p
的内存空间里面去找的。我们不知道里面有没有
\0
,也不知道\0
之前有多少个字符
printf("%d\n", strlen(&p+1));
:随机值
&p
是指针变量p
的地址。&p+1
是指向指针变量p
后面的地址。我们不知道里面有没有
\0
,也不知道\0
之前有多少个字符
printf("%d\n", strlen(&p[0]+1));
:5
&p[0]
是首元素地址,&p[0]+1
是第二个元素地址。
2.3 ⼆维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));
打印:
48
4
16
8
4
8
16
8
16
16
16
讲这个前面我们先复习一下:
我们一般说的时候是理解上面的那个3x4的图的,但是实际上内存里是连续存放的,也就是说内存里是像下面这样1x12这么放的。
数组名是数组首元素地址,但是有两个例外:
sizeof(数组名)
&数组名
printf("%d\n",sizeof(a));
:48
a
作为数组名,单独放在sizeof
内部,a
表示的是整个数组,计算的是整个数组的大小,单位是字节。三行四列12个元素。每个元素乘4。
printf("%d\n",sizeof(a[0][0]));
:4
a[0][0]
是第一行第一列元素,大小是4个字节。
printf("%d\n",sizeof(a[0]));
:16
a[0]
是第一行的数组名,单独放在sizeof
内部,计算的是数组的大小。第一行4个元素,也就是4x4是16个字节。
printf("%d\n",sizeof(a[0]+1));
:8这里面
a[0]
是第一行的数组名,但是没有单独放在sizeof
内部。所以这个a[0]
表示的是首元素地址。&a[0][0]
也就是第一行第一个数组元素的地址。
a[0]+1
就等价于&a[0][0]+1
就等价于&a[0][1]
。也就是
a[0][1]
的地址。是地址就是4/8个字节的长度。
因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n",sizeof(*(a[0]+1)) );
:4
a[0]+1
就等价于&a[0][0]+1
就等价于&a[0][1]
。
*(a[0]+1)
就相当于*&a[0][1]
就相当于a[0][1]
。一个元素,也就相当于1x4等于4个字节。
printf("%d\n",sizeof(a+1));
:8
a
是二维数组的名字,并没有单独放在sizeof
内部,
a
表示的是首元素的地址,也就是第一行的地址。类型是int(*)[4]
a+1
是第二行的地址。是地址就是4/8个字节的长度。
因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n",sizeof(*(a+1)) );
:16下面从两个角度讲一下:
*(a+1)
其实就是a[1]
,也就是第二行的数组名,单独放在sizeof
内部,计算的是第二行的大小。第二行4个元素,也就是4x4等于16个字节。
a+1
是第二行的地址,类型是int(*)[4]
,解引用访问的是这个数组a[1]
,第二行4个元素,也就是4x4等于16个字节。
printf("%d\n",sizeof(&a[0]+1));
:8
&a[0]
是第一行的数组名,&数组名,表示第一行的地址,&a[0]+1
就是第二行地址,是地址就是4/8个字节的长度。
因为我编译的环境是x64,所以是8,如果是x86的环境下编译,那么就是4。
printf("%d\n",sizeof(*(&a[0]+1)) );
:16
&a[0]
是第一行的数组名,&数组名,表示第一行的地址,&a[0]+1
就是第二行地址,
*(&a[0]+1))
第二行的地址解引用,就是第二行。第二行4个元素,也就是4x4等于16个字节。
printf("%d\n",sizeof(*a));
:16下面从两个角度讲一下:
a
是首元素的地址,也就是第一行的地址 ,*a
就是第一行的元素。第一行4个元素,也就是4x4等于16个字节。
*a
就是*(a+0)
就是a[0]
,就是第一行的元素。第一行4个元素,也就是4x4等于16个字节。
printf("%d\n",sizeof(a[3]));
:16乍一看越界了,但其实程序还是可以运行的。
为什么呢?
因为
sizeof
内部的表达式是不会真实计算的。就像
a[3]
是第四行的数组名,但他不会真实去访问第四行,也就谈不上越界。那这里为什么会算出结果呢?
因为
a[3]
第四行的数组名,他的类型是int[4]
(如果第四行存在的话 , 就是这个类型)第四行4个元素,也就是4x4等于16个字节。(也就相当于说
sizeof
是根据类型来推断的)
数组名的意义:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
3.指针运算笔试题解析
3.1 题⽬1
程序的结果是什么?
#include <stdio.h>
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
为什么呢?
3.2 题⽬2
在X86环境下
假设结构体的大小是20个字节
程序的结果是什么?
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
打印:
00100014
00100001
00100004
为什么呢?
-
指针+1和类型有关系。
-
整数+1就是+1。
printf("%p\n", p + 0x1);
p + 0x1
这个0x1
是十六进制,也就是说这里其实是p + 1
p
是结构体指针,+1
就是跳过一个结构体,也就是20个字节(20字节这个条件前面给了)这里相当于就是0x100014
printf("%p\n", (unsigned long)p + 0x1);
把指针变量
p
强制类型转换成unsigned int
,也就相当于整数+1。0x100001
printf("%p\n", (unsigned int*)p + 0x1);
+1是跳过一个
unsigned int
类型的变量,是4个字节。也就是0x100004
3.3 题⽬3
程序的结果是什么?
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
打印:
1
为什么呢?
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
注意这里面,用的是小括号,这就意味着这些小括号里其实是逗号表达式。
我们可以认为上面的代码就是:
int a[3][2] = { 1, 3, 5 };
数组里面:
1 3 5 0 0 0
a[0]
是首元素地址,p
是一个指针变量。
p[0]
就等价于*(p+0)
就等价于*p
。也就是
1
。
3.4 题⽬4
假设环境是x86环境,程序输出的结果是啥?
#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;
}
打印:
FFFFFFFC,-4
为什么呢?
int(*p)[4];
p
是一个数组指针,指向的类型是int
,p
指向的数组有4个元素。
p = a;
a
的类型是int(*)[5]
p
的类型是int(*)[4]
最后会采用
p
的类型。
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
p[4][2]
和*(*(p+4)+2)
等价。
*(p+4)
就是p+4
和p+5
之间的那段。
p[4][2]
和a[4][2]
之间差了4个元素,所以&p[4][2] - &a[4][2]
等于-4
。
%d
是十进制形式
%p
是补码形式//-4 //原码 10000000 00000000 00000000 00000100 //反码 11111111 11111111 11111111 11111011 //补码 11111111 11111111 11111111 11111100
写成16进制就是:
0xFF FF FF FC
3.5 题⽬5
程序的结果是什么?
#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;
}
打印:
10,5
为什么呢?
3.6 题⽬6
程序的结果是什么?
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
打印:
at
为什么呢?
char *a[] = {"work","at","alibaba"};
这个是
char*
的指针数组,里面有3个元素,分别是w
的地址,at
里面a
的地址,alibaba
里面a
的地址。(也就是三个字符串的首地址)
char**pa = a;
a
表示首元素地址。char**
这个二级指针变量pa
里面存的是a
的第一个元素的地址。即指向字符串work
的指针。
pa++;
这行代码将
pa
指针向前移动一个位置,即指向数组a
的第二个元素,也就是指向字符串at
的指针。
printf("%s\n", *pa);
这行代码通过解引用
pa
指针(即*pa
),打印出它当前指向的字符串at
,并换行。
%s
打印的是地址,是从这个地址往后打印字符串。
3.7 题⽬7
程序的结果是什么?
#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;
}
打印:
POINT
ER
ST
EW
为什么呢?
printf("%s\n", **++cpp);
然后%s
往后打印,打印POINT
。
printf("%s\n", *--*++cpp+3);
然后%s
往后打印,打印ER
。
printf("%s\n", *cpp[-2]+3);
然后%s
往后打印,打印ST
。
printf("%s\n", cpp[-1][-1]+1);
然后%s
往后打印,打印EW
。