首页 > 编程语言 >SecureCoding in C and C++(二)

SecureCoding in C and C++(二)

时间:2024-08-12 22:25:25浏览次数:14  
标签:变量 int 函数 C++ SecureCoding main 我们 Log

经过上期的环境搭建过后,我们将正式的学习C++系列,首先要学习的是C++的一些常用的变量
从编译和连接学起似乎也是不错的选择。
个人总结的一句话:编译其实就是对预处理语句进行处理后,然后对语句进行处理。对预处理语句,例如:#include 之类的处理方式其实就是将文件内容复制到对应的cpp文件内,随后生成obj文件,然后再经过链接(也就是link)将其处理。
好的让我们开始学习变量:

1. 变量

说起变量,学习过其他编程语言的小伙伴们其实很清楚,大致上也就那些,什么int char string float这些基本都是常见的了,但是这里我想说的是,其实他们去做什么作用我并不关心,就像一个人的身高和体重,利用整数和浮点数来表示我不是很关心,我关心的只是不同变量的大小之分。
来举个例子:

#include<iostream>
int main()
{
    int v =8;
    std::cout<<"hello world !\n" <<std::endl;
    std::cout<<v<<std::endl;
}

这里我们明显的使用了整型,也就是int

1.1整型变量 int

整型变量的作用是在一定范围内存放整数
占的大小是4个字节,是一个有符号变量,其空间大小为-20亿到+20亿
4Byte =32bit 其中的一位占做符号位,也就是实际上来说的31位表示数据位。
那么我们也可以让其始终为正,来表示全32位都是有效的数据的变量,这就引来了无符号整型:
unsigned int
这样看来,其32位全部可以用0或1来表示,因此他的大小为20亿的两倍,大概为42亿。

1.2 字符变量char

char 占1位
其实,我起初所说的对变量真正期望你使用其来表示什么数字其实并不关心,原因就在char里
其实char是用来表示一个字符的,但是你也可以用其来表示数字。
举个例子:

#include<iostream>
int main()
{
    char a ='A';
    char b =65;
    std::cout<<a<<std::endl;
    std::cout<<b<<std::endl;
    
}

在我们进行build之后,运行结果为:
在这里插入图片描述
那么为什么呢? 为什么char b输入的是数字,但是实际上出现的是A呢?这其实就是Ascll
说白了其实就是一些约定,所以在C++使用的过程中,我们没必要去让自己遵循一些既定俗成的约定,没啥必要,我们要发挥自己的主观能动性!
变量与变量之间唯一的区别,个人认为是在变量被创建的时候分配了多少空间
剩余的变量就不一一介绍了
short2字节
long 4 字节
long long 8字节

1.3 float double

float为4字节
double为8字节
float变量在定义的时候要在其后面加入f

    float c=5.5f;
    double d=5.2;

1.4 Bool

bool,学过js肯定都知道吧,其实就是boolean 用来返回true或者false
在这里插入图片描述
但是当我们使用cout去打印bool变量的时候
会出现的是:
在这里插入图片描述
是因为计算机并不知道什么是真的,什么是假的,他只知道0是0
因此bool里除了0表示假的,其余都是真的。
但是bool也跟char一样是占一个字节的。
有人可能就问了:
为什么不是一个bit呢?既然只表示0或者1,1位不就够了吗?
确实是这样的。1位确实就够了,但是当我们的CPU去寻找这个bool值的时候,如果这个bool为1位的话,我们没有把那去寻址只有1一个bit位的内容,ds和【】都需要2个字节。所以我们寻址的时候必须得按字节来寻址,经过cs的左移之类的。
因此我们不能去创建只有1bit的bool,我们要寻址。但是我们可以在一个bool内创建8个bool值,1bit1个。这都是后话了

我们也可以用sizeof去查看对应的变量类型占了多少个字节。
如下:
在这里插入图片描述
运行结果如下:
在这里插入图片描述
int占4字节、double 8字节、float4字节、char1字节 long 8字节 long long 8字节 short2 字节

1.5 总结

常用的变量也就是以上的内容了,剩余其他变量可以用他们进行组合,当然我们也可以让其变成指针型的变量或者引用。这都是后话了。

2.函数

什么是函数?其实就是一个被我们设计去实现某种功能的代码块罢了。也解决了实现某个功能,我们不必的去重复写的麻烦。函数有几个值得注意的地方:

  1. 函数的返回值
  2. 函数所要传入的参数
    例子:
int Multiply(int a,int b)
{
    return a*b;
}

这个函数的作用是返回两个整数的乘积,所以其返回结果是 int 也就是 写在了函数一开始的地方,随后传入了两个整型int变量。
那么如何去调用这个函数呢?
我们在main函数中定义两个整型的变量即可:
在这里插入图片描述
函数其实还是比较简单的哈(

3. 头文件

头文件说白了就是一个用来声明函数的文件。
举例:
为了输出服务器的日志方便,我们需要一个Log函数去打印服务器的日志到终端里:
这里我们编写一个十分简单的函数Log,

void Log(const char * message)
{
    std::cout<<message<<std::endl;
}
void InitLog()
{
    Log("Initializing Log");
}

我们将log放置在一个叫Log.h的头文件内;
在我们的main函数所在的文件内未进行#include “Log.h”的文件声明之前,我们是无法在main函数中使用的

#include<iostream>
int Multiply(int a,int b)
{
    return a*b;
}
void MultiplyAndLog(int a, int b)
{
    std::cout<<Multiply(a, b)<<std::endl;
}


int main()
{
    int a=1;
    int b=2;
    std::cout<<Multiply(a, b)<<std::endl;
    MultiplyAndLog(2, 3);
    Log("hello");
    InitLog();
}

我们将main函数所在文件main.c中尝试使用Log.h中有的函数,并且在Xcode中进行编译:
在这里插入图片描述
会报错:无法匹配到Log的函数调用和不清楚的InitLog定义。
这是因为我们没有对其进行声明;也就是没有调用Log.h的缘故
在我们加入之后,程序正常运行了。
当然,我们也可以这样:
在一个其他的文件内,编写log函数:
在这里插入图片描述
这里我们在一个单独的cpp文件中编写了log和InitLog函数,我们可以在main函数所在的cpp里进行声明:
在这里插入图片描述
可以看到成功运行了:
在这里插入图片描述
这里进行声明的时候,只需要将对应函数的 返回值,名称,传入参数输入即可
如这里的:

void Log(const char * message);

当然我们也可以在头文件中进行声明,随后在main.cpp中调用头函数
在这里插入图片描述
这样也是可以的。
但是有个问题需要深思:

3.1 头文件中 #pragma once的运用

我们知道cpp文件在进行编译的时候,其本质就是将预处理,也就是带“#”后面的内容进行复制粘贴行为,因此假设我们在处理一个含有结构体的头文件的时候,我们一不小心在main函数所在的cpp文件内进行了两次重复的调用,那么我们就生成了两个一模一样的结构体:
例如:
在这里插入图片描述
然后我们在main中引用了两次Log.h
在这里插入图片描述
这个时候就会报错。
错误为:
Redefinition of 'People'
这是因为预处理的特征是复制粘贴,导致了两个相同的结构体出现了。
那么我们需要在头文件中加入#pragma once 进行处理,这个预处理语句的作用就是防止头文件的内容被重复的调用,导致出现 Redefinition 的错误。

3.2 ifndef的运用

除了3.1中所提到的#pragma once
我们还可以利用预处理的特性来解决这个问题:
即在头文件中加入:
#ifndef _LOG_H
#define _LOG_H
#enif
来进行判断,其原理是:在编译器进行预处理的操作时,首先要将预处理的文件复制,然后粘贴到调用中:
这里进行复制时,会出现以下情况:
ifndef _LOG_H
define _LOG_H
你的代码:
endif
会首先执行:ifndef判断 判断是否定义了_LOG_H
如果没有定义,则进行定义 随后执行你的函数
如果定义了,那么直接进行endif。
来尝试下,例子如下:
在这里插入图片描述
这种情况下,即使在main中调用了两次的Log.h也不会出现错误:
在这里插入图片描述
还有一点需要注意的是 #include “”引号表示的是 相对于当前文件的情况,属于相对路径
而#include<>使用的是绝对路径
文件头也很简单呀~

4. 使用IDE来调试

4.1 使用VisualStudio

  1. 设置断点:
    直接按F9就可以设置断点
    在这里插入图片描述
    再点击一次红点就可以取消断点
    随后确保在debug模式下 点击第二个红色箭头所指的地方进行debug调试

在这里插入图片描述
点击后,界面会发生变化
在这里插入图片描述
这里有三个调试步骤

  • 逐语句 (step in) 进入当前函数,这里是进入Log函数 然后从Log函数里进行其他的调试 快捷键 F11
    在这里插入图片描述
  • 逐过程(step over) 跳过当前函数执行下一行,即不进入Log函数,直接调试下一行 快捷键F10
    在这里插入图片描述
  • 跳出(step out) 跳出当前函数回到断点处,退出调试模式. 快捷键shift + f10
    在这里插入图片描述
  1. 读内存
    我们按照第一部分的debug 进行
    当我们按F11 进入到 Log函数中
    在这里插入图片描述
    可以看到 message的内容为"Hello world !"
    好像有些许简单了,让我们来加入一些其他的变量来增大难度:
#include<iostream>
#include "Log.h"
int main()
{	
	int a = 8;
	const char* string = "Hello";
	for (int i = 0; i < 5; i++)
	{
		const char c = string[i];
		std::cout << c << std::endl;
	}
  	Log("Hello World!");
	std::cin.get();

}

新的代码后,我们不加断点直接进行运行来查看一下结果:
在这里插入图片描述

然后我们把断点设在int a处;
在这里插入图片描述
在未步入之前 a是多少呢?
在这里插入图片描述
为什么? 我设定的a明明是12403 啊
因为我们还没有进行执行 int a的指令,换句话说 这里只是cpu通过 cs:ip 读取到了这句 类似于 mov ax,8
但是还没有进行执行,ip还没有+3 的情况下.因此这里的值是未初始化的 a的值.所以是正常的.
一直步入到第6行的时候,再进行步入的时候才发现右下角中的string出现了值
在这里插入图片描述
这里要介绍下调试的窗口:

  • 自动窗口
    在这里插入图片描述

这里面可以看到IDE自动给你生成的名称以及其对应的值和类型

  • 局部变量
    在这里插入图片描述
    这里看到的是程序运行过程中所用到的局部变量的值
  • 监视1
    在这里插入图片描述

可以通过箭头所指方向进行添加所要监视的变量.

  • 内存读取
    在这里插入图片描述
    使用后可以查看内存指定位置的值
    在这里插入图片描述
    这里查看了Hello world
    也可以使用 &变量名来查看变量
    &a来试一下
    在这里插入图片描述
    这里可以看到就是a的值 08
    如果看到的是CC
    那么说明这段部分还没有被创造栈,也就是这个变量没有被初始化.

4.2 使用Xcode

这里我们使用Xcode来进行调试,首先把程序补全:

	#include<iostream>
#include "Log.h"

int main()
{   int a = 8;
    a++;
    const char * string ="Hello";
    for (int i =0 ;i <5 ;i++)
    {
        std::cout<<string[i]<<std::endl;
    }
    Log("Hello world!");
    std::cin.get();
}

然后我们来看下Xcode的debug功能:
在这里插入图片描述

  • step in 也就是 F7 步入 就是进入函数内部进行debug
  • step over F6跳过此函数 执行下一行
  • step out F8 跳出
  • create breakpoint currently 是comman +\ 是精确的创建断点
    相较于Vs来说 Xcode的debug模式是全一些的 甚至可以通过 comman + Y来激活/不激活某处断点。
    这里还是老样子在 int a处设下断点
    在这里插入图片描述
    这就是Xcode的调试界面了,与Vs不同的是,得手动点击播放按钮后才可以开始调试。
    我们分两个区域来查看
    第一个区域是左侧栏
    在这里插入图片描述
    这里显示的元素有 程序运行进程的pid 占用内存 以及 程序运行的线程 和当前调试的程序名:main
    点击main的下面会出现当前程序运行的情况
    在这里插入图片描述
    由于博主的MacBook是M系列的,因此跟Intel的指令读起来还是有些许不同的,不会存在常用的ax,bx等通用寄存器。这里就不必多言,关注的是debug的过程和思想。
    让我们来看另一个区域,就是debug区域的下方:
    在这里插入图片描述
    这个窗口跟VS的类似,有auto 、local 和all
    auto就是自动 local是本地的变量 all是都显示出来
    与VS不同的是 这里的监视器写在了左边
    在这里插入图片描述
    对于查看内存来说,我们只需右键想看到的变量的名称来进行查看内存
    这里查看的是string

在这里插入图片描述
结果如图:
在这里插入图片描述
可以看到,内容就展现在了右边。

看起来还是Xcode的debug展现的功能比较全一些,这也为我后期对VS的优化做了铺垫。

5. 常见的语法

5.1 if else语句

先来看一下简单的if else的示例
在这里插入图片描述
这里将Compar之后的结果 存储在bool变量里,可以直接方便的去套用if语句
我们更应该关心这个if 语句在实际的运行过程当中发生了什么
让我们来运行一下:
在这里插入图片描述
当然,显示了hello world !
当我们把 x 改为6
什么也没发生。这是当然啦
让我们在 int x的地方下断点
在这里插入图片描述
当我们一步一步的去步入的时候,ComparisonResult为false;
因此就退出了。
让我们去VS里面看一下汇编是怎么写的(别问为什么不用Xcode看,问就是没学过arm的)
在这里插入图片描述
首先使用mov 将 6放入 寄存器里
随后开始判断ComparisonResult的值
在这里插入图片描述
也是将其放入寄存器里,随后使用jne比较(jump not equal) 这里看出 x并不是为5 实际上是6 则跳转到了 07FF742BE6825h那边
最后因为不相等,就跳转到了 cin.get()
在这里插入图片描述
再来看一下if语句对应的汇编吧
在这里插入图片描述
这里的语句实际上是,将ComparisonResult的内容进行比较,然后执行je(jump equal) 相等就跳转 实际上比较的是是否与false相等:
这里因为不是false,所以就不跳转,转身去执行下面的log函数
在这里插入图片描述
实际上我们可以进一步的简化代码,使其更加清晰。
在这里可以这样进行简化
在这里插入图片描述
众所周知,只要是bool不为0
那么都为真,这里直接写成x就很合理了。
对于指针我们也是可以这样写的。
例如:
在这里插入图片描述
这样对于指针来说,只要不是空指针或者0,就可以成功的调用Log()

在这里插入图片描述
可以看到已经成功打印了。
那么如果我们将其设为nullptr,则不会打印。
在这里插入图片描述

在这里插入图片描述
就是这样!
好了由于时间的关系,这篇文章最终以一万字结束。期待下一篇,下一篇中,我将继续带来关于C++相关的精华部分,指针、引用、类与结构体等待。
喜欢的话点个关注,求个赞赞和收藏~~

标签:变量,int,函数,C++,SecureCoding,main,我们,Log
From: https://blog.csdn.net/baidu_33751906/article/details/141130804

相关文章

  • C ++ 也可以搭建Web?高性能的 C++ Web 开发框架 CPPCMS + MySQL 实现快速入门案例
    什么是CPPCMS?CppCMS是一个高性能的C++Web开发框架,专为构建快速、动态的网页应用而设计,特别适合高并发和低延迟的场景。其设计理念类似于Python的Django或RubyonRails,但针对C++提供了更细粒度的控制和更高效的性能。主要特点和优点1.高性能与并发处理​Cp......
  • C++类和对象(类的定义)
    目录一·类的定义1.1类的定义格式1.2访问限定符(public、private、protected)1.3类域一·类的定义1.1类的定义格式定义类的关键字是class,类定义结束时,后面的分号不能省略。类体中的内容称为类的成员:①类中的变量称为类的属性或成员变量;②类中的函数称为类的方......
  • 【C++面向对象】泛型编程(模板) 新手小白都能懂!
    目录泛型编程是什么?模板和泛型编程的关系?函数模板定义调用类模板定义调用总结/小注泛型编程是什么?顾名思义,“泛型”即“广泛的类型”,即不拘泥于一种特定类型的编程方法。在泛型编程时,我们通常使用一个或多个类型占位符来表示一种或多种类型,这些类型对于模板而言......
  • c++两人组合账号
    周小灵彤:周小灵彤(六年级)-CSDN博客王小灵弘:wanghongyv-CSDN博客这是联合账号,因为我们俩懒太勤快了(嘻嘻)                                                     ......
  • NDT算法详解与C++实现
    点云匹配在感知环节是一个很重要的信息获取手段,而其中的算法也有几个比较经典了,例如ICP(IterativeClosestPoint,迭代最近点)算法,而本文决定记录学习的是NDT算法,也就是NormalDistributionTransform,正态分布变换算法。什么是正态分布变换算法呢,简言之,就是把空间中的点云进行整......
  • C++基础知识:友元是什么,友元(全局函数做友元)
    在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术友元的目的就是让一个函数或者类访问另一个类中私有成员通俗一点说就是友元,就好比你的闺蜜,你可以让他了解你身上的特有的东西友元的关键字为friend全局函数做友元代码测试:#include......
  • 保姆级下载C++17
    一、下载MSYS2MSYS2是一款跨平台编译套件,方便的帮助我们安装环境,下面是MSYS的官网:MSYS2https://www.msys2.org/下载完成后安装,安装的路径千万不要有中文,把你安装的路径复制一下,以便后续步骤。二、安装GCC安装完成后打开刚才复制的路径,点开msys2.exe,输入以下命令来安装GCCp......
  • windows C++-C++/WinRT 中创建组件和事件(下)
    跨ABI的简单信号如果无需连同事件传递任何形参或实参,则可以定义自己的简单Windows运行时委托类型。以下示例展示Thermometer运行时类的更简易版本。它声明名为SignalDelegate的委托类型,然后使用该类型来引发信号类型事件,而不是具有参数的事件。//ThermometerWRC.i......
  • windows C++-使用 C++/WinRT 的集合
    在内部,Windows运行时集合具有大量复杂的移动部件。但要将集合对象传递到Windows运行时函数,或要实现自己的集合属性和集合类型时,C++/WinRT中有函数和基类可以提供支持。这些功能消除复杂性,并节省大量时间和精力上的开销。IVector是由元素的任意随机访问集合实现的Windo......
  • 2024华为OD笔试机试 - 模拟目录管理功能 (python/c++/java D卷C卷真题算法)
    华为OD机试(C卷+D卷)2024真题目录(Java&c++&python)题目描述实现一个模拟目录管理功能的软件,输入一个命令序列,输出最后一条命令运行结果。支持命令:创建目录命令:mkdir目录名称,如mkdirabc为在当前目录创建abc目录,如果已存在同名目录则不执行任何操作。此命令无输出......