深入理解指针(5)

embedded/2024/11/14 0:10:08/

目录

1. sizeof和strlen 的对比
2. 数组和指针笔试题解析
3 . 指针运算笔试题解析
———————————————————————————————————————————

1.sizeof和strlen的对比

1.1sizeof

sizeof计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

sizeof只关注占用内存空间的大小,不在乎内存中存放什么数据。

1.2strlen

strlen是C语言库函数,功能是求字符串长度。

函数原型:

1.size_t strlen ( const char * str )

统计的是从strlen函数的参数str中这个地址开始向后,\0之前的字符串中字符的个数。

strlen函数会一直向后找\0字符,直到找到为止,所以可能存在越界查找。

1.3sizeof和strlen的对比

sizeof:

  1. sizeof是操作符
  2. sizeof计算操作数所占内存的大小,单位是字节
  3. 不关注内存中存放什么数据

strlen:

  1. strlen是库函数,使用需要包含头文件string.h
  2. strlen是求字符串长度的,统计的是\0之前的字符的个数
  3. 关注内存中是否有\0,如果没有\0,就会持续往后找,可能会越界

2.数组和指针笔试题解析

2.1一维数组

#include <stdio.h>
int main()
{int a[] = { 1,2,3,4 };printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(a + 0));printf("%zd\n", sizeof(*a));printf("%zd\n", sizeof(a + 1));printf("%zd\n", sizeof(a[1]));printf("%zd\n", sizeof(&a));printf("%zd\n", sizeof(*&a));printf("%zd\n", sizeof(&a + 1));printf("%zd\n", sizeof(&a[0]));printf("%zd\n", sizeof(&a[0] + 1));return 0;
}

 

数组名是数组首元素的地址,但是有两个例外:

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

int a[] = { 1,2,3,4 };
1.printf("%zd\n", sizeof(a));//16--数组名a单独放到sizeof内部,a表示整个数组,计算的是整个数组的大小,单位是字节
2.printf("%zd\n", sizeof(a + 0));//4/8--这里的a表示首元素的地址,a+0还是首元素的地址
3.printf("%zd\n", sizeof(*a));//4--这里的a表示首元素的地址,*a就是首元素--a[0]
4.printf("%zd\n", sizeof(a + 1));//4/8--这里的a表示首元素的地址,a+1是第二个元素的地址
5.printf("%zd\n", sizeof(a[1]));//4--第二个元素,第二个元素大小是4个字节
6.printf("%zd\n", sizeof(&a));//4/8--这里的数组名表示整个数组,&a是整个数组的地址,数组的地址也是地址
7.printf("%zd\n", sizeof(*&a));//16--*和&相互抵消 => sizeof(a),a表示整个数组,计算的是整个数组的大小,单位是字节
8.printf("%zd\n", sizeof(&a + 1));//4/8--&a是数组的地址。&a+1是跳过整个数组后那个位置的地址,但它也是地址
9.printf("%zd\n", sizeof(&a[0]));//4/8--a[0]是首元素,对首元素取地址也是地址
10.printf("%zd\n", sizeof(&a[0] + 1));//4/8--第二个元素的地址

2.2字符数组

代码1:

#include <stdio.h>
int main()
{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));return 0;
}

char arr[] = { 'a','b','c','d','e','f' };
1.printf("%d\n", sizeof(arr));//6--这里的数组名表示整个数组,单位字节,没有\0
2.printf("%d\n", sizeof(arr + 0));//4/8--这里的arr表示首元素的地址,arr+0还是首元素的地址
3.printf("%d\n", sizeof(*arr));//1--这里的arr表示首元素的地址,*arr就是首元素,首元素的大小是1个字节
4.printf("%d\n", sizeof(arr[1]));//1--arr[1]是数组的第二个元素,第二个元素的大小是1个字节
5.printf("%d\n", sizeof(&arr));//4/8--这里的arr表示整个数组,取出整个数组的地址
6.printf("%d\n", sizeof(&arr + 1));//4/8--这里的arr表示整个数组,取出数组的地址+1,跳过整个数组,但也是地址
7.printf("%d\n", sizeof(&arr[0] + 1));//4/8--arr[0]是首元素,&arr[0]是首元素地址,+1表示第二个元素的地址

代码2:

#include <stdio.h>
#include <string.h>
int main()
{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));return 0;
}

strlen是计算字符串中‘\0’之前的字符的个数的 

char arr[] = { 'a','b','c','d','e','f' };
1.printf("%zd\n", strlen(arr));//随机值--这里的arr是首元素的地址,arr中没有\0,所以strlen会一直往后找,直到找到\0
2.printf("%zd\n", strlen(arr + 0));//随机值--这里的arr是首元素的地址,arr+0还是首元素的地址,没有\0,所以会一直往后找
3.printf("%zd\n", strlen(*arr));//程序崩溃--这里的arr是首元素的地址,*arr是首元素,‘a’的ACCLL码值是97,strlen是地址,我们把97传进去,strlen就会把97当成地址进行访问,程序直接崩溃
4.printf("%zd\n", strlen(arr[1]));//程序崩溃--arr[1]是第二个元素,‘b’的ACCLL码值是98,把98传给strlen,strlen会把98当做地址进行访问,程序崩溃
5.printf("%zd\n", strlen(&arr));//随机值--这里的arr是整个数组,取整个数组的地址,strlen还是会往后找\0
6.printf("%zd\n", strlen(&arr + 1));//随机值--这里的arr表示整个数组,arr+1跳过整个数组,后面不知道有没有\0,随机值,和前面从首元素开始找随机值差6,所以跳过了整个数组
7.printf("%zd\n", strlen(&arr[0] + 1));//随机值--取出首元素地址+1,第二个元素地址开始往后找\0,比从首元素地址开始的少1

代码3: 

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

char arr[] = "abcdef";
1.printf("%zd\n", sizeof(arr));//7--arr整个数组的大小
2.printf("%zd\n", sizeof(arr + 0));//4/8--arr数组首元素的地址,arr+0还是首元素的地址
3.printf("%zd\n", sizeof(*arr));//1--arr是数组首元素的地址,*arr首元素
4.printf("%zd\n", sizeof(arr[1]));//1--数组第二个元素
5.printf("%zd\n", sizeof(&arr));//4/8--arr表示整个数组,&arr整个数组的地址
6.printf("%zd\n", sizeof(&arr + 1));//4/8--arr表示整个数组,&arr整个数组的地址,&arr+1跳过整个数组的地址,还是地址
7.printf("%zd\n", sizeof(&arr[0] + 1)); //4/8--&arr[0]是首元素的地址,&arr[0]+1第二个元素的地址

代码4:

#include <stdio.h>
#include <string.h>
int main()
{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));return 0;
}

char arr[] = "abcdef";
1.printf("%d\n", strlen(arr));//6--arr首元素的地址,有\0
2.printf("%d\n", strlen(arr + 0));//6--arr表示首元素的地址,arr+0还是首元素
3.printf("%d\n", strlen(*arr));//程序崩溃--arr表示首元素的地址,*arr首元素,‘a’的ACCLL码值是97,将97传给strlen,strlen会将97当成地址访问,程序崩溃
4.printf("%d\n", strlen(arr[1]));//程序崩溃--arr[1]表示第二个元素,‘b’的ACCLL码值是98,将98传给strlen,strlen会将98当成地址访问,程序崩溃
5.printf("%d\n", strlen(&arr));//6--arr整个数组,&arr整个数组的地址传给strlen
6.printf("%d\n", strlen(&arr + 1));//随机值--&arr+1跳过整个数组,会跳过\0
7.printf("%d\n", strlen(&arr[0] + 1)); //5--取第一个元素+1

代码5:

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

 

char* p = "abcdef";
1.printf("%zd\n", sizeof(p));//4/8--p是指针变量,计算的是指针变量的大小,指针变量就是存放地址的
2.printf("%zd\n", sizeof(p + 1));//4/8--p+1是第二个元素的地址
3.printf("%zd\n", sizeof(*p));//1--p的类型是char*,*p只能访问一个字节
4.printf("%zd\n", sizeof(p[0]));//1--p[0]->*(p+0)->*p,和上面一样
5.printf("%zd\n", sizeof(&p));//4/8--&p是指针变量的地址,&p->char**->二级指针
6.printf("%zd\n", sizeof(&p + 1));//4/8--&p是p的地址,&p+1是跳过p变量,但还是地址
7.printf("%zd\n", sizeof(&p[0] + 1));//4/8--p[0]是a,取出a的地址+1,指向b的地址

代码6:

#include <stdio.h>
#include <string.h>
int main()
{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));return 0;
}

 

char* p = "abcdef";
1.printf("%d\n", strlen(p));//6--p里面存放的是a的地址,将a的地址传给strlen
2.printf("%d\n", strlen(p + 1));//5--p+1里面存放的是b的地址
3.printf("%d\n", strlen(*p));//程序崩溃--p里面存放的是a的地址,*p是‘a’,‘a’的ACCLL码值是97,将97传给strlen,strlen会将97当成地址访问,程序崩溃
4.printf("%d\n", strlen(p[0]));//程序崩溃--p[0]->*(p+0)->*p
5.printf("%d\n", strlen(&p));//随机值--&p取出变量p的地址,类型是char**
6.printf("%d\n", strlen(&p + 1));//随机值--&p取出变量p的地址,&p+1跳过一个指针变量
7.printf("%d\n", strlen(&p[0] + 1));//5--&p[0]->p,p+1指向b的地址

2.3二维数组

#include <stdio.h>
int main()
{int a[3][4] = { 0 };printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(a[0][0]));printf("%zd\n", sizeof(a[0]));printf("%zd\n", sizeof(a[0] + 1));printf("%zd\n", sizeof(*(a[0] + 1)));printf("%zd\n", sizeof(a + 1));printf("%zd\n", sizeof(*(a + 1)));printf("%zd\n", sizeof(&a[0] + 1));printf("%zd\n", sizeof(*(&a[0] + 1)));printf("%zd\n", sizeof(*a));printf("%zd\n", sizeof(a[3]));return 0;
}

int a[3][4] = { 0 };
1.printf("%zd\n", sizeof(a));//48--a表示整个数组,计算的是整个数组的大小,单位是字节
2.printf("%zd\n", sizeof(a[0][0]));//4--a[0][0]是第一行第一个元素
3.printf("%zd\n", sizeof(a[0]));//16--a[0]表示一整个一维数组的大小
4.printf("%zd\n", sizeof(a[0] + 1));//4/8--a[0]是第一行的数组名,但是没有单独放在sizeof内部,那么只能是数组首元素的地址,那就是第一行第一个元素的地址,+1则是第一行第二个数的地址
5.printf("%zd\n", sizeof(*(a[0] + 1)));//4--是第一行第二个元素
6.printf("%zd\n", sizeof(a + 1));//4/8--a是二维数组的数组名,a表示首元素的地址,也就是第一行的地址,a+1则是第二行的地址
7.printf("%zd\n", sizeof(*(a + 1)));//16--第二行整个数组的大小
8.printf("%zd\n", sizeof(&a[0] + 1));//4/8--a[0]是第一行的数组名,&a[0]是第一行的地址,&a[0]+1是地址
9.printf("%zd\n", sizeof(*(&a[0] + 1)));//16--&a[0]+1第二行的地址,*(&a[0]+1)是第二行整个数组的大小
10.printf("%zd\n", sizeof(*a));//16--a是第一行的地址,*a第一行的值
11.printf("%zd\n", sizeof(a[3])); //没有越界,a[3]是第四行的数组名,int[4]sizeof不会去真实计算,根据类型推算

3.指针运算笔试题解析

题目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:

#include <stdio.h>
//在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;
}

 

1.printf("%p\n", p + 0x1);//p是结构体指针,+1就是跳过结构体,跳过20个字节,转十六进制0x14
2.printf("%p\n", (unsigned long)p + 0x1);//整数+1,0x100001
3.printf("%p\n", (unsigned int*)p + 0x1); //=1是跳过一个unsigned int*类型变量,是4个字节

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

 

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

 

 

小地址-大地址=&p[4][2]-&a[4][2]=-4 

 1000000 0000000 0000000 0000100 --原码

 11111111 11111111 11111111 11111011 --反码

 11111111 11111111 11111111 11111100 --补码

       FF           FF         FF             FC

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

 

题目6:

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

 

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

 

 


http://www.ppmy.cn/embedded/115117.html

相关文章

新产品,推出 MLX90372GVS 第三代 Triaxis® 位置传感器 IC,适用于汽车和工业系统(MLX90372GVS-ACE-308)

Triaxis 旋转和线性位置传感器IC&#xff1a; MLX90372GVS-ACE-103 MLX90372GVS-ACE-108 MLX90372GVS-ACE-301 MLX90372GVS-ACE-200 MLX90372GVS-ACE-208 MLX90372GVS-ACE-303 MLX90372GVS-ACE-300 MLX90372GVS-ACE-350 MLX90372GVS-ACE-100 MLX90372GVS-ACE-101 MLX90372GVS-…

【STL】 set 与 multiset:基础、操作与应用

在 C 标准库中&#xff0c;set 和 multiset 是两个非常常见的关联容器&#xff0c;主要用于存储和管理具有一定规则的数据集合。本文将详细讲解如何使用这两个容器&#xff0c;并结合实例代码&#xff0c;分析其操作和特性。 0.基础操作概览 0.1.构造&#xff1a; set<T&…

110个oracle常用函数总结

110个oracle常用函数总结 1. ascii 返回与指定的字符对应的十进制数; sql> select ascii(a) a,ascii(a) a,ascii(0) zero,ascii( ) space from dual; a a zero space --------- --------- --------- --------- 65 97 48 32 2. chr 给出整数,返回对应的字符; sql>…

QT开发: Qt 框架中字符串核心类QString详解

QString 是 Qt 框架中用于处理和操作文本字符串的核心类。QString 提供了一系列强大且高效的方法来创建、操作和管理 Unicode 字符串。以下是对 QString 类的深入详解&#xff0c;包括其主要功能、常用方法和示例代码等。 1. QString 的基本概念 QString 是一个用于表示和操作…

tomcat的Catalinalog和localhostlog乱码

找到tomcat安装目录的loging文件 乱码这两个由UTF-8改为GBK

学习IEC 62055付费系统标准

1.IEC 62055 国际标准 IEC 62055 是目前关于付费系统的唯一国际标准&#xff0c;涵盖了付费系统、CIS 用户信息系统、售电系统、传输介质、数据传输标准、预付费电能表以及接口标准等内容。 IEC 62055-21 标准化架构IEC 62055-31 1 级和 2 级有功预付费电能表IEC 62055-41 STS…

【图灵完备 Turing Complete】游戏经验攻略分享 Part.5 编程

编程部分的话&#xff0c;第一关会让你输入机器码&#xff0c;这一章节还是比较简单的&#xff0c;因为操作码是固定给出的&#xff0c;只需要根据题意去编写&#xff0c;完成这章目的是为了解锁下面的关卡。 输入&#xff0c;移动COPY之后进行运算&#xff0c;然后输出。 激光…

Oracle脚本:排查占用UNDO段的SQL

Oracle脚本:排查占用UNDO段的SQL 📖Oracle SQL脚本:排查占用UNDO段未释放的SQL。 prompt ===============