Secure Coding in C and C ++ (三)关于语法与指针的感悟

news2024/9/21 2:32:24

上一篇文章中,我们讲了一些关于编译和链接以及基础的C++知识。详情请见文章
SecureCoding in C and C++(二)
本篇文章将从循环开始写起

1 循环

1. 1 for

先来个简单的例子:
打印hello world 五次:
在这里插入图片描述
很简单的吧
for循环的语法是这样的:

for (变量名,对变量的限制,变量自身的变化)

实际上,for循环先对第一个参数进行运行,比如常用的int i随后在for循环结束后进行评估,来确定第二个参数的bool值,随后执行最后的自身变化。
当然,只要你的变量在一开始声明,也不必要在for循环的第一个参数内声明:
如:
在这里插入图片描述
当然,我们也可以对其第二个参数进行提前的声明:
在这里插入图片描述
只不过这个时候对循环是否可以进行下去的判断逻辑就到了循环内部里面了。
所以还是跟我的上一篇文章一样,C++很自由,不要被传统的约定俗成的所限制,要大胆发挥自己的想象力!

1.2 while循环

while循环的语法是这样的:

while (bool)

只要括号里面的条件一直被满足,则一直进行下去。在这里插入图片描述
那么对于两种循环来说,如何去选择呢?
假设你正在做一款游戏,这款游戏有一个功能是一直需要运行的,如玩家的框体,一直处于running状态,这个时候就选择while
当我们在处理定长数组时,就选择使用for循环,因为for循环可以对循环的次数和情况做出更加精细化的处理。

1.3 do while(不是很常用)

语法:

 do {
        <#statements#>
    } while (<#condition#>);

例如:
在这里插入图片描述

2.控制流语句

2.1 continue

这里使用for循环来掩饰 continue语句
continue将跳到for循环的下一迭代。
示例:
在这里插入图片描述
如果i可以%2=0,那么就跳过这个循环,进行下一次循环。
因为我们的i是从1开始的,所以结果应该会打印 1,3,5
因为2,4可以%2=0,所以这里就进行了下一次循环。
结果如图:
在这里插入图片描述
进一步去理解一下:
我们改写这样:
在这里插入图片描述
结果显而易见了。不大于2的时候会执行Log打印。
在这里插入图片描述

2.2 break

就跟单词意思一样,休息一下
这里我们使用第一个例子:
在这里插入图片描述
当我们的i为2的时候,if判断为true 执行了break ,让循环休息了
所以这里指打印1,一点也不奇怪。
在这里插入图片描述
ok,让我们稍微break一下

2.3 return

对于控制流语句而言,return可以在任何语句中使用,可以在函数中用来返回值/或者其他东西
这里就不再详细的讲解了。
跳过,把篇幅给比较重要的下一个章节 -指针

3. 指针

3.1 指针的概念

这里讲解的指针式原始指针,对于智能指针来说,后续会讲解。
指针其实很简单,一句话吧:指针是一个整数,是一种储存内存地址的数字,就是这样。指针就是地址。就是这样
指针的类型并不会改变指针的实质,不管是int还是其他的,实质就是:指针只是一个内存地址。
例如:

void*ptr =NULL

在这里定义一个空指针。
NULL的含义其实就是

#define NULL 0

是的,这里就是让定义的指针ptr 为空指针,也就是内存地址为0.
也可以这样写:

void * ptr = nullptr;

这里定义了空指针。
定义格式:

type * name 

让我们来做一个简单的练习:
在一个变量前面加入一个 “&“符号,表示去取这个变量所在内存的地址:
在这里插入图片描述
这里使用&去取整形变量var的地址值,给指针ptr
下断点debug我们可以得知:
在这里插入图片描述
在这里插入图片描述

ptr内存储的就是变量var所在的地址。
在这里插入图片描述
好的,那么我们清楚了,如何去取某一变量的值到指针中,是“&”
那么怎么去逆向的使用指针呢?如何利用指针去修改他呢?
使用“
是的,就是一对正反操作:
这里我们来试验一下:
在这里插入图片描述
报错咯,因为我们在定义指针ptr的时候,是按照空指针去定义的。所以这里我们去写数值的时候,得把指针类型改为int才行。
这里做如下修改:
在这里插入图片描述
运行结果理所当然是12
让我们去进一步的查看这个过程,在var和
ptr处下断点:
一开始,var的内存地址和其值如图所示
在这里插入图片描述
随后执行到var =8时,
内存里变为
在这里插入图片描述
这里还可以去印证一件事情:
ptr的内存和*ptr的内存:
ptr的内存时指针变量所在的地址,&ptr是指针变量所指向变量的值。
即:这里的ptr指向的是变量var
理所应当的ptr的值是var变量的地址
也即是16FDFF2BC
去看一下ptr在内存里的情况:
在这里插入图片描述
在这里插入图片描述
可以看到这里是高地址存放高位,连起来就是 016FDFF2BC 就是var的地址。
而ptr本身的地址是前面的: 16FDFF2B0
这样就很明显了吧
让我们做一个测试去印证下:
我们知道指针的本质其实是地址,让我们再来创造一个指针add
用来显示ptr指针本身的地址:
在这里插入图片描述

这里对变量做出解释:
首先我们定义的整数变量var 是8
这里使用了ptr指向了var。
ptr指针的值,也就是var所在的内存地址。
随后我们使用了指针add去指向ptr,这里由于ptr本身就是一个指针变量,这里就相当于赋值了,将ptr内的var的地址的值,赋值给了指针变量add
接着使用。ptr_add变量,去指向了指针ptr所在的地址,(使用了&),这样ptr_add的值,就是指针变量ptr的值了。

在这里插入图片描述
与上面debug的结果十分吻合。

3.2 指针的用法

首先我们来看一下接下来的操作,我们向内存去申请一部分空间,这里常用的是用char指针去申请堆内存,因为char是一个字节:
示例如下:

#include<iostream>
#define LOG(x) std::cout<< x <<std::endl;
#include "Log.h"
int main()
{
    char * buffer = new char[8];//用char类型的指针去使用8字节的空间,并将空间的开始地址返回给buffer指针。
    memset(buffer, 3, 8);//这里的buffer其实存储的就是区域的起始地址。
    std::cin.get();
}

在我们运行之后,打个断点,观察其内存空间

在这里插入图片描述
这里查看的是buffer指针的值,也就是所申请的内存空间的开始的地址
在我们没有步入line 7的时候,可以看到这部分区域为:
在这里插入图片描述
很显然没有执行memset,让我们继续步入:
在这里插入图片描述
可以看到内存区域被memset设为了8
也就是8个字节的空间为3;
好,跟3.1讲的一样,我们来试一下双重指针。

#include<iostream>
#define LOG(x) std::cout<< x <<std::endl;
#include "Log.h"
int main()
{
    char * buffer = new char[8];//用char类型的指针去使用8字节的空间,并将空间的开始地址返回给buffer指针。
    memset(buffer, 3, 8);//这里的buffer其实存储的就是区域的起始地址。
    char ** ptr = &buffer;
    
    delete[] buffer;
    std::cin.get();
}

这里我们创建了一个指针ptr去指向buffer指针的地址,因此,ptr内的值,就是buffer指针所在的内存的值。
而buffer指针的内容是所开辟堆空间的起始的地址。
让我们来debug一下
先来看buffer指针的内容和其所在内存:
在这里插入图片描述
这是buffer指针的内容,左边是buffer指针所在的内存
紧接着我们来查看一下ptr指针的内容:
在这里插入图片描述
是的,可以看到ptr指针的内容为016FDFF2B0 存放的就是buffer指针的位置!
很简单哦
这就是指针

最后强调一句:
指针的实质就是地址!!!!
不要想太多

4. 引用

引用说白了,跟指针其实很相似,但是引用仅仅只是引用而已,并没有创造新的变量
变量类型& name

这就是引用
来看一个例子:

#include<iostream>
#define LOG(x) std::cout<< x <<std::endl;
#include "Log.h"
int main()
{
    int a =5;
    int& ref = a;
    LOG(ref);
    ref =10;
    LOG(a)
}

在这里插入图片描述
可以看到,我们通过修改引用ref 去修改了a的值。
让我们来搞一些复杂的东西:
在这里插入图片描述
可以看到Increment函数的作用是让value自增。
那么结果是多少呢?
6?
不,结果仍然是5
为何呢?
Increment函数实际上是新建了变量a,然后使其自增。
类似于:

void Increment(int a )
{   int a;
    a ++;
    value ++;
}

因此这里并没有让a自增。
那么我们应该怎么办呢?
唯一可以做的就是:指针
利用指针的逆写,将修改存入到内存中:
修改如下:
···
#include
#define LOG(x) std::cout<< x <<std::endl;
#include “Log.h”
void Increment(int* value )
{
(value)++;
}
int main()
{
int a =5;
int
ptr =&a;
Increment(ptr);
LOG(a);
}
···
这里的括号是因为,在指针变量递增之前做引用,也就是先逆向引用到指针所存储内存的内容,随后自增。
当然,这一小节的标题是引用:
回想一下为什么直接传入a不行呢?其实就相当于创造了一个新的对象。
那么我们直接传入a的引用就好了

#include<iostream>
#define LOG(x) std::cout<< x <<std::endl;
#include "Log.h"
void Increment(int& value )
{
    value++;
}
int main()
{
    int a =5;
    Increment(a);
    LOG(a);
}

这里就相当于

int& value  =a ;
value++;

引用做的,指针都可以做。指针的功能室比引用强的。
注意:更改引用是可以的!
如:

int main()
{
    int a =5;
    int b =10;
    int& ref=a;
    ref=3;
    LOG(ref)
    ref=b;

    LOG(a);
    LOG(ref);
}

这样是可以的,
但是在声明时,必须立马指出引用变量是引用的谁:
如果不声明,像这样在这里插入图片描述
就会报错!
这就是引用。

5 类

5.1什么是类

我们终于到了面向对象编程了。
如果你想做一款游戏,那么游戏玩家该如何去设计呢?
玩家有坐标、速度等
我们如果不使用一种将变量聚合等技术去创造玩家,
代码belike:

    int Player0X,Player0Y;
    int Player0Speed;
    int Player1X,Player1Y;
    int Player1Speed;

难以维护。。
因此我们使用类
使用calss
在这里插入图片描述
当我创建完毕Player后,尝试去改变其内部的属性值,报错了,被修改的属性是私有的。
这是因为我们没有注意可见性。默认情况下,一个类中的变量都是私有的,只有类内的函数可以去访问这些变量。如果我们希望可以在main中去访问,那么应该加入public
如果我们想要Player去移动,应该在class内编写函数,这种在类内部的函数叫做方法:

在这里插入图片描述

5.2 类和结构体的区别:

类默认是私有的,但是结构体不是
二者的区别在于可见度上
结构体默认为public
由于C中没有类,C++为了兼容C,保留了结构体
所以写了一个define
```#define struct class ``这样就兼容了C中的struct
个人习惯在有大量变量但是没有方法的时候使用struct
反之则使用class
其次就是继承,我只希望我的结构体,是表示一些数据。

5.3 如何去写?

来写一个LOG类
去打印一些调试方便的信息。

#include<iostream>
class Log{
private:
    int m_LogLevel;
public:
    const int LogLevelError =0 ;
    const int LogLevelwarning =1;
    const int LogLevelInfo =2;
public:
    void SetLevel(int level)
    {
    m_LogLevel =level;
}
    void Warn(const char * message)
    {   if(m_LogLevel>=LogLevelwarning)
        std::cout<< "[Waring]"<<message<<std::endl;
    }
    void Error(const char* message){
        if(m_LogLevel>=LogLevelError)
        std::cout<<"[Error]"<<message<<std::endl;
        
    }
    void Info(const char * message)
    {   if(m_LogLevel>=LogLevelInfo)
        std::cout<<"[Info]"<<message<<std::endl;
    }
    
};
int main()
{
    Log log ;
    log.SetLevel(log.LogLevelwarning);
    log.Warn("Hello");
    log.Info("Hello123");
    log.SetLevel(log.LogLevelError);
    log.Info("123");
}

这个类分了不同的级别,去打印日志,将级别与数字对应,级别越高,数字越小。当然这是一个非常基本的class示例。

6. 静态

6.1 结构体与类外的静态

这里讲解的是结构体外的静态,我们先来看一下:

#include <iostream>
#define LOG std::cout<<x<<std::endl;
int s_var=10;
int main()
{
    std::cout<<s_var<<std::endl;
}

这是在main里
我在另一个文件内这样写:
static int s_var =5;
显然这里是以main中声明的为准。
这是因为static声明的变量在link的时候只会从其文件内部进行link。因此这里不会将static的值传入到main里。
如果去掉这里的static
那么只需要在main中的s_var前加入external即可
这被称为external linkage
意思就是在link的时候去文件外找符号。
被static声明后的变量将不会被编译器找到(全局下)
当然对于函数也是一样的。

6.2 结构体中的静态

static其实就是相当于隐形药水的作用。
这里我们引入一个类:

#include <iostream>
#define LOG std::cout<<x<<std::endl;
struct Entity
{
    int x,y;
    void Print(){
        std::cout<<x<<","<<y<<std::endl;

    }
};
int main()
{
    Entity e;
    Entity e1;
    e.x=2;
    e.y=3;
    e1={5,6};
    e.Print();
    e1.Print();
}

这样当我们没有为里面的变量设置static时,是可以正常运行的。
但是当我们在x,y加入static后
在这里插入图片描述
这里就找不到x,y了
这是因为x,y被私有了,也就是静态化了,这个变量只有一个实例,我将其称之为static实例,不属于类的范畴了。
当我们在Entity的命名空间内加入x与y时便可以运行。
在这里插入图片描述

在这里插入图片描述
但是结果上来看 e和e1的结果是一样的,是因为x与y被static了,这两个变量只有一个实例,而这里就像是创造了两个变量,但是不属于类,因此这里的e与e1实际上指向的是同一个内存空间。
等同于:
Entity::x=xxx;
Entity::y=xxx;
说白了就是加上 static后 ,属性被私藏了。同样的道理,将方法前面加入static也是一样的调用方法:
Entity::Print()
但是当我们将x与y变为非static
Print仍为static时。。
在这里插入图片描述
静态方法无法访问非静态变量。
可以这样来理解,在方法加入静态后,此方法不属于这个类了,也就自然的不能访问类里面的变量了。方法只属于static类,这也就自然印证了每个非静态的方法总是获得当前类的一个实例作为参数,但是静态的没有实例呀,所以就无法获得参数,自然也就无法运行。要想使其运行,其实有一个方法:给他传入一个实例就好了:
这里我将在结构体外部编写一个Print
在这里插入图片描述
这样就可以成功打印了

总结一句:static的作用是将结构体内的方法/变量 变为只有一个实例,而这个实例只可以在类似命名空间的条件下使用,不属于类的范畴了。也可以理解为加入static后与该类没有关系了。非静态的变量不可以被静态的方法访问,因为静态的方法访问的本质是使用类内的变量进行传参,但是其不属于类,就无法传参。

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

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

相关文章

echarts学习:绘制地图

前言 经过之前一段时间的磨砺&#xff0c;我具备了基本的使用echarts绘制图表的能力。但是在最近这几个月里我接连遇到了几个棘手的任务&#xff0c;这大大的提升了我的echarts水平。其中我遇到的第一个高难度任务就是使用echarts绘制如下的地图&#xff1a; 简单的分析一下&a…

批量查询全国快递单号:高效追踪物流信息

在日常生活和工作中&#xff0c;我们经常会遇到需要查询多个快递单号物流信息的情况。如果手动逐一查询&#xff0c;不仅效率低下&#xff0c;而且容易出错。为了解决这个问题&#xff0c;我们可以借助固乔科技推出的【固乔快递查询助手】软件&#xff0c;轻松实现全国快递的批…

yolov5详解(一):网络结构

1. 完整的网络结构 以下是参考b站上作者以及yolov5官方代码画出的yolov5l v6.0版本的模型结构&#xff0c;v6.0版本的模型结构是目前yolov5版本的稳定版本&#xff0c;想必以后也不会有什么改变。l,m,n,s,x只是有些层以及输出通道数变化&#xff0c;整体架构是完全一样的&…

vue 项目中 使用vxe-grid 表格中给表格的表头设置特殊的格式 , 并且给指定的列文字设置颜色

项目场景&#xff1a; 相关背景&#xff1a; vue 项目中 使用vxe-grid 表格中给表格的表头设置特殊的格式&#xff0c;并为指定的列文字设置颜色 实现方案&#xff1a; 具体实现方法及步骤&#xff1a; 一、给表格的表头设置特殊的格式 实现方式一&#xff1a; :header-row-s…

WebDeveloper靶机复现

靶机设置 设置靶机为NAT模式 靶机IP发现 nmap 192.168.112.0/24 靶机ip为192.168.112.137 目录扫描 开放80端口&#xff0c;进行目录扫描 dirb 192.168.112.137 访问浏览器 目录拼接 拼接/ipdata 发现了一个流量包 在wireshark里面查看&#xff0c;发现wordpress的账户…

python提取b站视频的音频(提供源码

如果我想开一家咖啡厅&#xff0c;那么咖啡厅的音乐可得精挑细选&#xff01;又假设我非常喜欢o叔&#xff0c;而o叔只在b站弹钢琴&#xff0c;那这时候我就得想方设法把b站的视频转为音频咯&#xff01; 一、首先打开网页版bilibili&#xff0c;按F12&#xff1a; 二、刷新页面…

Java 空值与null 形参与实参学习

Java系列文章目录 文章目录 Java系列文章目录一、前言二、学习内容&#xff1a;三、问题描述四、解决方案&#xff1a;4.1 空值与null的区别4.1.1 空值&#xff08;Empty Value&#xff09;4.1.2 Null 4.2 形参与实参区别 五、总结&#xff1a;5.1 学习总结&#xff1a; 一、前…

智慧高速路三维可视化解决方案

项目背景 随着科技的快速发展&#xff0c;智慧高速公路的建设已成为交通领域的重要趋势。国家和相关部委陆续发布多项政策指导智慧公路建设&#xff0c;逐步制定相关建设标准规范&#xff0c;协助推动公路数字化、智能化升级。 方案简介 数字孪生高速公路解决方案是一种集成…

练习实践-基础设施-文件共享-FTP服务搭建-匿名/本地用户/虚拟用户三种模式

参考来源&#xff1a; 在线书籍-linux就该这么学-第11章 安装vsftpdf服务 [rootcentos7 home]# dnf install vsftpd Extra Packages for Enterprise Linux 7 - x86_64 0. CentOS-7 - Base - mirrors.aliyun.com …

C语言中的整数和浮点数在内存中存储

在C语言中&#xff0c;整形和浮点型数据的存储方式有所不同。 对于整形数据&#xff0c;C语言使用补码表示法存储。补码表示法可以方便地进行二进制加减法运算&#xff0c;同时能够简化硬件设计。对于正整数&#xff0c;其补码与原码相同&#xff0c;即直接存储其二进制表示。对…

Spring 循环依赖解决方案

文章目录 1. 循环依赖的产生2. 循环依赖的解决模型3. 基于setter/Autowired 的循环依赖1_编写测试代码2_初始化 Cat3_初始化 Person4_ 回到 Cat 的创建流程5_小结 4. 基于构造方法的循环依赖5. 基于原型 Bean 的循环依赖6. 引人AOP的额外设计7. 总结 IOC 容器初始化bean对象的逻…

如何对open62541.h/open62541.c的UA_Client进行状态(在线/掉线)监控

文章目录 1.背景2.解决方案3.异步连接4.注意事项4.1.线程问题4.2.UA_Client_run_iterate 1.背景 目前在利用open62541.h/open62541.c编写了一个与PLC进行OPCUA通讯的上位机程序。 上位机这边会定时对PLC的某个opcua变量进行写操作。但是假如PLC离线或者说拔掉网线&#xff0c;…

【多线程-从零开始-柒】单例模式,饿汉和懒汉模式

单例模式&#xff1a;是一种设计模式 设计模式&#xff0c;类似于“棋谱”&#xff0c;就是固定套路&#xff0c;针对一些特定的场景&#xff0c;给出一些比较好的解决方法只要按照设计模式来写代码&#xff0c;就可以保证代码不会太差&#xff0c;保证代码的下限 设计模式 设…

8月8日学习笔记 python基础

1.环境 python2&#xff0c; python3 yum list installed|grep python yum -y install python3 # 最新安装3.12可以使⽤源码安装&#xff0c;教程是在第⼀个星期pdf python3 --version 3.6.8 #进⼊到python的编辑状态 python3 # 如果直接输⼊python&#xff0c;也会进⼊到pyth…

MySQL基础练习题33-有趣的电影

目录 题目 准备数据 分析数据 总结 题目 找出所有影片描述为 非 boring (不无聊) 的并且 id 为奇数 的影片。 返回结果按 rating 降序排列。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists cinema (id int, movie varchar(255),…

php根据截止时间计算剩余的时间,并且在剩余时间不足1天时仅显示小时数

//获取政策库文章public function getIndexZckList(){$fl_id = input(fl_id);if(empty(

C++:list类(迭代器类)

前言 list是链表的意思 它属于链表中的带头双向循环链表 建议先掌握数据结构中的链表 C数据结构&#xff1a;单链表-CSDN博客 C数据结构&#xff1a;双向链表&#xff08;带头循环&#xff09;_c带头双向循环链表-CSDN博客 数据结构 首先我们需要一个链表的节点 templa…

ThinkPHP5漏洞分析之代码执行

漏洞概要 本次漏洞存在于 ThinkPHP 的缓存类中。该类会将缓存数据通过序列化的方式&#xff0c;直接存储在 .php 文件中&#xff0c;攻击者通过精心构造的 payload &#xff0c;即可将 webshell 写入缓存文件。缓存文件的名字和目录均可预测出来&#xff0c;一旦缓存目录可访问…

【张】#12 enum 枚举

enum 枚举定义格式&#xff1a; enum <类型名> {<枚举常量表> }; 枚举其实就是一个整数 enum example {Aa,Bb10,Cc //给Bb赋值为10后&#xff0c;Cc的值会变成11 }; 枚举变量只能使用枚举值&#xff0c;枚举可以赋值给整型&#xff0c;整型不能赋值给枚举 #inc…