梦开始的地方——c语言指针练习题(代码片段)

爱敲代码的三毛 爱敲代码的三毛     2022-12-03     451

关键词:

指针练习

注意我的环境是:win10下的VS2019-X86环境

练习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取出整个数组的地址,但它的地址和首元素的地址还是一样的,+1跳过了整个数组,现在指向的是数组最后一个元素后面的那个地址,把它强制转换为int*的指针
  • a是数组首元素地址,+1就是数组第二个元素的地址,解引用拿到的就是第2个元素的
  • 对(a+1)解引用输出的就是 2
  • ptr现在指向的是数组最后一个元素后面的那一块地址,现在prt是一个整形指针-1,往回走4个字节。就指向了数组最后一个元素的起始地址

最后输出

2,5

练习2

注意这是在在VS2019,x86环境

struct Test

    int Num;
    char *pcName;
    short sDate;
    char cha[2];
    short sBa[4];
*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()

    p = (struct Test*)0x100000;
    
    printf("%p\\n", p + 0x1);
    printf("%p\\n", (unsigned long)p + 0x1);
    printf("%p\\n", (unsigned int*)p + 0x1);
    
    return 0;

  • 结构体是20个字节,p是一个结构体指针,0x1是一个数组,相当于对指针+1,跳过结构体指针所指向元素大小,也就是20个字节

  • 最后打印 100014

  • p的地址是0x100000,强制转为为一个无符号的长整形,此时就会把0x100000当做16进制转换为无符号长整形

    0x100000 转二进制 00010000 00000000 00000000 ,再+1就是 00010000 00000000 00000001

    最后以%p的形式来打印,就会把这个二进制当做地址来打印,当然它是以16进制的形式打印

    0x100001

  • 最后一个printf,把结构体指针强制类型转换为无符号整形的指针,跳过4个字节,0x100004

最后打印

00100014
00100001
00100004

练习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);
    return 0;

  • 我这里的环境是win10下的VS2019-X86环境
  • 我们知道数组的地址是随着下标的增长从低到高,内存中存储的是16进制的数字,一个16进制位对应4个比特位
  • 我这里是小端存储,所以在内存中把低位字节存储在低地址处,高位字节储存在高地址处。
  • &a+1跳过整个数组,再强制类型转换为int*的指针,现在ptr1指向数组末尾的地址处
  • a是数组名里面存的是地址,地址是一个十六进制的数字,把这个地址强制转换为int类型的10进制。
  • 强制类型转换后再+1,就是对整形加了个1。然后再把+1之后的整形转换为int*的地址
  • 我们知道每个内存单元的大小是一个字节,每个字节有自己的一个地址,上面+1相当于对地址+1
  • 所以就跳过了一个字节,一个字节8个位,两个16进制位就等于一个字节
  • ptr1[-1]相当于(prt1+(-1)),此时ptr1就指向了数组最后一个元素的起始地址,再以%x打印
  • 打印的是16进制,我们是以小端存储放进去的就要以小端存储的形式拿出来,就是0x00000004
  • 此时ptr2指向的是数组第一个元素第二个字节的位置,ptr2是一个int*的指针,解引用拿到后面四个字节
  • 又因为我这里的平台是小端存储,所以拿出来就是0x02000000

最后打印

4,2000000

练习4

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

  • 化括号里放的是逗号表达式,逗号表达式取的是最后一个值
  • 二维数组在内存中也是连续存储的,p指向数组首元素
  • p[0]相当于*(p+0)

输出

1

练习5

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个元素,把二维数组首元素也就是第一行一维数组的地址赋给p

  • p[4][2]等价于*(*(p+4)+2),p是一个数组指针,指向的数组有4个元素,所以每加一次就会跳过4个元素

  • *(p+4)相当于拿到了一个数组名,也就是数组的首地址,再+2就拿到这个数组第二个元素的地址,解引用就拿到第二元素

  • &p[4][2] - &a[4][2],指针和指针相减的绝对值拿到的是指针之间的元素个数,这里是小减大所以是-4

  • -4在内存中的补码

    原码:10000000 00000000 00000000 00000100

    反码:11111111 11111111 11111111 11111011

    补码:11111111 11111111 11111111 11111100

  • 再以%p的形式打印,而%p打印地址是无符号的,它就会认为-4存的内存中的补码就是原码直接以16进制打印

  • FFFF FFFC

  • 而%d是打印有符号的整形就会直接打印-4

打印结果

FFFFFFFC,-4

练习6

int main()

	int a[2][5] =  1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ;
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)(*(a + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));

	return 0;

  • &a取出整个二维数组的地址+1,跳过整个二维数组,再强制类型转换为int*
  • a是二维数组的数组名,数组名是首元素地址,也就是第一行一维数组的地址,*(a+1)跳过第一行,拿到整个第二行一维数组的地址,也就是第二行的数组名。再强制类型转换为int*
  • *(ptr1-1),ptr1是一个整形指针,-1往回走4个字节
  • *(ptr2-1),同理往回走

最后输出

10,5

练习7

int main()

	char* a[] =  "work","at","alibaba" ;
	char** pa = a;
	pa++;
	printf("%s\\n", *pa);
	return 0;

  • char* a[]数组里存的都是字符串第一个字符的地址
  • pa是一个二级指针,存放的数组a的地址,也就是char* a[]首元素的地址
  • pa++,相当于指针+1,此时就指向了数组第二元素
  • *pa,pa指向数组第二元素,第二个元素是字符串的首地址,对它解引用就拿到了字符串

最后输出

at

练习8

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;

没有执行任何printf的指针指向图

  • **(++cpp),第一接引用拿到c数组第二个元素的地址,再解引用拿到第二元素所指向的字符串
  • 此时的cpp指向发生改变

  • 再执行第二printf,cpp里面存的是c+2的地址
  • ++cpp,让cpp指向下一个位置也就是c+1元素,在解引用找到c+1--修改c+1的指向,让它指向它的上一个元素,此时它指向的是一个字符串“ENTER”,再解引用拿到这字符串的首地址,再+3
  • 此时指向的就是"ST"

  • 前两个printf会修改指针指向,而后面两个不会
  • *cpp[-2]等价于*(cpp-2),指向的是“FIRST”,再+3跳过3个字符
  • cpp[-1][-1]等价于*(*(cpp-1)-1),此时指向的是"NEW"
  • 在+1跳过一个字符

最后输出

POINT
ER
ST
EW

梦开始的地方——c语言文件操作详解(代码片段)

文章目录C语言文件操作1.什么是文件?2.文件指针3.文件的打开和关闭4.文件的顺序读写fgetc&fputcfgets&fputsfread&fwritefscanf&fprintfscanf/fscanf/sscanf对比printf/fprintf/sprintf5.文件的随机读写(fseek&ftell&rewind)6.文件结 查看详情

梦开始的地方——c语言:函数指针+函数指针数组+指向函数指针数组的指针(代码片段)

文章目录一、函数指针1.函数指针定义2.函数指针的使用3.解读函数指针代码二、函数指针数组三、指向函数指针数组的指针四、回调函数一、函数指针1.函数指针定义整形指针是指向整形的指针:存放整形的地址数组指针是... 查看详情

梦开始的地方——c语言数据在内存中的存储(整形+浮点型)(代码片段)

文章目录整形在内存中的存储1.数值类型的基本分类2.整形在内存中的存储1.原码、反码、补码2.内存中为什么要存放补码?3.大小端存储4.无符号有符号数练习5.有符号数无符号数小结浮点型在内存中的存储IEEE754整形在内存中... 查看详情

梦开始的地方——c语言柔性数组(代码片段)

文章目录柔性数组什么是柔性数组?柔性数组的使用柔性数组的优点柔性数组什么是柔性数组?在C99中,结构体最后一个元素它允许是一个未知大小的数组,这就叫做柔性数组成员。这个概念听起来可能有点不可... 查看详情

梦开始的地方——c语言常用字符函数汇总(代码片段)

文章目录字符库函数1.strlen(求字符串长度)2.strcpy(字符串拷贝)3.strcat(字符串追加函数)4.strcmp(字符串比较)5.strncpy(字符串拷贝)6.strncat(字符串追加)7.strncmp(字符串比较)8.strstr(字符串查找)9.strtok(字符串分割)10.strerror(错误信息)字符库... 查看详情

梦开始的地方——c语言预处理+编译过程(代码片段)

文章目录C语言程序的编译(预处理)1.编译和链接1)编译的几个阶段预编译阶段编译阶段汇编阶段2)链接2.预处理1)预定义符号2)#define3)#和##4)带副作用的宏参数5)宏和函数对比3.常见预处理命令1)#undef2)命令行定义3)条件编译4)文件包含5... 查看详情

梦开始的地方——c语言中那些细节(代码片段)

文章目录static关键字1.static修饰局部变量2.static修饰全局变量3.static修饰函数函数的默认返回值隐式类型转换1.整形提升2.整型提升的意义3.算数转换static关键字1.static修饰局部变量生命周期延长:该变量不随函数结束而结束ÿ... 查看详情

梦开始的地方——c语言动态内存管理(malloc+calloc+realloc+free)(代码片段)

文章目录动态内存管理1.为什么需要动态内存分配?2.动态内存函数malloc&freecallocrealloc3.常见的动态内存错误对NULL解引用对动态开辟空间的越界访问对非动态开辟内存使用free释放使用free释放一块动态开辟内存的一部分对同... 查看详情

c语言编程中的指针(代码片段)

单片机编程中,涉及指针的地方很多,需要多看多练习多总结。#include<string.h>#include<stdio.h>#include<stdlib.h>intmain(void)unsignedintpInt32;//指向32位无符号整数的指针unsignedintUINT32_1;//32位的无符号整数unsignedintArray1_UINT3 查看详情

c语言习题练习5——指针(代码片段)

目录1.一维数组 2.字符数组3.二维数组4.指针系列笔试题(1-8题)5.实现一个函数,可以左旋字符串中的k个字符。字符串右旋:6.写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串7.有一个数字矩阵ÿ... 查看详情

梦开始的地方,从最小二乘法开始学机器学习(代码片段)

梦开始的地方,从最小二乘法开始学机器学习从这篇博客开始,我们将逐步实现不同的机器学习代码,以此来深入学习了解不同的机器学习背后的原理~文章目录梦开始的地方,从最小二乘法开始学机器学习00.参考... 查看详情

梦开始的地方,从最小二乘法开始学机器学习(代码片段)

梦开始的地方,从最小二乘法开始学机器学习从这篇博客开始,我们将逐步实现不同的机器学习代码,以此来深入学习了解不同的机器学习背后的原理~文章目录梦开始的地方,从最小二乘法开始学机器学习00.参考... 查看详情

梦开始的地方——c语言(枚举+位段+联合体)(代码片段)

位段1.什么是位段?要想了解位段就得先学会使用结构体。文章链接——>详解结构体位段的声明和结构体是十分类似的,它们有两个不同之处位段的成员必须是int、unsignedint、signedint位段的成员名后面有一个冒号和数字... 查看详情

c语言攻略-从零开始的c语言生活----初阶篇(代码片段)

各位大佬大家好啊!从今天开始正式的学习C语言,就废话不多说我所使用编译器:【VisualStudio2019】目录了解什么是C语言    C语言的发展史第一个C程序——梦开始的地方数据类型数据类型所占内存大小(sizeof关... 查看详情

c语言篇+指针进阶练习+qsort模拟实现(回调函数思想)+指针和数组笔试题(代码片段)

目录前言冒泡排序了解qsortc语言库qsort的使用qsort模拟实现指针和数组笔试题解析一维数组字符数组二维数组总结:数组名的意义前言qsort(quicksort)根据你给的比较函数给一个数组快速排序,是通过指针移动实现... 查看详情

梦开始的地方

1.自我介绍大家好,我是薛文李,是一名南京信息工程大学本科一年级计算机类的学生。高中时期我就对编程语言充满了憧憬,想着将来大学就学计算机,结果如愿以偿,我还是很幸运的进入到了心仪的专业。但是... 查看详情

指针中容易混淆的概念以及常见笔试题(代码片段)

...*p,intnum1,intnum2,int(*cmp)(counstvoid*e1,counstvoid*e2))8.指针和数组练习题1.字符串指针注:注意代码中的注释注:void*类型的指针可以接收任意 查看详情

01梦开始的地方,阿仁的c++学习笔记(p2~)(代码片段)

...业需要的考虑和受大佬室友的影响,实习之余,本菜鸡将开始学习c++。用的是bilibil播放量最多的那个c++入门课程,环境是vscode,开此贴为笔记之用。1、第一个helloworld程序1#include<iostream>2usingnamespacestd;34intmain()56cout<<"hello... 查看详情