TB-C/C++

1.main函数之前之后执行的代码

  • 设置栈指针
  • 初始化静态变量和全局变量(.data段内容,已初始化且不为0)
  • 赋初值(.bss段内容,未初始化的全局变量和静态变量)
  • 传参(argc,argv)
  • atexit()   在vs2010上没有效果
#include <iostream>
#include <stdio.h>
using namespace std;int f() {printf("before\n");return 0;
}void f2()
{printf("EXIT!\n");
}int a = f();int main() {atexit(exit_1);printf("main\n");return 0;
}before
main

2.结构体内存对齐

C++11引入两个关键字:

alignof:计算类型的对齐方式

alignas:指定结构体的对齐方式

// alignas 生效的情况struct Info {uint8_t a;uint16_t b;uint8_t c;
};std::cout << sizeof(Info) << std::endl;   // 6  2 + 2 + 2
std::cout << alignof(Info) << std::endl;  // 2struct alignas(4) Info2 {uint8_t a;uint16_t b;uint8_t c;
};std::cout << sizeof(Info2) << std::endl;   // 8  4 + 4
std::cout << alignof(Info2) << std::endl;  // 4struct alignas(4) Info2 {uint8_t a;uint16_t b;
};std::cout << sizeof(Info2) << std::endl;   // 4
std::cout << alignof(Info2) << std::endl;  // 4
// alignas 失效的情况struct Info {uint8_t a;uint32_t b;uint8_t c;
};std::cout << sizeof(Info) << std::endl;   // 12  4 + 4 + 4
std::cout << alignof(Info) << std::endl;  // 4struct alignas(2) Info2 {uint8_t a;uint32_t b;uint8_t c;
};std::cout << sizeof(Info2) << std::endl;   // 12  4 + 4 + 4
std::cout << alignof(Info2) << std::endl;  // 4若alignas小于自然对齐的最小单位,则被忽略。
如果想使用单字节对齐的方式,使用alignas是无效的。应该使用#pragma pack(push,1)或者使用__attribute__((packed))。#if defined(__GNUC__) || defined(__GNUG__)#define ONEBYTE_ALIGN __attribute__((packed))
#elif defined(_MSC_VER)#define ONEBYTE_ALIGN#pragma pack(push,1)
#endifstruct Info {uint8_t a;uint32_t b;uint8_t c;
} ONEBYTE_ALIGN;#if defined(__GNUC__) || defined(__GNUG__)#undef ONEBYTE_ALIGN
#elif defined(_MSC_VER)#pragma pack(pop)#undef ONEBYTE_ALIGN
#endifstd::cout << sizeof(Info) << std::endl;   // 6 1 + 4 + 1
std::cout << alignof(Info) << std::endl;  // 1
#if defined(__GNUC__) || defined(__GNUG__)#define ONEBYTE_ALIGN __attribute__((packed))
#elif defined(_MSC_VER)#define ONEBYTE_ALIGN#pragma pack(push,1)
#endif/**
* 0 1   3     6   8 9            15
* +-+---+-----+---+-+-------------+
* | |   |     |   | |             |
* |a| b |  c  | d |e|     pad     |
* | |   |     |   | |             |
* +-+---+-----+---+-+-------------+
*/
struct Info {uint16_t a : 1;uint16_t b : 2;uint16_t c : 3;uint16_t d : 2;uint16_t e : 1;uint16_t pad : 7;
} ONEBYTE_ALIGN;#if defined(__GNUC__) || defined(__GNUG__)#undef ONEBYTE_ALIGN
#elif defined(_MSC_VER)#pragma pack(pop)#undef ONEBYTE_ALIGN
#endifstd::cout << sizeof(Info) << std::endl;   // 2
std::cout << alignof(Info) << std::endl;  // 1位段是通过结构体来实现的一种以位(bit位)为单位的数据存储结构,它可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。由此可以看出,位段是一种节省空间的用法。位段的声明与结构体类似,但有两点不同:
1.位段的成员必须是整型家族的成员(char、int、unsigned int、signed int……)
2.位段的成员后面有一个冒号和一个数字注意:位段每一个成员冒号后面的数字,代表该成员在内存中占用的二进制位大小。就如这里的成员a占用2个bit位,成员d占用30个bit位。而且要记住冒号后面数字的大小是不能超过前面成员类型大小的。

3.指针和引用的区别

  • 指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是原变量的别名
  • 指针可以有多级,引用只有一级
  • 指针可以为空,引用不能为NULL且在定义时必须初始化
  • 指针在初始化后可以改变指向,而引用在初始化之后不可再改变
  • sizeof指针得到的是本指针的大小,sizeof引用得到的是引用所指向变量的大小
  • 当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,两者指向的地址相同,但不是同一个变量,在函数中改变这个变量的指向不影响实参,而引用却可以。
  • 引用本质是一个指针,同样会占4字节内存;指针是具体变量,需要占用存储空间(,具体情况还要具体分析)。
  • 引用在声明时必须初始化为另一变量,一旦出现必须为typename refname &varname形式;指针声明和定义可以分开,可以先只声明指针变量而不初始化,等用到时再指向具体变量。
  • 引用一旦初始化之后就不可以再改变(变量可以被引用为多次,但引用只能作为一个变量引用);指针变量可以重新指向别的变量。
  • 不存在指向空值的引用,必须有具体实体;但是存在指向空值的指针。
void test(int *p)
{int a=1;p=&a;cout<<p<<" "<<*p<<endl;
}int main(void)
{int *p=NULL;test(p);if(p==NULL)cout<<"指针p为NULL"<<endl;return 0;
}
//运行结果为:
//0x22ff44 1
//指针p为NULLvoid testPTR(int* p) {int a = 12;p = &a;}void testREFF(int& p) {int a = 12;p = a;}
void main()
{int a = 10;int* b = &a;testPTR(b);//改变指针指向,但是没改变指针的所指的内容cout << a << endl;// 10cout << *b << endl;// 10a = 10;testREFF(a);cout << a << endl;//12
}

指针常量(int* const p):在指针常量中,指针自身的值是一个常量,不可改变,始终指向同一个地址。在定义的同时必须初始化。

常量指针(const int *p, int const *p):在常量指针中,指针指向的内容是不可改变的,指针看起来好像指向了一个常量。

int main() {int m = 10;const int n = 20; // 必须在定义的同时初始化const int *ptr1 = &m; // 指针指向的内容不可改变int * const ptr2 = &m; // 指针不可以指向其他的地方ptr1 = &n; // 正确ptr2 = &n; // 错误,ptr2不能指向其他地方*ptr1 = 3; // 错误,ptr1不能改变指针内容*ptr2 = 4; // 正确int *ptr3 = &n; // 错误,常量地址不能初始化普通指针吗,常量地址只能赋值给常量指针const int * ptr4 = &n; // 正确,常量地址初始化常量指针int * const ptr5; // 错误,指针常量定义时必须初始化ptr5 = &m; // 错误,指针常量不能在定义后赋值const int * const ptr6 = &m; // 指向“常量”的指针常量,具有常量指针和指针常量的特点,指针内容不能改变,也不能指向其他地方,定义同时要进行初始化*ptr6 = 5; // 错误,不能改变指针内容ptr6 = &n; // 错误,不能指向其他地方const int * ptr7; // 正确ptr7 = &m; // 正确int * const ptr8 = &n;*ptr8 = 8;return 0;
}
判断下面对错,并说明理由int main()
{char * const str = "apple";* str = "orange";cout << str << endl;getchar();
}错误"apple"是字符串常量放在常量区,str指向"apple",那么str指向的是字符串常量"apple"的首地址,也就是字符a的地址,因此str指向字符a,*str就等于字符a,对*str的修改就是对字符串首字符a的修改,但"apple"是一个字符串常量,常量的值不可修改。根据字符串赋值规则,可以修改整个字符串,方法是对指向字符串的指针str进行赋值,如下:str = "orange";但依旧是错误的,在该赋值语句中,系统会在常量区一块新的空间写入字符串"orange"并返回其首地址,此时str由指向字符串常量"apple"的首地址变为指向字符串常量"orange"的首地址,str指向的地址发生了变化,但str是指针常量不能被修改,所以错误。如果想要程序编译通过,就不能将str声明为指针常量,否则str在初始化之后就无法修改。因此将const修饰符去掉,并修改字符串赋值语句,修改后程序如下:int main()
{char * str = "apple";str = "orange";cout << str << endl;getchar();
}

4.传递函数参数什么时候使用指针,什么时候使用引用

  • 需要返回函数内局部变量的内存的时候用指针。使用指针传参需要开辟内存,用完要记得释放指针,不然会内存泄漏。而返回局部变量的引用是没有意义的

  • 对栈空间大小比较敏感(比如递归)的时候使用引用。使用引用传递不需要创建临时变量,开销要更小

  • 类对象作为参数传递的时候使用引用,这是C++类对象传递的标准方式

5.堆和栈的区别

  • 申请方式不同
    • 栈由系统自动分配
    • 堆是自己申请和释放的
  • 申请大小限制不同
    • 栈顶和栈底是之前预设好的,栈是向栈底扩展,大小固定,可以通过ulimit-a 查看,由ulimit -s修改
    • 堆向高地址扩展,是不连续的内存区域,大小可以灵活调整
  • 申请效率不同
    • 栈由系统分配,速度快,不会有碎片
    • 堆由程序员分配,速度慢,会有碎片
  • 栈空间默认是4M,堆区一般是1G-4G

6.堆和栈哪个快

毫无疑问是栈快一点。

因为操作系统会在底层对栈提供支持,会分配专门的寄存器存放栈的地址,栈的入栈出栈操作也十分简单,并且有专门的指令执行,所以栈的效率比较高也比较快。

而堆的操作是由C/C++函数库提供的,在分配堆内存的时候需要一定的算法寻找合适大小的内存。并且获取堆的内容需要两次访问,第一次访问指针,第二次根据指针保存的地址访问内存,因此堆比较慢。

7.区别下面指针类型

  • int *p[10]表示指针数组,强调数组概念,是一个数组变量,数组大小为10,数组内每个元素都是指向int类型的指针变量。

  • int (*p)[10]表示数组指针,强调是指针,只有一个变量,是指针类型,不过指向的是一个int类型的数组,这个数组大小是10。

  • int *p(int)是函数声明,函数名是p,参数是int类型的,返回值是int *类型的。

  • int (*p)(int)是函数指针,强调是指针,该指针指向的函数具有int类型参数,并且返回值是int类型的。

8.new/delete与malloc/free的异同

相同点:都可以用于内存的动态申请和释放

不同点:

  • 前者是C++运算符,后者是C/C++语言标准库函数
  • new自动计算要分配的空间大小,malloc需要手工计算
  • new是类型安全的,malloc不是。例如:
int *p = new float[2]; //编译错误
int *p = (int*)malloc(2 * sizeof(double));//编译无错误
  • new调用名为operator new的标准库函数分配足够空间并调用相关对象的构造函数,delete对指针所指对象运行适当的析构函数;然后通过调用名为operator delete的标准库函数释放该对象所用内存。后者均没有相关调用
  • 后者需要库文件支持,前者不用
  • new是封装了malloc,直接free不会报错,但是这只是释放内存,而不会析构对象

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

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

相关文章

Paddle3D 2 雷达点云CenterPoint模型训练

2 Paddle3D 雷达点云CenterPoint模型训练–包含KITTI格式数据地址 2.0 数据集 百度DAIR-V2X开源路侧数据转kitti格式。 2.0.1 DAIR-V2X-I\velodyne中pcd格式的数据转为bin格式 参考源码&#xff1a;雷达点云数据.pcd格式转.bin格式 def pcd2bin():import numpy as npimport…

【EDA软件】国产嘉立创EDA使用

嘉立创&#xff08;JLCPCB&#xff09; 嘉立创&#xff08;JLCPCB&#xff09;提供的EDA专业版是一款在线的原理图设计与PCB设计工具&#xff0c;可以协助用户完成电子设计项目。以下是使用嘉立创EDA专业版的一般步骤&#xff1a; 注册与登录&#xff1a; 首先&#xff0c;你需…

实习知识整理11:确认订单并将订单的相关信息插入用户订单表和订单详情表

用户订单表&#xff1a; 订单详情表&#xff1a; 思路分析&#xff1a;首先我们需要知道当点击了确认订单按钮后&#xff0c;需要向后端传递哪些数据&#xff0c;先看用户订单表&#xff1a;ORDER_ID是不需要传的&#xff0c;这个可以在后台生成就行了&#xff1b;USER_ID是需要…

go http升级为websocket举例

当使用 Go 语言编写 WebSocket 服务器时&#xff0c;可以使用 net/http 包来处理客户端的 HTTP 请求&#xff0c;并将其升级为 WebSocket 连接。以下是一个简单的示例代码&#xff1a; go package main import ( "fmt" "log" "net/http…

docker commit之后镜像越来越大解决办法

每次对docker的镜像进行增删改之后&#xff0c;它的体积都会扩充&#xff0c;甚至大到5G左右&#xff0c;在项目中是不可以接受的。 查找变大的原因为&#xff1a; docker容器就是以便于移植和部署著称。那么在docker使用过程中&#xff0c;少不了对容器进行反复的的打包和部署…

2023年12月GESP C++七级编程题转Python真题解析

七、2023年12月GESP C(Python)七级编程题 2023年12月GESP Python最高六级&#xff0c;但C与Python同级编程题相同。本篇引用2023年12月GESPC七级编程题&#xff0c;用Python实现。 【七级编程题1】 【试题名称】&#xff1a;商品交易 时间限制&#xff1a;1.0 s 内存限制&…

大数据系列之:读取parquet文件统计数据量

大数据系列之&#xff1a;读取parquet文件统计数据量 一、Spark读取parquet文件统计数据量二、parquet-tools统计parquet文件数据量三、实际应用案例 一、Spark读取parquet文件统计数据量 首先&#xff0c;创建一个 SparkSession 对象&#xff1a; val spark SparkSession.b…

微信公众号请求获取相关权限后端代码

用微信的东西&#xff0c;总要避免到各种与微信那边的交互。 1.首先要有个接口&#xff0c;证明你是服务端&#xff08;填写服务器配置&#xff09; 接入概述 | 微信开放文档 主代码 /*** 服务器配置验证* param* return*/PassTokenGetMapping("/giveWxCheck")publi…

详解Vue3中的事件监听方式

本文主要介绍Vue3中的事件监听方式。 目录 一、v-on指令二、使用符号简写三、事件修饰符四、动态事件名五、常见的监听事件六、自定义事件 在Vue3中&#xff0c;事件监听的方式与Vue2有一些不同。 下面是Vue3中事件监听方式的详细介绍&#xff1a; 一、v-on指令 Vue3中仍然使…

​LeetCode解法汇总2487. 从链表中移除节点

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 给你一个…

vue 使用 splice 删除元素UI视图不同步怎么办?

vue 使用 splice 删除元素UI视图不同步怎么办&#xff1f; https://www.zhihu.com/question/585036077?write#:~:text%E5%9C%A8Vue%E4%B8%AD%E4%BD%BF%E7%94%A8splice,%28%29%E6%96%B9%E6%B3%95%E5%88%A0%E9%99%A4%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E5%85%83%E7%B4%A0%E6%…

(PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测

目录 I. 前言II. TCNIII. TCN-RNN/LSTM/GRU3.1 TCN-RNN3.2 TCN-LSTM3.3 TCN-GRU IV. 实验结果 I. 前言 前面已经写了一系列有关LSTM时间序列预测的文章&#xff1a; 深入理解PyTorch中LSTM的输入和输出&#xff08;从input输入到Linear输出&#xff09;PyTorch搭建LSTM实现时…

算法训练第五十二天|300. 最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

300. 最长递增子序列&#xff1a; 题目链接 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组…

Arduino驱动VL53L0X ToF激光测距传感器(距离传感器)

目录 1、传感器特性 2、硬件原理图 3、控制器和传感器连线图 4、驱动程序 VL53L0X激光测距传感器是一款基于意法半导体(STMicroelectronics&#

python实现简易的flask后端接口

先安装插件pip install flask 新建py脚本文件编码&#xff1a; # -*- coding: utf-8 -*- from flask import Flask from flask_cors import CORS # 跨域依赖&#xff0c;通过pip install flask-cors安装app Flask(__name__) cors CORS(app) # 跨域设置&#xff0c;这样设置…

WPF容器的背景对鼠标事件的影响

背景&#xff1a;在实现鼠标拖动窗口的过程中发现对父容器设置了鼠标拖动窗口的事件MouseLeftButtonDown private void DragWindow(object sender, MouseButtonEventArgs e) {if (e.LeftButton MouseButtonState.Pressed)DragMove(); } 问题&#xff1a;非常困惑的是&#x…

C# windows服务程序开机自启动exe程序

我们使用传统的Process.Start(".exe")启动进程会遇到无法打开UI界面的问题&#xff0c;尤其是我们需要进行开启自启动程序设置时出现诸多问题&#xff0c;于是我们就想到采用windows服务开机自启动来创建启动一个新的exe程序&#xff0c;并且是显式运行。 首先是打开…

软件测试入门(知识汇总)

1、黑盒测试、白盒测试、灰盒测试 1.1 黑盒测试 黑盒测试又叫功能测试、数据驱动测试 或 基于需求规格说明书的功能测试。该类测试注重于测试软件的功能性需求。 采用这种测试方法&#xff0c;测试工程师把测试对象看作一个黑盒子&#xff0c;完全不考虑程序内部的逻辑结构和…

clickhouseSQL日期相关

1. 毫秒级时间戳转日期/小时 --13位时间戳转具体时间 toDateTime(report_time / 1000) as _c00 -- 获取时间戳对应的时间点整点(结果&#xff1a;%Y-%m-%d %H:00:00.0) eg&#xff1a;2022-09-28 23:00:00.0 toStartOfHour(toDateTime(report_time / 1000)) AS _10-- 获取时间…

Rust使用gRPC

需要先安装protoc&#xff08;Protocol Buffers Compiler&#xff09;&#xff0c;可据此Protobuf Compiler Installation下载 第一步&#xff1a;创建项目 创建两个新的Rust项目&#xff0c;分别作为服务端与客户端&#xff1a; cargo new rust_grpc_servercargo new rust_grp…