复习指针(17)

<1>sizeof与strlen的对比

1.1 sizeof

  • 在学习操作符的时候,我们学习了sizeof , sizeof计算变量所占内存空间大小,单位是字节,如果操作数是类型的话,计算的是使用 类型创建的变量所占内存空间的大小
  • sizeof只关注占用内存空间的大小,不在乎内存中存放什么数据

a.语法

sizeof(变量);
sizeof(类型);
sizeof 变量; // 不加括号也可以,只能变量

b.基础类型sizeof大小(32 / 64)

  • char:1字节
  • short:2字节
  • int:4字节
  • long:32位4 / 64位8
  • long long:8字节
  • float:4字节
  • double:8字节

c.进阶类型sizeof大小

1.普通数组

int arr[10];

sizeof(arr);

  • arr 代表整个数组
  • 结果:元素个数 × 单个类型大小
  • 上例:10×4=40

2.字符数组

char str[] = "abc";

sizeof(str);

  • 字符串末尾自带 \0 
  • 一共:a b c \0 → 4字节

👉 sizeof 包含结束标志 \0

3.数组传参

void test(char arr[])

{

sizeof(arr); // 结果恒为4/8

}

  • 数组传参退化为指针(本质传参的类型就是指针)
  • sizeof算的是指针大小(固定由本身系统决定),不再是数组大小

4.指针sizeof(重)

  • 32位系统:任意类型指针都是 4字节
  • 64位系统:任意类型指针都是 8字节

规律:

  • char* 、 int* 、 double* 、数组指针、函数指针 sizeof全部一样大 因为指针只存地址,地址长度统一

5.结构体sizeof(内存对齐)

  • 成员偏移必须是自身大小整数倍

举例:Short占2个字节,就只能放在偶数的位置0 2 4 6

Int占4个字节,所以只能放在4的倍数0 4 8 12

  •  整体大小必须是最大基本成员整数倍

这个挺好理解,就是类型占用的内存空间大小的倍数

  • 会有空洞填充,浪费空间
struct S
{
    char c;  //1字节
    int i;   //4字节
};
 

内存格子:0 1 2 3 4 5 6 7

  • 0号格子:放 char(满满1字节都在用)
  • 1、2、3:空洞!啥都不放!空白!
  • 4、5、6、7:放 完整int(4格全部存int数字,一点不空)

👉看懂区别:

  • int自己内部:没有空洞,满满4字节数据
  • char 和 int 中间:被迫空了3格

为什么非要空着?为什么int不能从1号格子开始放?

原因:CPU读取速度(硬件底层!必考原理)

CPU读内存不是1格1格读CPU一次天生就是读:4字节、8字节整块读取

  • int是4字节数据
  • CPU设计:只喜欢从 0、4、8、12…这种4倍数地址一口气抓4个格子
  • 如果int强行从1号地址开始: 需要读两次内存、还要拼接数据、速度巨慢、效率极低、部分老CPU直接报错崩溃

6.sizof的核心特性

  •  只看空间大小,不看存什么数据
  • 不访问内存,不运算内容
  • 不计算表达式运行结果

7.实例

a.

这里涉及了C语言算术类型转化规则:不同类型运算时,低精度类型会向高精度类型转化

因此int会提升为double , 所以运算结果的类型是double

b.

有无 \0 的区别

2.2 strlen

  • 库函数,功能是求字符串长度

  • 统计的是strlen函数的参数str,从这个地址开始向后,\0之前字符串的个数,函数会一直向后找\0字符,直到找到为止,所以可能会出现存在越界查找
#include <stdio.h>
int main()
{
char arr1[3] = {'a', 'b', 'c'};
char arr2[] = "abc";
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr2));
return 0;
}

随机值,\0不知道在哪 3 3 4

1.3 sizeof 与 strlen的对比

<2> 数组和指针笔试题解析

数组名的意义:

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

2.1一维数组

int main()
{
	int a[] = { 1,2,3,4 };//4 * 4 = 16

	printf("%zu\n", sizeof(a));//16, a是数组名,数组名单独放在sizeof中,数组名表示整个数组,计算的是整个数组的大小,单位是字节
	printf("%zu\n", sizeof(a + 0));//4/8,a是数组名,并没有单独放在sizeof内部,也没有&,所以a就是首元素的地址
	//a+0,还是首元素的地址,sizeof(a + 0)计算的是一个地址的大小,4/8
	printf("%zu\n", sizeof(*a));//a是数组名,并没有单独放在sizeof内部,也没有&,所以a就是首元素的地址
	//*a就是首元素==a[0],就是整数1,sizeof(*a)就是4个字节
	printf("%zu\n", sizeof(a + 1));//4/8, a就是首元素的地址,a+1就是第二个元素的地址,sizeof(a + 1)计算的是地址的大小
	printf("%zu\n", sizeof(a[1]));//4,a[1]就是数组的第二个元素,大小是4个字节
	printf("%zu\n", sizeof(&a));//&a-取出的是数组的地址,数组的地址也是地址呀,是地址大小就是4/8个字节
	//&a 的特殊性体现在+-整数
	//int (*p)[4] = &a;
	printf("%zu\n", sizeof(*&a));//16,&a-取出的是数组的地址,他的类型是int(*)[4],对于数组指针解引用,访问的是这个数组,大小就是16个字节
	//2. *&a == a, sizeof(*&a) == sizeof(a)
	printf("%zu\n", sizeof(&a + 1));//4/8,&a是数组的地址,&a+1是跳过数组后的地址,是地址就是4/8个字节
	printf("%zu\n", sizeof(&a[0]));//4/8,a[0]是第一个元素,&a[0]就是第一个元素的地址,大小就是4/8
	printf("%zu\n", sizeof(&a[0] + 1)); //4/8, &a[0]就是第一个元素的地址,&a[0] + 1就是第二个元素的地址,大小就是4/8

	return 0;
}

2.2 字符数组

#include <stdio.h>
int main()
{
    char arr[] = { 'a','b','c','d','e','f' };

    printf("%zu\n", sizeof(arr));//6, arr表示整个数组,sizeof(arr)计算的是整个数组的大小
    printf("%zu\n", sizeof(arr + 0));//4/8, arr就是首元素的地址,只要是地址大小就是4/8个字节
    printf("%zu\n", sizeof(*arr));//1,arr就是首元素的地址,*arr就是首元素,sizeof(*arr)计算的是首元素的大小
    printf("%zu\n", sizeof(arr[1]));//1, arr[1]是第二个元素,大小就是1个字节
    printf("%zu\n", sizeof(&arr));//4/8,&arr取出的是数组的地址,数组的地址也是地址,大小就是4/8个字节
    printf("%zu\n", sizeof(&arr + 1));//4/8, &arr取出的是数组的地址,&arr+1是跳过数组后的地址,跳过去后还是地址,大小就是4/8个字节
    printf("%zu\n", sizeof(&arr[0] + 1));//4/8,&arr[0] + 1是数组第二个元素的地址,大小就是4/8个字节
    return 0;
}

#include <stdio.h>
#include <string.h>

int main()
{
    char arr[] = { 'a','b','c','d','e','f' };
    printf("%zu\n", strlen(arr));//随机值,arr是数组首元素的地址,字符串中没有\0
    printf("%zu\n", strlen(arr + 0));//随机值,arr是数组首元素的地址,arr+0还是首元素的地址,字符串中没有\0
    //printf("%zu\n", strlen(*arr));//arr是数组首元素的地址,*arr是首元素-'a'-97
    //非法访问内存 - 程序就会崩溃
    //printf("%zu\n", strlen(arr[1]));//err,非法访问内存,崩溃,arr[1] == 'b' == 98
    printf("%zu\n", strlen(&arr));//随机值,&arr是数组的地址,从数组的地址也就是数组的起始位置开始向后数字符串的长度,要找\0,但是没有\0
    //所以是随机值
    printf("%zu\n", strlen(&arr + 1));//随机值,&arr+1是跳过整个数组后的地址,向后找\0,也是随机值
    printf("%zu\n", strlen(&arr[0] + 1));//随机值,&arr[0] + 1是第二个元素的地址
    return 0;
}

#include <stdio.h>
int main()
{
    char arr[] = "abcdef";
    printf("%zu\n", sizeof(arr));//7
    printf("%zu\n", sizeof(arr + 0));//4/8
    printf("%zu\n", sizeof(*arr));//1
    printf("%zu\n", sizeof(arr[1]));//1
    printf("%zu\n", sizeof(&arr));//4/8
    printf("%zu\n", sizeof(&arr + 1));//4/8
    printf("%zu\n", sizeof(&arr[0] + 1));//4/8
    return 0;
}

#include <stdio.h>
#include <string.h>
int main()
{
    char arr[] = "abcdef";
    printf("%zu\n", strlen(arr));//6
    printf("%zu\n", strlen(arr + 0));//6
    //printf("%zu\n", strlen(*arr));//err
    //printf("%zu\n", strlen(arr[1]));//err
    printf("%zu\n", strlen(&arr));//6
    printf("%zu\n", strlen(&arr + 1));//随机值
    printf("%zu\n", strlen(&arr[0] + 1));//5
    return 0;
}

#include <stdio.h>
int main()
{
    char* p = "abcdef";
    printf("%zu\n", sizeof(p));//4/8, p是指针变量
    printf("%zu\n", sizeof(p + 1));//4/8, p+1就是b的地址
    printf("%zu\n", sizeof(*p));//1
    printf("%zu\n", sizeof(p[0]));//1, p[0]->*(p+0) -> *p
    printf("%zu\n", sizeof(&p));//4/8, &p是指针变量p的地址,是一个二级指针
    printf("%zu\n", sizeof(&p + 1));//4/8
    printf("%zu\n", sizeof(&p[0] + 1));//4/8, &p[0] + 1是b的地址
    return 0;
}

#include <stdio.h>
#include <string.h>
int main()
{
    char* p = "abcdef";
    printf("%zu\n", strlen(p));//6
    printf("%zu\n", strlen(p + 1));//5
    //printf("%zu\n", strlen(*p));//err
    //printf("%zu\n", strlen(p[0]));//err
    printf("%zu\n", strlen(&p));//随机值,p的内容不确认,p变量在内存中后续的空间内容不确定
    printf("%zu\n", strlen(&p + 1));//随机值
    printf("%zu\n", strlen(&p[0] + 1));//5

    return 0;
}

2.3 二维数组


int main()
{
    int a[3][4] = { 0 };
    printf("%zu\n", sizeof(a));//48
    printf("%zu\n", sizeof(a[0][0]));//4
    printf("%zu\n", sizeof(a[0]));//16,a[0]是第一行这个一维数组的数组名,单独放在sizeof内部了,a[0]表示第一行这个数组
    //sizeof(a[0])计算的是第一行的大小
    printf("%zu\n", sizeof(a[0] + 1));//4/8, a[0]就是第一行第一个元素的地址==&a[0][0], a[0]+1是第一行第二个元素的地址
    printf("%zu\n", sizeof(*(a[0] + 1)));//4, *(a[0] + 1)是第一行第二个元素
    printf("%zu\n", sizeof(a + 1));//4/8, a是二维数组的数组名,这里只能表示数组首元素的地址,也就是第一行的地址
    //a+1 就是第二行的地址
    //a --> int(*)[4]
    printf("%zu\n", sizeof(*(a + 1)));//16,*(a + 1) == a[1]
    //a[1]就是第2行的数组名,计算的是整个数组的大小
    printf("%zu\n", sizeof(&a[0] + 1));//4/8
    //a[0]是第一行的数组名
    //&a[0]取出的是第1行的地址
    //&a[0] + 1是第2行的地址
    printf("%zu\n", sizeof(*(&a[0] + 1)));//16
    printf("%zu\n", sizeof(*a));//16,a是二维数组的数组名,这里只能表示数组首元素的地址,也就是第一行的地址
    //*a 就是第一行
    printf("%zu\n", sizeof(a[3]));//16,不会有越界访问的
    //sizeof在计算变量,数组的大小的时候,是通过类型来推导的,不会真实去访问内存空间

    return 0;
}

<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;
}
//程序的结果是什么?

3.2 题目2:

//在X86环境下 1
//假设结构体的⼤⼩是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;
}

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

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

图解

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

3.6 题目6:

#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}

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

图解

#知识复习#
全部评论
这个之前打acm的时候用到过
点赞 回复 分享
发布于 昨天 22:46 辽宁

相关推荐

不愿透露姓名的神秘牛友
05-01 01:56
已编辑
喵_coding:我只能说实习生干的就是这种活,能做内部aiagent就不赖了,我也在阿里另一个bu呆着,一样做这种业务告警和客服agent,业务需求其实不急着做。 但该说不说,还技术债确实有点坑,重构类的活都是脏活
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务