【C语言航路】第六站:指针初阶

news2024/9/23 1:30:16

目录

一、指针是什么

二、指针和指针类型

1.指针类型的意义

2.指针+-整数

3.指针解引用

三、野指针

1.野指针的成因

(1)指针未初始化

(2)指针越界访问

(3)指针指向的空间释放

 2.如何规避野指针

(1) 指针初始化

(2) 小心指针越界

(3) 指针指向空间释放,及时置NULL

(4) 避免返回局部变量的地址

(5) 指针使用之前检查有效性

四、指针运算

1.指针+-整数

2.指针-指针

3.指针的关系运算

五、 指针和数组

六、二级指针

1.什么是二级指针?

2.二级指针的解引用

 七、指针数组

1.指针数组的概念

 2.尝试模拟一个二维数组

 总结


一、指针是什么

这部分内容,在我们之前的文章中已经提及过

链接:【C语言航路】第一站:(初识C语言(终幕)

这里在简单的回忆一下

学习指针必须先要理解——内存,内存是电脑上的存储设备,一般都是4G/8G/16G等,程序运行的时候会加载到内存中,也会使用内存空间

我们将内存划分为一个个小格子,每一个格子是一个内存单元,也正好是一个字节的大小,对每一个内存单元进行编号,在生活中我们也将这一个个编号称作地址,而地址在c语言中又叫做指针

 我们举一个例子,假设我们定义一个变量 int a=10;那么a是一个int类型的变量,需要占用四个字节的空间,而每个字节都有地址,&a取出的是哪一个的地址呢?其实取出的是第一个字节的地址(较小的地址),也就是说,下图中,&a最终取出来的地址是0x0012ff40 ,而这个地址我们可以存放到一个变量中,int* pa=&a,这颗*代表pa是一个指针,int代表pa所指向的类型是int类型,这个pa也叫做指针变量。

理解指针需要理解两个要点

1.指针是内存中一个最小单元的编号,也就是地址

2.平时口头语中所说的指针,通常指的是变量,是用来存放内存地址的变量

总结:指针就是地址,平时口头语中的指针通常指的是指针变量

我们看这段代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	printf("%p\n", &a);
	printf("%p\n", p);
	return 0;
}

运行结果为,打印出来的地址是一样的

 我们还可以看这段代码,使用*来解引用指针

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	printf("%p\n", &a);
	printf("%p\n", p);
	*p = 20;
	printf("%d", a);
	return 0;
}

运行结果为

总结:指针变量,就是用来存放地址的变量。(存放在指针中的值都被当成地址处理)。

那这里的问题是: 一个小的单元到底是多大?   其实是1个字节

那么如何编址呢?

经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。 对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0)

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

.....

11111111 11111111 11111111 11111111

这里就有2的32次方个地址。 每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB)

4G的空间进行编址。 同样的方法,那64位机器,如果给64根地址线,那能编址多大空间,我们也可以计算出来

这里我们就知道了:

在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。

那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地 址。

总结: 指针变量是用来存放地址的,地址是唯一标示一个内存单元的。 指针的大小在32位平台是4个字节,在64位平台是8个字节

二、指针和指针类型

1.指针类型的意义

我们在上面了解了

32位机器上,地址是4个字节,指针变量的大小也是4个字节

64位机器上,地址是8个字节,指针变量的大小也是8个字节

那在这里就有人产生困惑了,反正都是4个或者8个字节,那为什么要区分int*,char*.....这些呢?为什么不直接弄一个通用指针呢?

其实既然计算机没有这个通用指针,那就说明这些类型是有意义的。我们现在就来探讨以下这些指针的类型的意义。

为了了解这个内容,我们先看这个代码

#include<stdio.h>
int main()
{
	int a = 0x11223344;
	int* pa = &a;
    *pa=0;
	return 0;
}

我们打开调试,窗口,内存,并将列改为4

 继续往下走

 我们发现,四个字节全部被改为0

我们在看这一段代码

#include<stdio.h>
int main()
{
	int a = 0x11223344;
	//int* pa = &a;
	char* pc = &a;
	*pc = 0;
	return 0;
}

我们仍然监视内存

 继续往下走

 我们发现只改变了一个字节。

所以我们得出结论:指针类型是有意义的

指针类型决定指针进行解引用时候,一次访问几个字节(访问权限)

int*访问四个字节 char*访问一个字节,float*访问四个字节

我们在继续看这个代码

#include<stdio.h>
int main()
{
	int a = 0x11223344;
	int* pa = &a;
	char* pc = &a;
	printf("%p\n", pa);
	printf("%p\n", pa+1);
	printf("%p\n", pc);
	printf("%p\n", pc+1);

	return 0;
}

运行结果为

 由此我们发现,pa+1跳过了四个字节,pc+1跳过了一个字节

所以我们得出

指针类型决定指针的步长(指针+1到底跳过几个字节)

字符指针+1,跳过一个字节

整型指针+1,跳过四个字节

我们可以看这个代码

#include<stdio.h>
int main()
{
	int a = 0x11223344;
	char* pc = (char*)&a;
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		*pc = 0;
		pc++;
	}
	return 0;
}
我们先取出a的地址放到一个char*类型的指针中,因为a取地址后是一个int*类型的指针,所以要强制类型转换。对其进行遍历四次,每次只能改一个字节的空间,四次刚好将a改为0。

我们在这里在总结一下指针类型的意义:

1.

指针类型决定指针进行解引用时候,一次访问几个字节(访问权限)

int*访问四个字节 char*访问一个字节,float*访问四个字节

2.

指针类型决定指针的步长(指针+1到底跳过几个字节)

字符指针+1,跳过一个字节

整型指针+1,跳过四个字节

2.指针+-整数

我们在上面说过,指针的不同类型,其实提供了不同的视角去观看和访问内存

char*一次访问1个字节,+1跳过一个字节

int *一次访问4个字节,+1跳过四个字节

当然在这里,除了+1,还可以-5,+2等操作

比如int*  pa=10;

pa+4  其实就是向后走4*sizeof(int)个字节,也就是+16个字节

pa-5   其实就是向前走5*sizeof(int)个字节,也就是-20个字节

也就是指针类型决定了指针向前或向后走一步有多大(距离)

3.指针解引用

这里也同样在前面说过了

指针的类型决定了对指针解引用时有多大权限(能操作几个字节)

比如char*指针解引用一次就能操作一个字节,而int*指针解引用一次可以操作4个字节

三、野指针

概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

1.野指针的成因

(1)指针未初始化

#include <stdio.h>
int main()
{
    int* p;//局部变量指针未初始化,默认为随机值
    *p = 20;
    return 0;
}

如上代码所示,指针是一个局部变量未初始化,默认未随机值,这样随意进行修改指针的值是非常危险的行为。

(2)指针越界访问

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    for (i = 0; i <= 11; i++)
    {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
    }
    return 0;
}

如上代码所示,指针的指向范围超出了数组arr的范围,p就是一个野指针,此时如果随意修改指针里的内容是很危险的,假如这个野指针指向的恰好就是本程序已有的其他值,那么就会出现问题。

(3)指针指向的空间释放

我们看这个代码

#include<stdio.h>
int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	printf("%d", *p);
	return 0;
}

这个代码也是,a是一个局部变量,返回的时候a已经被销毁了,此时的这个地址就是一个野指针。

当然我们可以运行一下,我们会发现仍然是10,这是因为之前的函数栈帧还没有被破坏掉。我们加上个代码就能破坏掉这个函数栈帧,导致结果不一样

 2.如何规避野指针

(1) 指针初始化

#include<stdio.h>
int main()
{
	int a = 0;
	int* pa = &a;//指针的初始化
	int* pc = NULL;//空指针,专门用来初始化指针
	return 0;
}

如上代码所示,如果不知道该初始化成什么,可以初始化成NULL,当然要注意,空指针不可以解引用。解引用空指针会使程序崩溃。因为0地址处是不能让用户使用的

(2) 小心指针越界

(3) 指针指向空间释放,及时置NULL

(4) 避免返回局部变量的地址

(5) 指针使用之前检查有效性

四、指针运算

指针的运算共有三种

1.指针+- 整数

2.指针-指针

3.指针的关系运算

1.指针+-整数

这快的内容在前文中已经涉及过,这里在简单的讲解一个案例

#define N_VALUES 5
#include<stdio.h>
int main()
{
    float values[N_VALUES];
    float* vp;
    //指针+-整数;指针的关系运算
    for (vp = &values[0]; vp < &values[N_VALUES];)
    {
        *vp++ = 0;
    }
    return 0;
}

这段代码唯一需要注意的就是一个表达式*vp++。++的优先级比较高,但是由于他是前置++,所以先解引用vp,然后vp++。最终的效果就是将这个数组置零。

2.指针-指针

指针-指针有个前提

两个指针要指向同一个空间,并且两个指针类型相同

指针-指针的绝对值是两个指针之间的元素个数

我们看这个代码

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%d", &arr[9] - &arr[0]);
	return 0;
}

运行结果为

 我们之前讲解过自己实现一个求字符串长度的解法,一种是计数器,另外一种是递归的思想,今天我们在采用一种指针-指针的方法,使用\0处的指针-起始点

代码如下

#include<stdio.h>
int my_strlen(char* arr)
{
	int* str = arr;
	while (*arr != '\0')
	{
		arr++;
	}
	return arr - str;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

当然,我们也可以将*,和++进行合并

#include<stdio.h>
int my_strlen(char* arr)
{
	int* str = arr;
	while (*arr++ != '\0')
		;
	return arr - str - 1;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

3.指针的关系运算

#define N_VALUES 5
#include<stdio.h>
int main()
{
	float values[N_VALUES];
	float* vp;
	for (vp = &values[N_VALUES]; vp > &values[0];)
	{
		*--vp = 0;
	}
}

这段代码与前面一段代码很相似,功能就是将数组置零。这个是先--vp然后在解引用,最终vp停留的位置就是数组元素的起点

我们在看一下这段代码

#define N_VALUES 5
#include<stdio.h>
int main()
{
	float values[N_VALUES];
	float* vp;
	for (vp = &values[N_VALUES - 1]; vp >= &values[0]; vp--)
	{
		*vp = 0;
	}
}

这段代码,是将上面的*和--混用的部分代码给拆开写了。这样写确实可以实现我们的功能,但是要注意的是,第二种写法在绝大多数编译器是没有问题的,但是然而我们还是应该避免这样写,因为标准并不保证它可行。在少部分的编译器上还是会出问题的。

因为这个段代码最后会出现数组首元素的前一个地址与这个数组首元素地址进行比较。

而我们的标准是这样规定的:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较。

五、 指针和数组

1.指针和数组是不同的对象

指针是一种变量,存放地址的,大小4/8字节的

数组是一组相同类型元素的集合,是可以放多个元素的,大小是取决于元素个数和元素类型的

2.数组的数组名是数组首元素的地址,地址是可以放在指针变量中

可以通过指针访问数组

比如下面的代码就可以实现指针访问数组

#include<stdio.h>
int main()
{
    int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    //赋值
    for (i = 0; i < sz; i++)
    {
        *p = i + 1;
        p++;
    }
    //打印
    p = arr;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", *p);
        p++;
    }
    return 0;
}

或者也可以这样写

#include<stdio.h>
int main()
{
    int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    //赋值
    for (i = 0; i < sz; i++)
    {
        *(p + i) = i + 1;   
    }
    //打印
    for (i = 0; i < sz; i++)
    {
        printf("%d ", *(p+i));

    }
    return 0;
}

还有一点需要说明的是

我们在这里出现了

int arr[10];

int* p=arr;

这里说明arr是一个首元素地址

而地址就可以解引用。所以我们知道arr[i]--->*(arr+i)

而加法是满足交换律的,所以进而推出*(i+arr)

进而推出    i[arr]

事实上,这个确实是没有问题的,可以正常使用的,因为 [ ]他只是一个操作符,i和arr是这个操作符的操作数而已。在我们电脑里面arr[i]也会被翻译成*(arr+i)。

六、二级指针

1.什么是二级指针?

我们在之前说过的指针是这样的int* pa=&a;也就是将a的地址放入pa中,pa的类型是int*。

那么pa这个指针其实应该也有一个地址,我们如果取出他的地址,将他放入另外一个变量,这就叫做二级指针。

也就是int**ppa=&pa。其中第二颗*代表着他是一个指针,前面的int*代表着他指向的类型是一个int*类型的数据

如下代码所示

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	return 0;
}

如下图示关系

2.二级指针的解引用

对于二级指针的解引用需要两颗*

有如下等式成立

**ppa==*pa=a

举个例子

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	printf("%d\n", **ppa);
	**ppa = 50;
	printf("%d\n", **ppa);
	return 0;
}

运行结果为

 七、指针数组

1.指针数组的概念

在这里,我们首先需要了解指针数组是数组还是指针呢?

其实,是数组,从语言的角度来思考,指针是修饰词,数组才是主语

比如字符数组,他是一个数组,里面存放的都是字符

比如整型数组,他是一个数组,里面存放的都是整型

所以我们便能猜测到,指针数组,他是一个数组,里面存放的都是指针

我们举一个例子

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	int e = 50;
	int* arr[5] = { &a,&b,&c,&d,&e };
	int i = 0;
	for (i - 0; i < 5; i++)
	{
		printf("%d ", *(arr[i]));
	}
	return 0;
}

运行结果为

 图解为

 2.尝试模拟一个二维数组

我们了解了指针数组的概念以后,我们可以利用其模拟一个二维数组,假设我们要模拟三行四列的二维数组

我们是这样想的,先定义出三个一维数组a,b,c。然后定义一个指针数组,令指针数组的每个元素是这些一维数组的首元素地址。然后我们就可以通过两次遍历就能模拟出来这个二维数组了

 代码如下

#include<stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	int b[] = { 2,3,4,5 };
	int c[] = { 3,4,5,6 };
	int* arr[] = { a,b,c };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", *(arr[i]+j));
		}
		printf("\n");
	}
	return 0;
}

运行结果为

当然,如果我们将*(arr[i]+j)改为arr[i][j]也是正确的,因为这两种是可以相互转换的


 总结

本节课我们主要详细讲解了指针与内存对于他们理解,指针和指针类型,指针类型的意义,野指针的成因,以及如何规避野指针,指针的三种运算,指针与数组的关系,二级指针,以及指针数组

如果对你有帮助的话,不要忘记点赞加收藏哦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/59727.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

伸手运动想象训练与伸手抓取想象的关系

本研究旨在确定为期4周的目标导向性伸手&#xff08;抓取任务&#xff09;的运动想象训练&#xff08;MIT&#xff09;是否会以相同的方式影响伸手&#xff08;MIR&#xff09;和抓取&#xff08;MIG&#xff09;运动想象的皮质活动。试验过程中&#xff0c;我们在健康的年轻参…

基于未知环境下四旋飞行器运动规划应用研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

QT QDoubleSpinBox 浮点计数器控件(使用详解)

本文详细的介绍了QDoubleSpinBox控件的各种操作&#xff0c;例如&#xff1a;新建界面、获取数值、设置前后缀、设置最大/小值、设置显示精度、关联信号槽、优化信号、关联控件、文件源码、样式表等等操作。 本文是QT控件使用详解的第十五篇 QT QDoubleSpinBox 浮点计数器控件(…

【ArcGIS风暴】ArcGIS栅格影像去除黑边(背景值)方法汇总

文章目录 1. 数据加载时属性中设置去除黑边2. 应用setnull工具去除黑边3. 应用栅格计算器去除黑边4. 应用复制栅格工具去除黑边5. 应用影像分析去除黑边6. 应用镶嵌数据集去除黑边影像产生黑边的原因无外乎在设置无效值时,将无效值设成了0,而影像在导入软件进行渲染时,并没有…

制作一个简单HTML静态网页(HTML+CSS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

TensorRT安装

本文是为了记录安装TensorRT过程中遇到的一些问题。 首先进入TensorRT下载页面&#xff0c;选择你要下载的TensorRT版本。 因为TensorRT不同的版本依赖于不同的cuda版本和cudnn版本。所以很多时候我们都是根据我们自己电脑的cuda版本和cudnn版本来决定要下载哪个TensorRT版本。…

[附源码]计算机毕业设计校园招聘系统设计Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

如何收到消息第一时间将网站置灰,难道让程序员上个线?

注意&#xff1a;文本不是讲如何将网站置灰的那个技术点&#xff0c;那个技术点之前汶川地震的时候说过。 本文不讲如何实现技术&#xff0c;而是讲如何在第一时间知道消息后&#xff0c;更快速的实现这个置灰需求的上线。 实现需求不是乐趣&#xff0c;指挥别人去实现需求才…

安全研究 # 二进制代码相似性检测综述

本文参考&#xff1a; [1]方磊,武泽慧,魏强.二进制代码相似性检测技术综述[J].计算机科学,2021,48(05):1-8. (信息工程大学数学工程与先进计算国家重点实验室, 国家重点研发课题,北大核心) 摘要 代码相似性检测常用于代码预测、知识产权保护和漏洞搜索等领域&#xff0c;可分为…

Numpy入门[11]——生成数组的函数

Numpy入门[11]——生成数组的函数 参考&#xff1a; https://ailearning.apachecn.org/ 使用Jupyter进行练习 import numpy as nparange arange 类似于Python中的 range 函数&#xff0c;只不过返回的不是列表&#xff0c;而是数组&#xff1a; arange(start, stop None, st…

Java并发编程—java内存模型2

文章目录重排序数据依赖性as-if-serial重排序对多线程的影响顺序一致性同步程序的顺序一致性效果同步/异步总线事务双重校验锁—————————————————————————————————— 重排序 数据依赖性 数据依赖不能进行重排序 as-if-serial as-if-seri…

[附源码]计算机毕业设计大学生心理健康测评系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Compressed Bloom Filters论文总结

Compressed Bloom Filters论文总结AbstractI. INTRODUCTIONII. COMPRESSED BLOOM FILTERS:THEORYA. Bloom FiltersB. Compressed Bloom FiltersIII. COMPRESSED BLOOM FILTERS:PRACTICEA. ExamplesIV. DELTA COMPRESSIONV. COUNTING BLOOM FILTERSVI. CONCLUSIONAbstract 我们…

Elasticsearch面试题

Elasticsearch面试题 1 为什么要使用Elasticsearch? 系统中的数据&#xff0c;随着业务的发展&#xff0c;时间的推移&#xff0c;将会非常多&#xff0c;而业务中往往采用模糊查询进行数据的搜索&#xff0c;而模糊查询会导致查询引擎放弃索引&#xff0c;导致系统查询数据…

C#/WPF/.NET 找到的程序集清单定义与程序集引用不匹配

vs 窗口报错 引发的异常:“System.Windows.Markup.XamlParseException”(位于 PresentationFramework.dll 中) “初始化“CircularGauge.CircularGaugeControl”时引发了异常。”&#xff0c;行号为“288”&#xff0c;行位置为“23”。代码位置报错 FileLoadException: 未能…

【Qt记录】属性 Q_PROPERTY

使用&#xff1a; Qt 拥有一个属性系统。我经常在QSS中使用 QWidget#SWNotifyMsgDialog QLabel#label_sure[status"normal"]配合在代码中使用 ui.label_sure->setProperty("status","warning"); 函数原型&#xff1a;bool QObject:setProp…

物联网开发笔记(56)- 使用Micropython开发ESP32开发板之手机蓝牙控制舵机

一、目的 这一节我们学习如何使用我们的ESP32开发板来实现通过蓝牙控制接在ESP32开发板上的舵机。 二、环境 ESP32 MG90S舵机 Thonny IDE 几根杜邦线 手机 舵机的链接方法见第54节&#xff1a;物联网开发笔记&#xff08;54&#xff09;- 使用Micropython开发ESP32开发板之…

Win11的两个实用技巧系列之电脑死机解决办法

目录 Win11电脑突然死机卡住不动?Win11电脑死机屏幕静止 方法一&#xff1a; 方法二&#xff1a; sfc包括有以下命令&#xff1a; 方法三&#xff1a; 点击拿去 Win11电脑突然死机卡住不动?Win11电脑死机屏幕静止 在使用Win11系统时&#xff0c;很多用户经常会遇到自己…

12月3日下午:thinkphp框架中的视图以及模型剩余部分

回忆知识&#xff1a; dump()与halt()方法 dump()&#xff1a;输出内容后不会终止脚本&#xff0c;会继续向下执行 halt()&#xff1a;输出内容后会终止脚本&#xff0c;结束程序 //dump()和halt()public function haltTest(){$result \db(demo)->where(id,,1)->select…

【Qt记录】Windows 窗口层级(Z-order)

功能&#xff1a;软件有一个提示弹窗&#xff0c;希望让他永远在最上面&#xff0c;保证该窗口在任何情况下不会被遮挡。 由上面的问题就查找对应的api找到了SetWindowPos()。 在说这个函数之前&#xff0c;先了解以下概念&#xff1a; Z-order&#xff08;Z顺序&#xff09…