C++ primer 第12章 動態內存

文章目錄

  • 前言
  • 動態內存與智能指針
    • shared_ptr類
      • shared_ptr和unique_ptr都支持的操作
      • shared_ptr獨有的操作
      • make_shared 函數
      • shared_ptr的拷貝和賦值
      • shared_ptr自動銷毀所管理的對象
      • shared_ptr還會自動釋放相關聯的內存
      • 程序使用動態內存出于以下原因
    • 直接管理內存
      • 使用new動態分配和初始化對象
      • 動態分配的const對象
      • 內存耗盡 定位new
      • 釋放動態內存
      • 使用new和delete管理動態內存存在三個常見問題
      • delete之后重置指針值
      • 智能指針和new對比
    • shared_ptr和new結合使用
      • 定義和改變shared_ptr的其他方法
      • 不要混合使用智能指針和普通指針
      • 不要使用get初始化另一個智能指針或為智能指針賦值
    • 智能指針和異常
      • 使用自己定義的釋放操作
      • 使用智能指針基本規范
    • unique_ptr
      • unique_ptr特有的操作
      • release返回指針
      • 傳遞unique_ptr參數和返回unique_ptr
      • 向unique_ptr傳遞刪除器
    • weak_ptr
  • 動態數組
    • new和數組
      • 釋放動態數組
      • 智能指針和動態數組
      • 指向數組的unique_ptr
      • 連接兩個字符串
    • allocator類
      • 標準庫allocator類及其算法
      • allocator分配未構造的內存
      • allocator算法

前言

靜態內存用來保存局部static對象、類static數據成員以及定義在任何函數之外的變量。棧內存用來保存定義在函數內的非static對象。分配在靜態或棧內存中的對象由編譯器自動創建和銷毀。對于棧對象,僅在其定義的程序塊運行時才存在;static對象在使用之前分配,在程序結束時銷毀。

除了靜態內存和棧內存,每個程序還擁有一個內存池。這部分內存被稱為自由空間或堆。程序用堆來存儲動態分配的對象——即,那些在程序運行時分配的對象。動態對象的生存期由程序來控制,也就是說,當動態對象不再使用時,我們的代碼必須顯式地銷毀它們。

動態內存與智能指針

動態內存的管理是通過一對運算符來完成的:new,在動態內存中為對象分配空間并返回一個指向該對象的指針;delete,接受一個動態對象的指針,銷毀該對象,并釋放與之關聯的內存。

動態內存的使用很容易出現問題,因為確保在正確的時間釋放內存是及其困難的。忘記釋放內存,會產生內存泄漏;在尚有指針引用內存的情況下我們就釋放了內存,會產生引用非法內存的指針。

新標準庫提供了兩種智能指針類型來管理動態對象,它可以自動釋放所指向的對象。shared_ptr允許多個指針指向同一個對象;unique_ptr則“獨占”所指向的對象。weak_ptr是一種弱引用,指向shared_ptr所管理的對象,這三種類型都定義在memory頭文件中。

shared_ptr類

當我們創建一個智能指針時,必須提供額外的信息——指針可以指向的類型。與vector一樣,我們在尖括號內給出類型,之后是所定義的這種智能指針的名字:

shared_ptr<string>p1;		shared_ptr,可以指向string
shared_ptr<list<int>>p2;	shared_ptr,可以指向list<int>

默認初始化的智能指針中保存著一個空指針。

shared_ptr和unique_ptr都支持的操作

在這里插入圖片描述

shared_ptr獨有的操作

在這里插入圖片描述

make_shared 函數

p3指向一個值為42的int的智能指針
shared_ptr<int>p3 = make_shared<int>(42);p4指向一個值為"88888"的string的智能指針
shared_ptr<string>p4 = make_shared<string>(5,'8');p5指向一個值為0的int的智能指針(int的初始化值為0)
shared_ptr<int>p5 = make_shared<int>();

shared_ptr的拷貝和賦值

當進行拷貝或賦值操作時,每個shared_ptr都會記錄有多少個其他shared_ptr指向相同的對象。離開作用域計數器會遞減,賦值操作,原指針計算器會遞減,賦值指向的指針計數器會遞增。示例代碼如下:

	auto p = make_shared<int>(42);//p指向的對象只有p一個引用者auto r = make_shared<int>(4);//p指向的對象只有p一個引用者auto r2(r);//p指向的對象只有p一個引用者cout << "p引用數量:" << p.use_count()<<endl;{auto q(p);//p和q指向相同對象,此對象有兩個引用者cout << "p引用數量:" << p.use_count() << endl;cout << "p指向:" << *p << endl;*q = 10;cout << "p指向:" << *p << endl;}cout << "在賦值之前,p引用數量:" << p.use_count() << endl;cout << "在賦值之前,原r引用數量:" << r2.use_count() << endl;r = p;cout << "在賦值之后,原r引用數量:" << r2.use_count() << endl;cout << "在賦值之后,p引用數量:" << p.use_count() << endl;cout << "p指向:" << *p << endl;

輸出結果:

p引用數量:1
p引用數量:2
p指向:42
p指向:10
在賦值之前,p引用數量:1
在賦值之前,原r引用數量:2
在賦值之后,原r引用數量:1
在賦值之后,p引用數量:2
p指向:10

shared_ptr自動銷毀所管理的對象

當指向一個對象的最后一個shared_ptr被銷毀時,shared_ptr類會通過析構函數自動銷毀此對象。shared_ptr的析構函數會遞減它所指向的對象的引用計數,如果引用計數為0,shared_ptr的析構函數就會銷毀對象,并釋放它占用的內存。

shared_ptr還會自動釋放相關聯的內存

當動態對象不再被使用時,shared_ptr會自動釋放動態對象,這一特性使得動態內存的使用變得非常容易。
假設現有一個Foo類,示例代碼:

//factory返回一個shared_ptr,指向一個動態分配的對象
shared_ptr<Foo> factory(T arg){return make_shared<Foo>(arg);
}void use_factory(T arg){shared_ptr<Foo>p=factory(arg);//使用p
}//p離開作用域,它指向的內存會被自動釋放//但如果有其他shared_ptr也指向這塊內存,則它就不會被釋放:
shared_ptr<Foo> use_factory2(T arg){shared_ptr<Foo>p=factory(arg);//使用preturn p;//當我們返回p時,引用計數進行了遞增
}//p離開作用域,它指向的內存不會被釋放

因此,如果將shared_ptr存放于一個容器中,而后不再需要全部元素而只使用其中一部分,要記得用erase刪除不再需要的那些元素。

程序使用動態內存出于以下原因

  • 程序不知道自己需要使用多少對象 例如:容器類
  • 程序不知道所需對象的準確類型
  • 程序需要在多個對象間共享數據

我們定義一個類,它使用動態內存是為了讓多個對象共享相同的底層數據。我們定義一個名為Blob的類,保存一組元素。我們希望Blob對象的不同拷貝之間共享相同的元素,即,當我們拷貝一個Blob時,原Blob對象及其拷貝應該引用相同的底層元素。

Blob類的定義如下:

#ifndef __BLOB__
#define __BLOB__#include<string>
# include<iostream>
# include<vector>
# include<memory>
using namespace std;class Blob {
public:typedef vector<string>::size_type size_type;Blob();Blob(initializer_list<string>il);size_type size()const { return data->size(); }bool empty()const{return data->empty();}//添加和刪除元素void push_back(const string &t) { data->push_back(t); }void pop_back();//元素訪問string & front();string & back();//獲取元素shared_ptr<vector<string>> getData() { return data; }//修改元素void changeData(size_type i,string str) {(*data)[i] = str;}private:shared_ptr<vector<string>>data;void check(size_type i,const string &msg)const;
};Blob::Blob() :data(make_shared<vector<string>>()){}
Blob::Blob(initializer_list<string>il) : data(make_shared<vector<string>>(il)) {}
void Blob::check(size_type i, const string &msg)const {if (i >= data->size())throw out_of_range(msg);
}
string& Blob::front() {check(0,"front on empty Blob");return data->front();
}
string& Blob::back() {check(0, "back on empty Blob");return data->back();
}
void Blob::pop_back() {check(0, "pop_back on empty Blob");data->pop_back();
}
void print(ostream& os,Blob b) {for (auto a : *(b.getData())) {os << a << " ";}os << endl;
}#endif

對Blob進行測試代碼如下:

	Blob b1{ "a","an","the" };cout << "b1的元素為:";print(cout,b1);Blob b2(b1);cout << "b2的元素為:";print(cout, b2);b2.changeData(0,"hello");cout << "對b2中的元素進行修改后:" << endl;cout << "b1的元素為:";print(cout, b1);cout << "b2的元素為:";print(cout, b2);

輸出結果:

b1的元素為:a an the
b2的元素為:a an the
對b2中的元素進行修改后:
b1的元素為:hello an the
b2的元素為:hello an the

從輸出結果我們可以看出,Blob在多個對象間共享數據。當我們拷貝、賦值或銷毀一個Blob對象時,它的shared_ptr成員會被拷貝、賦值或銷毀。

我們定 義的Blob與vector的區別:

vector<string>v1;
{vector<string>v2={"a","an","the"};v1=v2;//從v2拷貝元素到v1中
}//v2被銷毀,其中的元素也被銷毀
//v1有三個元素,是原來v2元素的拷貝
Blob<string>b1;
{Blob<string>b2={"a","an","the"};b1=b2;//b1和b2共享相同的元素
}//b2被銷毀,其中的元素不能銷毀
//b1指向最初由b2創建的元素

直接管理內存

C++語言定義了兩個運算符來分配和釋放動態內存。運算符new分配內存,delete釋放new分配的內存。相對于智能指針,使用這兩個運算符管理內存非常容易出錯。

使用new動態分配和初始化對象

在自由空間分配的內存是無名的,因此new無法為其分配的對象命名,而是返回一個指向該對象的指針。
例如:

int *pi = new int(1024);
vector<int> *pv = new vector<int>{1,2,3,4,5};

如果我們提供了一個括號包圍的初始化器,就可以使用auto,從此初始化器來推斷我們想要分配的對象的類型。只有當括號中僅有單一初始化器時才可以使用auto:

auto p1 = new auto(obj); //p1指向一個與obj類型相同的對象
auto p2 = new auto(a,b,c); //錯誤:括號中只能有單個初始化器

動態分配的const對象

類似其他任何const對象,一個動態分配的const對象必須進行初始化。

const int *pci = new const int(1024);

內存耗盡 定位new

雖然現代計算機通常都配備大容量內存,但是自由空間被耗盡的情況還是有可能發生。一旦一個程序用光了它所有可用的內存,new表達式就會失敗。默認情況下,如果new不能分配所要求的內存空間,它會拋出一個類型為bad_alloc的異常。我們可以改變使用new的方式來阻止它拋出異常。我們稱這種形式的new為定位new。

int *p1 = new int;//如果分配失敗,new拋出std::bad_alloc
int *p1 = new (nothrow) int;//如果分配失敗,new返回一個空指針

釋放動態內存

我們通過delete表達式來將動態內存歸還給系統。delete表達式接受一個指針,指向我們想要釋放的對象:

delete p;

與new類型類似,delete表達式也執行兩個動作:銷毀給定的指針指向的對象;釋放對應的內存。

我們傳遞給delete的指針必須指向動態分配的內存,或者是一個空指針。釋放一塊并非new分配的內存,或者將相同的指針值釋放多次,其行為是未定義的。

由內置指針(而不是智能指針)管理的動態內存在被顯式釋放前一直都會存在。

使用new和delete管理動態內存存在三個常見問題

  • 忘記delete內存,會導致“內存泄漏”問題,因為這種內存永遠不可能歸還給自由空間了。
  • 使用已經釋放掉的對象。
  • 同一塊內存釋放兩次。當有兩個指針指向相同的動態分配對象時,可能發生這種錯誤。如果對其中一個指針進行了delete操作,對象的內存就被歸還給自由空間了。如果我們隨后又delete第二個指針,自由空間就可能被破壞。

delete之后重置指針值

當我們delete一個指針后,指針值就變為無效了。雖然指針已經無效,但在很多機器上指針仍然保存著(已經釋放了的)動態內存的地址。在delete之后,指針就變成了人們所說的空懸指針,即,指向一塊曾經保存數據對象但現在已經無效的內存的指針。
未初始化指針的所有缺點空懸指針也都有。有一種方法可以避免空懸指針的問題:在指針即將要離開其作用域之前釋放掉它所關聯的內存。如果我們需要保留指針,可以在delete之后將nullptr賦予指針,這樣就清楚地指出指針不指向任何對象。

動態內存的一個基本問題是可能有多個指針指向相同的內存。在delete內存之后重置指針的方法只對這個指針有效,對其他任何仍指向(已釋放的)內存的指針是沒有作用的,以下代碼重置p對q沒有任何作用。

int *p(new int(42));
auto q=p;  //p和q指向相同的內存
delete p;//p和q均變為無效
p=nullptr;//指出p不再綁定到任何對象

智能指針和new對比

int *q=new int(42),*r=new int(100);
r=q;
auto q2=make_shared<int>(42),r2=make_shared<int>(100);
r2=q2;

以上代碼new指針的兩個問題:

  • 內存泄漏問題,r和q都指向42的內存地址,而r原來保存的地址——100的內存再無指針管理,不能進行釋放,造成內存泄漏。
  • 空懸指針問題,r和q指向同一個動態對象,如果程序編寫不當,很容易產生釋放了其中一個指針,而繼續使用另一個指針的問題。

智能指針的優勢:

  • 將q2賦予r2,賦值操作會將q2指向的對象地址賦予r2,并將r2原來指向的對象的引用計數減1,將q2指向的對象的引用計數加1,這樣前者的引用計數變為0,其占用的內存空間會被釋放,不會造成內存泄漏。
  • 而后者的引用計數變為2,也不會因為r2和q2之一的銷毀而釋放它的內存空間,因此也不會造成空懸指針的問題。

shared_ptr和new結合使用

如果我們不出事后一個智能指針,它就會被初始化為一個空指針。我們還可以用new返回的指針來初始化智能指針。接受指針參數的智能指針構造函數是explicit,因此我們不能將一個內置指針隱式轉換為一個智能指針,必須使用直接初始化形式來初始化一個智能指針。

shared_ptr<int>p1=new int(1024);//錯誤
shared_ptr<int>p2(new int(1024));//正確:使用了直接初始化形式

定義和改變shared_ptr的其他方法

在這里插入圖片描述
在這里插入圖片描述

不要混合使用智能指針和普通指針

void process(shared_ptr<int>ptr){}shared_ptr<int> x(new int(1024));//引用計數為1
process(x); //正確,拷貝x會遞增它的引用計數,在process中引用計數為2
int j=*x;//正確:引用計數為1
void process(shared_ptr<int>ptr){}int *x(new int(1024));
process(x); //錯誤,不能將int*轉換為shared_ptr<int>
process(shared_ptr<int>(x));//正確,但內存會被釋放
int j=*x;//未定義的,x是一個空懸指針

在該調用中,我們將一個臨時的shared_ptr傳遞給process,當這個調用所在的表達式結束時,這個臨時對象就被銷毀了。銷毀這個臨時變量會遞減引用計數,此時引用計數為0,因此,當臨時變量被銷毀時,它所指向的內存會被釋放,但x繼續指向(已經釋放的)內存,從而變成一個空懸指針,如果試圖使用x的值,其行為是未定義的。
當將一個shared_ptr綁定到一個普通指針時,我們就將內存的管理責任交給了這個shared_ptr。一旦這樣做了,我們就不應該再使用內置指針來訪問shared_ptr所指向的內存了。
使用一個內置指針來訪問一個智能指針所負責的對象是很危險的,因為我們無法知道對象何時會被銷毀。

不要使用get初始化另一個智能指針或為智能指針賦值

智能指針類型定義了一個名為get的函數,它返回一個內置指針,指向智能指針管理的對象。當我們需要向不能使用智能指針的代碼傳遞一個內置指針時可用get。使用get返回的指針的代碼不能delete此指針。

	shared_ptr<int>p(new int(42));//p的引用計數為1int *q = p.get();//正確,但使用q時要注意,不要讓它管理的指針被釋放,p的引用計數仍然為1{process(shared_ptr<int>(q));}int foo = *p;//錯誤,此時p的引用計數為1,但p指向的內存已經被釋放了,p成為類似空懸指針的shared_ptr

在本例中,p和q指向相同的內存,由于它們是相互獨立創建的,因此各自的引用計數都是1。當q所在的程序塊結束時,q被銷毀,這會導致q所指向的內存被釋放。從而q變成一個空懸指針,當我們試圖使用p時,將發生未定義的行為。而且當p被銷毀時,這塊內存會被第二次delete。

智能指針和異常

使用智能指針,即使程序塊過早結束,智能指針類也能確保在內存不再需要時將其釋放:

void f(){shared_ptr<int>sp(new int(42));//分配一個新對象//這段代碼拋出一個異常,且在f中未被捕獲
}//在函數結束時shared_ptr自動釋放內存

與之相對的,如果使用內置指針管理內存,且在new之后在對應的delete之前發生異常,則內存不會被釋放:

void f(){int *ip=new int(42);//動態分配一個新對象//這段代碼拋出一個異常,且在f中未被捕獲,則內存不會被釋放delete ip;//釋放內存的代碼
}

使用自己定義的釋放操作

現有以下類和函數:

struct destination;//表示我們正在連接什么
struct connection;//使用連接所需的信息
connection connect(destination*);//打開連接的函數
void disconnect(connection);//關閉給定的連接

代碼一:

void f(destination &d){//獲得一個連接connection c = connect(&d);//使用連接//如果我們在f退出前忘記調用disconnect,就無法關閉c了
}

代碼二,使用shared_ptr:

//定義刪除器:
void end_connection(connection *p){disconnect(*p);}
//創建shared_ptr:
void f(destination &d){//獲得一個連接connection c = connect(&d);shared_ptr<connection>p(&c,end_connection);//使用連接//當f退出時(即使是由于異常而退出),connection會被正確關閉
}

當p被銷毀時,它不會對自己保存的指針執行delete,而是調用end_connection,接下來end_connection會調用disconnect,從而確保連接被正確關閉。如果f正常退出,那么p的銷毀會作為結束處理的一部分。如果發生了異常,p同樣會被銷毀,從而連接被正確關閉。

使用智能指針基本規范

  • 不適用相同的內置指針值初始化(或reset)多個智能指針
  • 不delete get()返回的指針
  • 不適用get()初始化或reset另一個智能指針
  • 如果使用get()返回的指針,記住當最后一個對應的智能指針銷毀后,你的指針就變為無效了
  • 如果你使用智能指針管理的資源不是new分配的內存,記住傳遞給它一個刪除器

unique_ptr

與shared_ptr不同,某個時刻只能有一個unique_ptr指向一個給定對象,與shared相同的操作在表中

unique_ptr特有的操作

在這里插入圖片描述
由于一個 unique_ptr擁有它所指向的對象,因此 unique_ptr不支持普通的拷貝或賦值操作:

unique_ptr<string>p1(new string("hello"));
unique_ptr<string>p2(p1);//錯誤,不支持拷貝
unique_ptr<string>p3;
p3=p1;//錯誤,不支持賦值

雖然我們不能拷貝或賦值unique_ptr,但可以通過調用release或reset將指針的所有權從一個(非const)unique_ptr轉移給另一個unique_ptr:
在這里插入圖片描述

release返回指針

如果我們不用另一個智能指針來保存release返回的指針,我們的程序就要負責資源的釋放:

p2.release();//錯誤,p2不會釋放內存,而且我們丟失了指針
auto p=p2.release();//正確,但我們必須記得 delete p

傳遞unique_ptr參數和返回unique_ptr

不能拷貝unique_ptr的規則有一個例外:我們可以拷貝或賦值一個將要被銷毀的unique_ptr。最常見的例子是從函數返回一個unique_ptr:

unique_ptr<int> clone(int p){return unique_ptr<int>(new int(p));
}

還可以返回一個局部對象的拷貝:

unique_ptr<int> clone(int p){unique_ptr<int>ret(new int(p));//...return ret;
}

向unique_ptr傳遞刪除器

類似shared_ptr,unique_ptr默認情況下用delete釋放它所指向的對象。與shared_ptr一樣,我們可以重載一個unique_ptr中默認的刪除器。
對比shared_ptr的連接程序,我們重寫連接程序如下:

//定義刪除器:
void end_connection(connection *p){disconnect(*p);}
//創建shared_ptr:
void f(destination &d){//獲得一個連接connection c = connect(&d);unique_ptr<connection,decltype(end_connection)*>p(&c,end_connection);//使用連接//當f退出時(即使是由于異常而退出),connection會被正確關閉
}

weak_ptr

weak_ptr是一種不控制所指向對象生存期的智能指針,它指向由一個shared_ptr管理的對象。將一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數。一旦最后一個指向對象的shared_ptr被銷毀,對象就會被釋放。即使有weak_ptr指向對象,對象也還是會被釋放。
在這里插入圖片描述
示例代碼:

	auto p = make_shared<int>(42);auto q(p);auto p2(p);cout << "p.use_count():" << p.use_count() <<endl;weak_ptr<int>wp(p);cout << "p.use_count():" << p.use_count() << endl;cout << "wp.use_count():" << wp.use_count() << endl;

輸出結果:

p.use_count():3
p.use_count():3
wp.use_count():3

由于對象可能不存在,我們不能使用weak_ptr直接訪問對象,必須調用lock。次函數檢查weak_ptr指向的對象是否仍存在。

if(shared_ptr<int>np=wp.lock()){ //如果np不為空則條件成立
//在if中,np與p共享對象
}

動態數組

new和數組

int *pia = new int[10];//分配10個int,pia指向第一個int

分配一個數組會得到一個元素類型的指針。

我們還可以提供一個元素初始化器的花括號列表:
在這里插入圖片描述

釋放動態數組

delete p;//p必須指向一個動態分配的對象或為空
delete [] p;//p必須指向一個動態分配的數組或為空,
//數組中的元素按逆序進行銷毀,即,最后一個元素首先被銷毀,
//然后是倒數第二個,以此類推

智能指針和動態數組

標準庫提供了一個可以管理new分配的數組的unique_ptr版本。

unique_ptr<int[]>up(new int[10]);
up.release();//自動用delete[]銷毀其指針

指向數組的unique_ptr

在這里插入圖片描述
與unique_ptr不同,shared_ptr不直接支持管理動態數組,如果希望使用shared_ptr管理一個動態數組,必須提供自己定義的刪除器:

shared_ptr<int>sp(new int[10],[](int *p){delete[]p;});
sp.reset();//使用我們提供的lambda釋放數組,它使用delete[]釋放數組

如果未提供刪除器,這段代碼將是未定義的。默認情況下,shared_ptr使用delete銷毀它指向的對象。

連接兩個字符串

代碼:

	const char *c1 = "hello";const char *c2 = "world";char *r = new char[strlen(c1)+ strlen(c2)+1];strcpy(r,c1);strcat(r, c2);cout << r << endl;string s1 = "hello";string s2 = "world";strcpy(r,(s1+s2).c_str());cout << r << endl;

輸出結果:

helloworld
helloworld

allocator類

new有一些靈活性上的局限,其中一方面表現在它將內存分配和對象構造組合在了一起,這可能會導致不必要的浪費。
標準庫allocator類定義在頭文件memory中。它幫助我們將內存分配和對象構造分離開來。它提供一種類型感知的內存分配方法,它分配的內存是原始的、未構造的。

標準庫allocator類及其算法

在這里插入圖片描述

allocator分配未構造的內存

在這里插入圖片描述
還未構造對象的情況下就使用原始內存是錯誤的。
為了使用allocate返回的內存,我們必須用construct構造對象。使用未構造的內存,其行為是未定義的。

當我們用完對象后,必須對每個構造的元素調用destroy來銷毀它們。函數destroy接受一個指針,對指向的對象執行析構函數。

allocator算法

在這里插入圖片描述
在這里插入圖片描述
類似copy,uninitialized_copy返回(遞增后的)目的位置迭代器,因此,一次uninitialized_copy調用會返回一個指針,指向最后一個構造的元素之后的位置。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/446554.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/446554.shtml
英文地址,請注明出處:http://en.pswp.cn/news/446554.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C語言順序查找二分查找

介紹 順序查找 按照順序一個個查找 #include<stdio.h> //順序查找 int search(int arr[],int len,int aim) {int i;for(i0;i<len;i){if(arr[i]aim){return i;//返回下標 }}return -1;//表示未查詢到} int main() {int arr[]{13,355,256,65,234,-1,35,-6,-3,-4,0};…

C++ primer 第12章 12.3 使用標準庫:文本查詢程序

文章目錄使用標準庫&#xff1a;文本查詢程序文本查詢程序設計數據結構在類之間共享數據自己的文本查詢程序書中的文本查詢程序使用標準庫&#xff1a;文本查詢程序 我們將實現一個簡單的文本查詢程序&#xff0c;作為標準庫相關內容學習的總結。 我們的程序允許用戶在一個給…

C語言二維數組 int arr[2][3]

基礎使用 先遍歷行再遍歷列 #include<stdio.h> //二維數組的基本使用 int main() {//二維數組的初始化int arr1[2][2]{{2,2},{0,0}};int arr2[2][3]{2,2,2,8,8,8};int arr3[6][9];int i,j;for(i0;i<6;i){for(j0;j<9;j){arr3[i][j]1;}}arr3[2][5]0;//打印printf(&…

牛客網C++面經 類和數據抽象

請你來說一下C中struct和class的區別 在C中&#xff0c;可以用struct和class定義類&#xff0c;都可以繼承。區別在于&#xff1a;structural的默認繼承權限和默認訪問權限是public&#xff0c;而class的默認繼承權限和默認訪問權限是private。另外&#xff0c;class還可以定義…

C++ primer 第13章 拷貝控制

文章目錄前言拷貝、賦值與銷毀拷貝構造函數合成拷貝構造函數拷貝初始化和直接初始化拷貝初始化的發生&#xff1a;參數和返回值拷貝初始化的限制拷貝賦值運算符重載賦值運算符合成拷貝賦值運算符析構函數析構函數完成的工作什么時候會調用析構函數合成析構函數代碼片段調用幾次…

C語言 指針自增自減加減運算 p++ p+i

介紹 自增自減代碼 #include<stdio.h> #include<string.h> //指針自增--short void increase(short *arr,int len) {int i;arr&arr[0];for(i0;i<len;i){printf("arr[%d]%d,address%p\n",i,*arr,arr);arr;} }//指針自減--char void decrease(char…

C++ 編譯與底層

原文鏈接 編譯與底層請你來說一下一個C源文件從文本到可執行文件經歷的過程&#xff1f; 對于C源文件&#xff0c;從文本到可執行文件一般需要四個過程&#xff1a;預處理階段&#xff1a;對源代碼文件中文件包含關系&#xff08;頭文件&#xff09;、預編譯語句&#xff08;…

C語言 指針數組-字符指針數組整型指針數組 char*s[3] int*a[5] 數組指針int(*p)[4]

基本介紹 1.指針數組:由n個指向整型元素的指針而組成,里面存放指針 Int *ptr[3]; 2.地址: ptr[i]:元素地址 &ptr[i]:指針地址 圖示 代碼: 內存布局: 代碼 #include<stdio.h> #include<string.h> //指針數組--int void pointer(int *arr,int len) {int …

uninitialized_copy測試代碼示例

原測試代碼如下&#xff1a; int main() {vector<int>v1{1,3,5,7,9,2,4,6,8};allocator<int>alloc;auto data alloc.allocate(9);uninitialized_copy(v1.begin(),v1.end(), data);auto end data 9;while(data!end) {cout << *data <<" "…

C語言的地址 內存

取地址在CPU的寄存器產生&#xff0c;不占據內存地址由計算器總線&#xff0c;地址作為常量不消耗內存指針 存儲不同的地址&#xff0c;間接賦值空類型指針 void* 類型指針 不可以取數據 或者修改數據 需要進行強制類型轉換int num 10;void *p &num;std::cout << …

C語言 多重指針--整型字符字符串 int**pp

介紹 多重指針:一個指針指向另一個指針 離值越近的指針級別越大:一級 內存布局 代碼 圖示: 多重指針–整型 #include<stdio.h> #include<string.h> //多重指針--整型//二級指針 void two() {printf("二級指針:\n");int a896;int *p&a,**pp&…

C++ primer 第13章 拷貝控制

文章目錄前言拷貝、賦值與銷毀拷貝構造函數合成拷貝構造函數拷貝初始化和直接初始化拷貝初始化的發生&#xff1a;參數和返回值拷貝初始化的限制拷貝賦值運算符重載賦值運算符合成拷貝賦值運算符析構函數析構函數完成的工作什么時候會調用析構函數合成析構函數代碼片段調用幾次…

牛客網C++面經 C++11

請問C11有哪些新特性&#xff1f; auto關鍵字&#xff1a;編譯器可以根據初始值自動推導出類型。但是不能用于函數傳參以及數組類型的推導nullptr關鍵字&#xff1a;nullptr是一種特殊類型的字面值&#xff0c;它可以被轉換成任意其它的指針類型&#xff1b;而NULL一般被宏定義…

C語言 返回指針的函數--指針函數 int* max(int a)

定義 strlong示例代碼 代碼1: #include<stdio.h> #include<string.h> //返回指針的函數//比較兩個字符串,返回更長的字符串 char *strlong(char* a,char* b) {char *p1&a[0];char *p2&b[0];while(true){if(*p1\0){return b;}else if(*p2\0){return a;}p1…

第2、3講 圖像的存儲格式

本圖像處理系列筆記是基于B站楊淑瑩老師的課程進行學習整理的。 文章目錄黑白圖像8位灰度索引圖像8位偽彩色索引圖像24位真彩色圖像圖像文件格式BMP文件存儲格式BMP文件頭位圖信息頭顏色表位圖信息——BITMAPINFO結構BMP位圖文件匯總按照顏色深度分類&#xff0c;常用圖像文件&…

Ubuntu18.04.4 環境下對屬性加密算法CP-ABE環境搭建

注意事項 cpabe依賴pbc&#xff0c;pbc依賴gmp&#xff0c;gmp依賴M4、bison、flex如果權限不夠 &#xff0c;命令的前面加上sudo &#xff0c;不要直接使用root用戶進行操作&#xff0c;其帶來的隱患有很多 第一步 配置簡單的環境 簡單環境 包括gcc、g、make、cmake、openss…

C語言 函數指針 int(*ptr)(int,int)

基本介紹 函數指針:指向函數的指針 與數組類似 定義 Int(*pmax)(int ,int)max; Int(*pmax)(int x,int y)max;//形參名稱不重要 函數返回類型(*指針)(形參類型)函數名稱; 具體案例 代碼: *pmax取到函數本身 調用函數指針方式: (*pmax)(x,y); pmax(x,y);//與java中調用函數一…

C++ primer 第14章 操作重載與類型轉換

文章目錄基本概念直接調用一個重載的運算符函數某些運算符不應該被重載使用與內置類型一致的含義選擇作為成員或者非成員輸入和輸出運算符重載輸出運算符<<輸出運算符盡量減少格式化操作輸入輸出運算符必須是非成員函數重載輸入運算符>>算術和關系運算符相等運算符…

C語言 回調函數 produce(arr,len,getRand)

基本介紹 回調函數:形參中包含另一個函數的函數指針 用函數指針接收另一個函數 案例 代碼解析 具體代碼 #include<stdio.h> #include<stdlib.h> //回調函數--//函數原型 int getRand(); int *produce(int*arr,int len,int(*get)()); int main() {int arr[10…

從零開始配置服務器密碼機的開發環境

開發環境環境配置安裝gcc編譯器安裝g編譯器安裝make安裝cmake安裝ssh安裝git和配置安裝大文件管理工具git-lfs安裝數據庫sqlite3安裝數據庫sqlite_orm文件安裝Openssl安裝Tcl和Tk安裝tcl-expect-dev安裝boost安裝clang-format安裝Clion注意事項安裝automake和libudev-dev環境配…