关于指针,你不可以错过的练习(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;
}

相关文章
|
7天前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
25天前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
24 1
|
26天前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
22 2
|
28天前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
1月前
|
存储 C++ 索引
C++函数指针详解
【10月更文挑战第3天】本文介绍了C++中的函数指针概念、定义与应用。函数指针是一种指向函数的特殊指针,其类型取决于函数的返回值与参数类型。定义函数指针需指定返回类型和参数列表,如 `int (*funcPtr)(int, int);`。通过赋值函数名给指针,即可调用该函数,支持两种调用格式:`(*funcPtr)(参数)` 和 `funcPtr(参数)`。函数指针还可作为参数传递给其他函数,增强程序灵活性。此外,也可创建函数指针数组,存储多个函数指针。
|
2月前
|
编译器 C++
【C++核心】指针和引用案例详解
这篇文章详细讲解了C++中指针和引用的概念、使用场景和操作技巧,包括指针的定义、指针与数组、指针与函数的关系,以及引用的基本使用、注意事项和作为函数参数和返回值的用法。
33 3
|
27天前
|
算法 C++
【算法】双指针+二分(C/C++
【算法】双指针+二分(C/C++
|
28天前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
2月前
|
C++
C++(十八)Smart Pointer 智能指针简介
智能指针是C++中用于管理动态分配内存的一种机制,通过自动释放不再使用的内存来防止内存泄漏。`auto_ptr`是早期的一种实现,但已被`shared_ptr`和`weak_ptr`取代。这些智能指针基于RAII(Resource Acquisition Is Initialization)原则,即资源获取即初始化。RAII确保对象在其生命周期结束时自动释放资源。通过重载`*`和`-&gt;`运算符,可以方便地访问和操作智能指针所指向的对象。
|
2月前
|
C++
C++(九)this指针
`this`指针是系统在创建对象时默认生成的,用于指向当前对象,便于使用。其特性包括:指向当前对象,适用于所有成员函数但不适用于初始化列表;作为隐含参数传递,不影响对象大小;类型为`ClassName* const`,指向不可变。`this`的作用在于避免参数与成员变量重名,并支持多重串联调用。例如,在`Stu`类中,通过`this-&gt;name`和`this-&gt;age`明确区分局部变量与成员变量,同时支持链式调用如`s.growUp().growUp()`。