【C语言-指针——指针案例分析】

数组名什么时候代表首元素地址?什么时候代表整个数组?

整个数组
1 . sizeof(数组名),这里的数组名表示整个数组。
2 . &数组名,这里的数组名表示整个数组。取出的是整个数组的地址


首元素地址
除了上面两种情况外,其他情况都是首元素地址。

指针和一维数组之间

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));
  1. 首先,sizeof()函数是用来计算对象占用内存大小的(字节)。
  2. sizeof(数组名),这里的数组名表示整个数组。sizeof()计算的是整个数组的大小。
  3. &数组名,这里的数组名表示整个数组。取出的是整个数组的地址
  4. 除以上两种情况之外,所有的数组名都是数组首元素的地址。

32位机器中,地址是4个字节,64位机器中,地址是8个字节(4*8=32,8*8=64)

1


printf(“%d\n”,sizeof(a));

结果是:16(字节)
计算的是数组大小,大小为16。

2


printf(“%d\n”,sizeof(a+0));

结果是:4/8(字节)
a没有单独放在sizeof()中,也没有&的存在。
因此a表示的是数组首元素的地址。
所以a+0是数组第一个元素的地址。地址占用的内存是4/8个字节。

3


printf(“%d\n”,sizeof( * a));

结果是:4(字节)
*a指的是数组首元素。
计算的是数组首元素的大小,单位是字节。所以结果是4。

4


printf(“%d\n”,sizeof(a+1));

结果是:4/8(字节)
a+1是第二个元素的地址,地址的大小就是4/8。

5


printf(“%d\n”,sizeof(a[1]));

结果是:4(字节)
a[1]指的是第二个元素
这里计算的是第二个元素的大小,大小为4。

6


printf(“%d\n”,sizeof(&a));

结果是:4/8(字节)
&a是整个数组的地址,整个数组的地址也是地址,地址的大小就是4/8字节。
&a是数组的指针,那么它的类型是数组指针。(&a => 类型:int (*) [ 4 ] )

7


printf(“%d\n”,sizeof( * &a));

结果是:16(字节)
这里计算的是整个数组的大小,所以是16。
数组指针,加1可以跳过整个数组,解引用可以访问整个数组。
&a是数组的地址,&a就是拿到了数组。(&a =>a,a就是数组名)

sizeof(*&a)=>sizeof(a)

8


printf(“%d\n”,sizeof(&a+1));

结果是:4/8(字节)
&a是整个数组的地址,&a+1指的是:跳过整个数组,指向数组后面的空间。
数组后面空间地址,大小为4/8。(字节)

9


printf(“%d\n”,sizeof(&a[0]));

结果是:4/8(字节)
&a[0]其实就是首元素地址。
计算的是首元素地址的大小,大小为4/8。

10


printf(“%d\n”,sizeof(&a[0]+1));

结果是:4/8(字节)
&a[0]+1指的是第二个元素地址。
计算的是第二个元素地址的大小,大小为4/8。

*&a出现在sizeof()中,a也代表整个数组。
*&a在sizeof()之外,那么a代表的是数组首元素地址。
*&a只有放在sizeof()中,a才代表整个数组。

关于sizeof():
sizeof()的返回值是size_t
size_t <==> unsigned int,无符号整型的大小是4个字节。

指针和字符数组

char arr[] = {'a','b','c','d','e','f'};
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));
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));

壹(1)


printf(“%d\n”, sizeof(arr));

结果是:6(字节)
sizeof(数组名),这里的数组名表示整个数组。sizeof()计算的是大小。


printf(“%d\n”, strlen(arr));

结果是:随机值
我们不知道arr前面后面到底存放着什么,前面的空间和后面的空间我们都不知道有什么。
可以理解为:无法明确strlen的指向对象。
strlen的判定停止条件是遇到 ’ \0 ’ ,而实际上,内存中的 ’ f ’ 后面的东西是未知的,是随机的。从地址arr处往后,下一个字符碰到 ‘ \0 ’ 是一个概率问题。因此结果是6或者6以上的任何数。统一为随机值。)
strlen的判定原理,使得结果出现随机值。

壹(2)


printf(“%d\n”, sizeof(arr+0));

结果是:4/8(字节)
arr+0是数组首元素地址,大小为4/8。


printf(“%d\n”, strlen(arr+0));

结果是:随机值
strlen的判定原理,使得结果出现随机值。

壹(3)


printf(“%d\n”, sizeof( * arr));

结果是:1(字节)
*arr,解引用出来是数组首元素。大小为1。


printf(“%d\n”, strlen( * arr));

strlen(*arr)相当于:strlen( ’ a ’ ) <=> strlen( 97 )
结果是:err——非法访问(出错)
只能访问被分配过的内存。

壹(4)


printf(“%d\n”, sizeof(arr[1]));

结果是:1(字节)
arr[1]是数组第二个元素,大小为1。


printf(“%d\n”, strlen(arr[1]));

strlen(arr[1])相当于:strlen( ’ b ’ ) <=> strlen( 98 )
结果是:err——非法访问(出错)
只能访问被分配过的内存。

壹(5)


printf(“%d\n”, sizeof(&arr));

结果是:4/8(字节)
&arr,这里指的是整个数组的地址,大小为4/8。


printf(“%d\n”, strlen(&arr));

结果是:随机值
strlen传参传的是指针,arr和&arr的地址值都一样。从地址arr处往后,下一个字符碰到 ‘ \0 ’ 是一个概率问题。
strlen的判定原理,使得结果出现随机值。
(此代码还会出现类型不匹配问题,strlen的参数类型是const char *,而&arr的类型是char * [6] )

壹(6)


printf(“%d\n”, sizeof(&arr+1));

结果是:4/8(字节)
&arr+1,这里指的是跳过整个数组,指向数组后边的地址,地址大小都为4/8。


printf(“%d\n”, strlen(&arr+1));

结果是:随机值
strlen的判定原理,使得结果出现随机值。
&arr是数组的地址,&arr+1是跳过整个arr数组,后面紧挨着的地址,求字符串长度更是随机值了。

壹(7)


printf(“%d\n”, sizeof(&arr[0]+1));

结果是:4/8(字节)
&arr[0]+1指的是数组第二个元素的地址,大小为4/8。


printf(“%d\n”, strlen(&arr[0]+1));

结果是:随机值
strlen的判定原理,使得结果出现随机值。
&arr[0]+1指的是数组第二个元素 ’ b ’ 的地址,从 ’ b ’ 的地址处往后,下一个字符碰到 ‘ \0 ’ 是一个概率问题。


我们需要额外了解的是:(字符串和字符数组的区别
内存存储方式不同:
字符数组在内存中是连续存储的一块区域,每个元素占用一个字节的内存空间。字符串在内存中也是连续存储的一块区域,但以空字符’\0’结尾,表示字符串的结束,每个字符占用一个字节的内存空间。
简单点来说:

  1. 字符串规定以 ’ \0 ’ 结尾。
  2. 字符数组没有规定后面必须加 ’ \0 ’ 。

sizeof只关注占用内存空间的大小,单位是字节,不关注内存中的内容。
sizeof是操作符
strlen是求字符串长度的,统计的是\0之前出现的字符串个数,一定要找到\0才结束,所以可能存在越界问题
strlen是库函数


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));
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));

贰(1)


printf(“%d\n”, sizeof(arr));

结果是:7(字节)
字符串arr中除了 abcdef 这六个字母,还有最后一个末尾默认要跟的 \0 。
数组名单独放在sizeof内部,计算的是数组总大小,单位是字节。
所以一共就只有7个字节的大小。


printf(“%d\n”, strlen(arr));

结果是:6
此处的arr代表首元素地址,不是整个数组。f 后面就是\0。
strlen函数,统计 ‘ \0 ’ 之前的元素个数。

贰(2)


printf(“%d\n”, sizeof(arr+0));

结果是:4/8(字节)
arr+0是首元素地址,地址大小是4/8。


printf(“%d\n”, strlen(arr+0));

结果是:6
arr+0代表首元素地址。f 后面就是\0。
strlen函数,统计 ‘ \0 ’ 之前的元素个数。

贰(3)


printf(“%d\n”, sizeof( * arr));

结果是:1(字节)
本质来说,arr就是数组首元素地址,所以*arr是数组首元素,大小为一个字节。


printf(“%d\n”, strlen( * arr));

结果是:err
*arr是字符a,是97。
strlen的参数类型是const char *
( ’ a ’ <=> 97)所以传给strlen的是一个非法地址。
造成非法访问。

贰(4)


printf(“%d\n”, sizeof(arr[1]));

结果是:1(字节)
arr[1]是数组的第二个元素,大小是1字节。


printf(“%d\n”, strlen(arr[1]));

结果是:err
strlen的参数类型是const char *
造成非法访问。

贰(5)


printf(“%d\n”, sizeof(&arr));

结果是:4/8(字节)
&arr是数组的地址,数组的地址也是地址,大小为4/8字节。


printf(“%d\n”, strlen(&arr));

结果是:6
&arr的类型是char * [6]
strlen需要的类型是:char *
虽然类型匹配不上,但是不影响结果的计算。( ‘f‘ 的后面就是 ‘\0’)

贰(6)


printf(“%d\n”, sizeof(&arr+1));

结果是:4/8(字节)
&arr+1是跳过整个数组后的下一个地址,大小为4/8字节。


printf(“%d\n”, strlen(&arr+1));

结果是:随机值
&arr+1是跳过数组的下一个地址。后面的内容不可预测
统计字符串的长度是随机值。

贰(7)


printf(“%d\n”, sizeof(&arr[0]+1));

结果是:4/8(字节)
首元素地址加1,指的就是第二个元素的地址,大小为4/8个字节。


printf(“%d\n”, strlen(&arr[0]+1));

结果是:5
&arr[0]+1指的是字符串中第二个元素,从第二个元素到最后的\0,一共有5个元素。
strlen函数,统计 ‘ \0 ’ 之前的元素个数。

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));
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))

叁(1)


printf(“%d\n”, sizeof( p));

结果是:4/8(字节)
p是指针变量,指向的内存空间大小是1个字节,但是它本身是4/8字节。


printf(“%d\n”, strlen( p));

结果是:6(字节)
标准求字符串长度代码

叁(2)


printf(“%d\n”, sizeof(p+1));

结果是:4/8(字节)
p是 ’ a ’ 的地址,而p+1是 ’ b ’ 的地址。


printf(“%d\n”, strlen(p+1));

结果是:5(字节)
p+1是b的地址

叁(3)


printf(“%d\n”, sizeof( * p));

结果是:1(字节)
*p中,p代表的就是首元素的地址,解引用后其实就是 ’ a '。
’ a ’ 的大小是1。


printf(“%d\n”, strlen( * p));

结果是:err
p是a的地址,*p是a,’ a ’ =97。strlen(97)是非法访问。

叁(4)


printf(“%d\n”, sizeof(p[0]));

结果是:1(字节)
p[ 0 ] <=>*(p+0)<=> *p
p[ 0 ] 指的是’ a ',它的大小是一个字节。


printf(“%d\n”, strlen(p[0]));

结果是:err
p[0] <=> * (p[0]) <=> *p
’ a ’ =97。strlen(97)是非法访问。

叁(5)


printf(“%d\n”, sizeof(&p));

结果是:4/8(字节)
&p是二级指针,指针大小就是4/8字节。


printf(“%d\n”, strlen(&p));

结果是:随机值
&p拿到的是p指针的起始地址
在这里插入图片描述
strlen的判定原理,使得结果出现随机值。
复习巩固:数据的存储
大端——从高位开始

叁(6)


printf(“%d\n”, sizeof(&p+1));

结果是:4/8(字节)
&p+1指的是指针p的地址后面的一个地址。
本句代码的操作,不存在越界访问,没有去使用它,就不算越界访问了。


printf(“%d\n”, strlen(&p+1));

结果是:随机值
strlen的判定原理,使得结果出现随机值。
在这里插入图片描述

叁(7)


printf(“%d\n”, sizeof(&p[0]+1));

结果是:4/8(字节)
p[ 0 ] <=>*(p+0)<=> *p(最终结果是,p[ 0 ] <=> ’ a ’ )
带入就是:
&p[0]+1 <=> &( ’ a ’ )+1,取出字符a的地址,然后加一。就是字符b的地址了。大小为4/8个字节。


printf(“%d\n”, strlen(&p[0]+1))

结果是:5(字节)
&p[0]是a的地址,&p[0]+1是b的地址。


关于随机值
不同电脑的值不同,但同一台电脑,可能每次值都是一样的。


指针和二维数组

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]));

首先了解二维数组:

在这里插入图片描述

1

int a[3][4] = {0};
printf(“%d\n”,sizeof(a));

结果是:48(三行 * 四列 * 4 = 48 字节 )
二维数组,先行后列。
这里,sizeof的对象是整个数组。
12个元素,每个元素类型为int。所以大小为48字节。

2

int a[3][4] = {0};
printf(“%d\n”,sizeof(a[0][0]));

结果是:4(字节)
这里,sizeof的对象是第一行第一列的元素。

3

int a[3][4] = {0};
printf(“%d\n”,sizeof(a[0]));

结果是:16(字节)
a[0]是第一行的数组名。数组名放在sizeof中,计算的是整个数组的大小。

4

int a[3][4] = {0};
printf(“%d\n”,sizeof(a[0]+1));

结果是:4(字节)
a[0]作为第一行的数组名,但没有单独放在sizeof中,没有取地址,表示的是数组首元素地址,就是a[0][0]的地址。
所以a[0]+1就是第一行第二个元素的地址。

5

int a[3][4] = {0};
printf(“%d\n”,sizeof(*(a[0]+1)));

结果是:4(字节)
a[0]+1是第一行第二个元素的地址,那么加一个*之后是解引用第一行第二个元素地址。
sizeof(a[0]+1)计算的是第一行第二个元素大小。

6

int a[3][4] = {0};
printf(“%d\n”,sizeof(a+1));

结果是:4/8(字节)
a是数组首元素地址,在二维数组中,代表的是数组第一行地址。加一之后就是第二行的地址。
第二行的地址还是地址。
a+1的类型为int (*)[ 4 ],其实是一个数组指针类型。
a的类型和a+1的类型其实是一样的,都是数组指针

7

int a[3][4] = {0};
printf(“%d\n”,sizeof(*(a+1)));

结果是:16(字节)
a+1是第二行的地址,第二行可以看作一个一维数组。
*(a+1)就代表着是整个第二行。
整个第二行,一共四个元素,大小为16个字节。

8

int a[3][4] = {0};
printf(“%d\n”,sizeof(&a[0]+1));

结果是:4/8(字节 )
首先,&a[0]代表的是第一行的地址,加一之后就是第二行地址了。
地址大小就是4/8字节。

9

int a[3][4] = {0};
printf(“%d\n”,sizeof(*(&a[0]+1)));

结果是:16(字节 )
&a[0]+1代表的是第二行地址,解引用后,就是整个第二行了。
整个第二行的大小是16字节。

10

int a[3][4] = {0};
*printf(“%d\n”,sizeof(a));

结果是:16( 字节 )
a没有单独放在sizeof中,也就是说,这里的a是首元素地址。
解引用后,就是第一行。
第一行的大小就是16了。

11

int a[3][4] = {0};
printf(“%d\n”,sizeof(a[3]));

结果是:16( 字节 )

如果第四行存在:
a[3]是第四行的数组名,单独放在sizeof中,计算的是整个数组的大小。

即使此时第四行不存在,依旧不影响结果是16
在这里,sizeof不存在越界访问问题,因为sizeof的访问是虚拟的。

数据大小的本质是数据类型
int类型大小为 4 字节
char类型为 1 字节
short类型为2字节
long类型为 8 字节
int [ 4 ] 类型为 16 字节

所以sizeof在使用时,对象是否存在并不重要,重要的是对象的数据类型。知道其数据类型,那么对应大小也就知道了。

补充:放入sizeof中的运算式不参与运算。
一般代码步骤如下:
代码运行步骤。

C语言中,表达式有2个属性:
值属性
类型属性

总结:
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值