各种类型内存占用

分32位和64位

32位(内存字节)

char(1)

short(2)

int(4)

long(4)

long long(8)

float(4)

double(8)

long double(8(vs)/12(gcc))

size_t/ssize_t(4)

所有指针(4)

64位

char(1)

short(2)

int(4)

long(8)

long long(8)

double(8)

long double(8(vs)/16(gcc))

size_t/ssize_t(8)

所有指针(8)

以上所有无符号和有符号占用一样

堆和栈区别

堆和栈都是在内存上面分配空间,区别:

管理方式

栈:系统分配内存,自动回收

堆:手动分配,手动释放

内存大小

栈的内存空间小,固定受限

堆的空间大,几乎受限于整机内存大小

分配方式

栈:连续内存,向下增长

堆:零散内存,向上增长

访问速度

栈快,堆慢

生命周期

栈随函数调用/结束自动存亡

堆由代码控制生命周期

cin,get,getline区别

cin

c++的输入函数,读取输入,遇到空格或者\n就会停止比如

hello world,就只会读取到hello,末尾有\n会保留在缓冲区中。

get

无参的时候读取单个字符,包括换行空格。有参的时候读取一行,遇到\n停止,\n保留在缓冲区。

getline

读取一行,遇到\n停止,\n会读出来丢掉

getline(cin,str)

c++专属,整行读取,不限定长度,string自动扩容,遇到换行停止,\n自动丢弃。

get和getline区别

就是\n,get会不动他,getline会读取出来丢掉

坑点

cin和get/getline混用的时候比如:

char str[100];
cin>>a;
cin.get(str,100);

cin了以后缓冲区中会残留\n,get或者getline看到\n会直接停止了,那么怎么解决这个问题呢,书上是在cin了以后调用一次get把\n吃掉,我网上查还有用ignore清掉残留。

string内存坑点

string是c++的变量类型,string的结构是指针+容量+大小+内部短字符缓冲区。

sizeof测量的是string对象本身的大小,64位sizeof(str)=32字节,32位sizeof(str)=24字节,与存放的字符串长度无关如果字符串<=15个字符,会存放到对象本身,如果>15个字符,会额外在堆上开辟空间存储len+1,但不影响sizeof(str)

这里又区分c和c++

c的字符串char* p和char p[]

sizeof(p)=8,因为p是指针,不是内容,测量的指针在64位中就是8字节

char p[]="abc",数组大小=len+1,字符串末尾会有\0,len是字符个数。

c++的string操作更方便,c语言的两个字符串之间不能直接赋值,类似于数组,要循环加入,或者cstring中的使用strcpy复制或者使用strcat附加实现。而string支持直接赋值,拼接,附加。

另外size和length函数没有区别,返回的都是字符个数(去掉\0后的)

结构体坑点

c和c++的结构体差不多,但是有区别,c++的结构体可以放string类型(c++的专属)以及c++的结构体可以放成员函数,但是放了成员函数就和类没啥区别了。

枚举量坑点

枚举量enum只有赋值运算符没有算数运算符如++,但如果运算的值是有效的可以强制转换

常指针与指针常量

类型用模板T代表

指向常量的指针,用 const T *p或T const *p表示,是一个指针指向一个常量,指针的地址可以改变,但是他所指向的地址存的值不可以改变。

常指针,用 T *const p表示,表示常量指针类型,指针指向的地址不可以改变,但是指向的地址所存的值可以改变

指向常量的常指针,用 const T *const p表示,这种指针指向固定地址,并且该地址存的值也是固定不变的。

回调函数

我个人其实不喜欢用回调函数,我个人更喜欢用qt的信号槽机制,那个更容易看懂,但难免还是有要用到的地方。

常规回调函数

说白了就是将一个函数指针传入另一个函数,main里面运行的时候运行到这一步只会调用一下他就继续往下走,满足条件才会执行这个函数

#typydef void(*callback)() //定义函数指针类型,名字叫callback

//回调函数
void func(){
std::cout<<"回调函数执行完毕"<<std::endl;
}

void run(callback cb){
cb();//触发回调
}

int main(){
run(func);
}

注意传入的是函数指针,传入的函数带上()就执行了。

c++11的std::function函数回调

这个就好看多了,用function<void(int)>代替typedef void(*callback)(int)

#include<iostream>
#include<functional>//function头文件

void task(function<void(int)> cb){
cout<<"任务执行完毕"<<endl;
cb(200);
}

//普通函数
void run(int n){
cout<<"普通函数回调"<<n<<endl;
}

int main(){
//方案一
task(run);
//方案二
task([](int n){cout<<"lamda回调"<<n<<endl;});
}

这里面新增了lamda方案,方案一是和常规的一样调用。方案二相当于是把run函数省略直接写到lamda的执行部分了,task函数可以看见有一个int的传参,会自动赋值给lamda的参数位,给lamda当实参使用。

类成员函数做回调

坑点:类的普通成员函数有隐藏this指针,类型和普通的函数指针不匹配。

情况一:静态成员函数

静态成员函数没有this指针,可以和常规一样调用。

class demo{
public:
static void staticcb(int val){
cout<<"静态成员函数"<<val<<endl;
}
};


void run(function<void(int)> cb){
cb(200);
}

int main(){
run(demo::staticcb);
return 0;
}

 情况二:类的普通成员函数

用std::function+std::bind绑定对象this指针

#include<iostream>
#include<functional>
using namespace std;

class demo{
public:
void menbercb(int n){
cout<<"普通成员函数"<<n<<endl;
}
};

void run(function<void(int)> cb){
cb(200);
}

int main(){
demo d;
auto cb=bind(&demo::menbercb,&d,placeholders::_1);
run(cb);
return 0;
}

bind的第一个参数是成员函数地址,第二个参数是类实例也可以理解为对象,第三个参数是参数占位符,代表回调传入的第一个参数。

lamda介绍

//lamda格式
[捕获列表](参数)mutable->返回值{函数体}

常用捕获:

[ ] 不捕获任何变量

[&] 引用捕获所有局部变量

[=] 按值捕获所有局部变量

[var] 仅值捕获var

#define和#typedef区别

#define是给一个已有的常量取别名

格式:#define 别名 类型

#typedef是变量定义语法

格式:

typedef int myint;//给int取一个别名叫myint

typedef int* INTPTR;//指向int类型的指针
INTPTR P;//等价于int* p

typedef int ARR_INT[5];//定义一个int类型长度为5的数组类型,别名为ARR_INT
ARR_INT arr;//等价于int arr[5]
arr[0]=1;

区别:

1、typedef只能给数据类型取别名,不能给字面量,变量,数字取别名,给字面量,变量,数字取别名用#define

2、typedef是类型别名,define是单纯的文本替换,比如:

typedef int* a;
#define b int*;

a a1,a2;
b b1,b2;

前者typedef定义的a1、a2都是int*类型,后者define定义的b1、b2就不一样,等价于int *b1,b2,b1是int*,b2只是int。这点要注意。

更多推荐