关于指针,你不可以错过的练习(c/c++)

简介: 关于指针,你不可以错过的练习(c/c++)

前言:

除了sizeof()单独放数组名和&数组名,其他的数组名都仅仅代表首元素地址,

接下来我们就靠一些练习来巩固一下对指针的学习吧.

1.练习1 :一维数组

int main()
{
  //一维数组
  int a[] = { 1,2,3,4 };//4个元素,每个元素是int类型
  printf("%d\n", sizeof(a));    
    //sizeof里面直接放数组名,计算的是整个数组的大小,所以是16
  printf("%d\n", sizeof(a + 0));//这里数组名表示的是首元素地址,地址都是4或者8个字节
  printf("%d\n", sizeof(*a));  //4个字节,相当于a[0]
  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 对数组指针解引用访问一个数组的大小,单位是字节,等价于                   
    sizeof(a)
  printf("%d\n", sizeof(&a + 1));//由于&a是一个指针数组类型,+1跳过一个数组,指向最后数组的后一个元素的地址,所以是4/8
  printf("%d\n", sizeof(&a[0]));//取出的是第一个元素的地址,地址的大小是4/8
  printf("%d\n", sizeof(&a[0] + 1));//取出的是第二个元素的地址,地址的大小是4/8
    return 0;
}

2.练习2:字符数组

//字符数组
  char arr[] = { 'a','b','c','d','e','f' };
  printf("%d\n", sizeof(arr));  //数组的大小,6个字节
  printf("%d\n", sizeof(arr + 0));//首元素地址+1,是地址就是4/8个字节
  printf("%d\n", sizeof(*arr));//首元素的大小,类型是char,所以是1个字节
  printf("%d\n", sizeof(arr[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字节
  //strlen是求字符串长度的,统计的是\0之前所有的元素个数
  printf("%d\n", strlen(arr));    //arr是首元素地址,因为字符数组中没有手动添加\0,所以我们不知道后面到什么地方才会有\0出现,所以大小是随机值 大于等于6
  printf("%d\n", strlen(arr + 0)); //随机值,同上
  printf("%d\n", strlen(*arr));  //  *arr是首元素,等价于字符a->97,strlen就把97当做一个地址
  //97作为地址直接进行访问就是非法访问
  printf("%d\n", strlen(arr[1])); //字符b,同样是非法访问
  printf("%d\n", strlen(&arr));   //把数组指针传给了char*,类型发生变化但是值没变,随机值
  printf("%d\n", strlen(&arr + 1)); //随机值
  printf("%d\n", strlen(&arr[0] + 1));//随机值

3.练习3:字符数组2

char arr[] = "abcdef";
  printf("%d\n", sizeof(arr)); //加上\0,一共7个字节
  printf("%d\n", sizeof(arr + 0));//地址的大小就是4/8字节
  printf("%d\n", sizeof(*arr));//a的大小是1个字节
  printf("%d\n", sizeof(arr[1]));//b大小也是一个字节
  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)); //6字节
  printf("%d\n", strlen(arr + 0));//同上
  printf("%d\n", strlen(*arr)); //传进去的是97会报错
  printf("%d\n", strlen(arr[1]));//同上
  printf("%d\n", strlen(&arr));//6字节
  printf("%d\n", strlen(&arr + 1));//跳过了整个数组,所以是随机值
  printf("%d\n", strlen(&arr[0] + 1));//从第二个元素开始,所以是5字节

4.练习4:字符数组3

char* p = "abcdef"; 
  //把字符串的首地址存在p里面
  printf("%d\n", sizeof(p));  //首元素地址,所以是4/8字节
  printf("%d\n", sizeof(p + 1)); //4/8   本质上就是地址+1,仍然是地址
  printf("%d\n", sizeof(*p));   //1 *p == 'a'
  printf("%d\n", sizeof(p[0])); //同上 
  printf("%d\n", sizeof(&p));   //4/8   p变量的起始地址 类型是char**二级指针类型
  printf("%d\n", sizeof(&p + 1));  //跳过一个char*,还是地址,4/8
  printf("%d\n", sizeof(&p[0] + 1));  //地址,4/8
  printf("%d\n", strlen(p));     //6字节
  printf("%d\n", strlen(p + 1));  //跳过首元素,所以是五个字节
  printf("%d\n", strlen(*p));    //err
  printf("%d\n", strlen(p[0]));  //err
  printf("%d\n", strlen(&p));    //char**,这个地址有没有\0不可知,所以是随机值
  printf("%d\n", strlen(&p + 1)); //随机值
  printf("%d\n", strlen(&p[0]+1)); //5

5.练习5:二维数组

int a[3][4] = { 0 };
  printf("%zd\n", sizeof(a));      //48 数组名单独放在sizeof内部,表示整个数组,所以计算的是整 
    个数组的大小,单位是字节   48
  printf("%zd\n", sizeof(a[0][0]));//4
  printf("%zd\n", sizeof(a[0]));   //16 二维数组是数组的数组,a[0]单独放在sizeof内部,就计算的 
    是整个第一行的大小
  printf("%zd\n", sizeof(a[0] + 1)); //4/8  a[0]并非单独放在sizeof内部,所以a[0]表示数组首元 
    素的地址, 也就是第一行第一个元素的地址,a[0] <-->&a[0][0],所以 a[0]+1表示&a[0][1]
  printf("%zd\n", sizeof(*(a[0] + 1)));//4  对第一行第二个元素进行解引用
  printf("%zd\n", sizeof(a + 1));     //4/8 第二行地址
  printf("%zd\n", sizeof(*(a + 1)));  //16 第二行的大小
  printf("%zd\n", sizeof(&a[0] + 1)); //4/8 第二行的地址
  printf("%zd\n", sizeof(*(&a[0] + 1)));//16 第二行的大小
  printf("%zd\n", sizeof(*a));  //16 第一行大小
  printf("%zd\n", sizeof(a[3]));  //16 不会越界,因为编译器会根据变量的类型直接进行判断,并不会 
    真的去访问
  //表达式有两个属性,一个是值属性,一个是类型属性

6.小结

   总结:

       数组名的意义:

       1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。

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

       其他数组名都表示首元素的地址.

7.几个关于指针的小题目

7.1 难度: *

int main()
{
  int a[5] = { 1, 2, 3, 4, 5 };
  int* ptr = (int*)(&a + 1);
  printf("%d,%d", *(a + 1), *(ptr - 1));
  return 0;
}
//a这里表示首元素的地址,+就是第二个元素的地址,解引用得到2
//ptr指向数组的最后一个元素的后面的一个int的地址,-1则指向最后一个元素,解引用答案是5
//程序的结果是什么?
//2  5

7.2 难度: **

//注:默认是x86的环境
struct Test
{
  int Num;
  char* pcName;
  short sDate;
  char cha[2];
  short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
  //考点:指针+1加几个字节? 取决于指针的类型
    //注:十六进制
  printf("%p\n", p + 0x1);                  //0x00100014
  printf("%p\n", (unsigned long)p + 0x1);   //0x00100001
  printf("%p\n", (unsigned int*)p + 0x1);   //0x00100004
  return 0;
}

7.3 难度:  *** 破题点:画图

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);
    //默认小端字节序
  //内存 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
          //ptr2指向01后面的00  Ox 02 00 00 00   还原出来就是2000000
  //答案 4,2000000
  return 0;
}

7.4 难度: * 考点:逗号表达式

int main()
{
  int a[3][2] = { (0, 1), (2, 3), (4, 5) };
  int* p;
  p = a[0];
  printf("%d", p[0]); //1
  return 0;
}

7.5 难度: ***

int main()
{
  int a[5][5];
  int(*p)[4];
  p = a;
    //类型不合适
  //a - int(*)[5]
  //p - int(*)[4]
  //画图
  printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    //%p打印就是把-4转成补码形式再转16进制
    //指针相减是表示两个指针之间的元素个数
  //10000000000000000000000000000100
  //11111111111111111111111111111011
  //11111111111111111111111111111100 -->地址FFFFFFFC
  //答案是FFFFFFFC,-4
  return 0;
}

7.6 难度:  **

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));
  //10   5
  return 0;
}

7.6 难度 **

int main()
{
  char* a[] = { "work","at","alibaba" };
  //a的每个元素是char*的,存的是首字符地址  
  //答案 at
  char** pa = a;
  pa++;
  printf("%s\n", *pa);
  return 0;
}

7.7 难度 : ****

int main()
{
  //++的优先级高于*号
  char* c[] = { "ENTER","NEW","POINT","FIRST" };
  char** cp[] = { c + 3,c + 2,c + 1,c };
  char*** cpp = cp;
  printf("%s\n", **++cpp);   //p的地址 POINT
  printf("%s\n", *-- * ++cpp + 3); //得到的是e的地址再+3打印出来就是ER
  printf("%s\n", *cpp[-2] + 3);  //ST
  printf("%s\n", cpp[-1][-1] + 1);//EW
  return 0;
}

相关文章
|
19天前
|
存储 安全 C++
C++中的引用和指针:区别与应用
引用和指针在C++中都有其独特的优势和应用场景。引用更适合简洁、安全的代码,而指针提供了更大的灵活性和动态内存管理的能力。在实际编程中,根据需求选择适当的类型,能够编写出高效、可维护的代码。理解并正确使用这两种类型,是掌握C++编程的关键一步。
21 1
|
19天前
|
数据采集 存储 编译器
this指针如何使C++成员指针可调用
本文介绍了C++中的this指针,它是一个隐藏的指针,用于在成员函数中访问对象实例的成员。文章通过代码示例阐述了this指针的工作原理,以及如何使用指向成员变量和成员函数的指针。此外,还提供了一个多线程爬虫示例,展示this指针如何使成员指针在对象实例上调用,同时利用代理IP和多线程提升爬取效率。
this指针如何使C++成员指针可调用
|
5天前
|
存储 安全 编译器
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
32 5
|
4天前
|
C++ 容器
【编程技巧】 C++11智能指针
C++11引入了智能指针以自动管理内存,防止内存泄漏和悬挂指针: - `shared_ptr`:引用计数,多所有权,适用于多个对象共享资源。 - `unique_ptr`:独占所有权,更轻量级,适用于单一对象所有者。 - `weak_ptr`:弱引用,不增加引用计数,解决`shared_ptr`循环引用问题。 ## shared_ptr - 支持引用计数,所有者共同负责资源释放。 - 创建方式:空指针、new操作、拷贝构造/移动构造,以及自定义删除器。 - 提供`operator*`和`operator-&gt;`,以及`reset`、`swap`等方法。 ## unique_ptr
|
5天前
|
C++ 容器
C++之评委打分案例(vector与deque容器练习)
C++之评委打分案例(vector与deque容器练习)
8 1
|
7天前
|
存储 Java C#
C++语言模板类对原生指针的封装与模拟
C++|智能指针的智能性和指针性:模板类对原生指针的封装与模拟
|
5天前
|
C++
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
7 0
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
|
7天前
|
设计模式 C++ 开发者
C++一分钟之-智能指针:unique_ptr与shared_ptr
【6月更文挑战第24天】C++智能指针`unique_ptr`和`shared_ptr`管理内存,防止泄漏。`unique_ptr`独占资源,离开作用域自动释放;`shared_ptr`通过引用计数共享所有权,最后一个副本销毁时释放资源。常见问题包括`unique_ptr`复制、`shared_ptr`循环引用和裸指针转换。避免这些问题需使用移动语义、`weak_ptr`和明智转换裸指针。示例展示了如何使用它们管理资源。正确使用能提升代码安全性和效率。
14 2
|
12天前
|
存储 算法 安全
C++一分钟之-数组与指针基础
【6月更文挑战第19天】在C++中,数组和指针是核心概念,数组是连续内存存储相同类型的数据,而指针是存储内存地址的变量。数组名等同于指向其首元素的常量指针。常见问题包括数组越界、尝试改变固定大小数组、不正确的指针算术以及忘记释放动态内存。使用动态分配和智能指针可避免这些问题。示例代码展示了安全访问和管理内存的方法,强调了实践的重要性。
26 3
|
17天前
|
C++
C++小练习:猜数游戏
C++小练习:猜数游戏