主頁 > 軟體設計 > 【C語言進階學習筆記】五、動態記憶體分配(爆肝吐血力作,強烈建議收藏!!!)

【C語言進階學習筆記】五、動態記憶體分配(爆肝吐血力作,強烈建議收藏!!!)

2021-07-27 08:07:22 軟體設計

前言

現代計算機基本都是基于馮諾伊曼結構體系設計出來的,馮諾伊曼結構體系的核心就是“存盤程式”,將程式(指令集)和資料以同等地位存盤在記憶體中,但是我們的記憶體空間并不是無限大的,所以為了高效的利用好記憶體空間,作業系統會對這些記憶體空間進行相應的磁區,不同區域的記憶體有其對應的功能和使用方式,
在這里插入圖片描述
比如區域變數、函式形參通常是存盤在堆疊區的,這部分記憶體空間的特點就是臨時使用,用完即釋放(當然這個都是由作業系統自動完成的,不需要程式員的干預);
再比如全域變數通常存放在靜態區,此外由static修飾的區域變數也會放到靜態區(所以static修飾區域變數,本質上是改變了其存盤的位置,從堆疊區-- > 靜態區),這部分記憶體空間就是生命周期很長,長到整個程式運行結束;
再例如我們使用的常量字串,會被保存到常量區,這部分記憶體區域的特點就是類似于“常量”,不可被修改,相當于添加了一個“const”的buff,
(我的廢話:在學習的時候如果可以多多思考,聯系不同的知識點,當將這些內容串起來時,可以形成一個比較宏觀、整體的角度,更進一步,如果我們能夠從中找到樂趣就更好了!現在我們進入正題吧!)


全文結構:
在這里插入圖片描述

文章目錄

    • 前言
    • 一、尋根問底
      • 什么是動態記憶體分配 / 管理?
      • 為什么需要動態記憶體分配?
      • 怎么建立動態記憶體分配?
    • 二、動態記憶體函式
      • malloc
      • free
      • calloc
      • realloc
    • 三、常見的動態記憶體錯誤
      • 1)對空指標NULL的解參考操作
      • 2)對動態開辟空間的越界訪問
      • 3)對非動態開辟記憶體使用 free 釋放
      • 4)使用 free 釋放一塊動態開辟記憶體的一部分
      • 5)對同一塊動態記憶體的多次釋放
      • 6)動態開辟記憶體忘記釋放(導致記憶體泄露)
    • 四、柔性陣列


一、尋根問底

什么是動態記憶體分配 / 管理?

由程式員根據實際編程需要向作業系統申請,在堆區上開辟的,供程式員操作使用和維護的記憶體空間,程式員的游樂園!通常是一些臨時用到的資料或者變數,隨時開辟,用完隨時釋放,而不必等到函式結束后由作業系統回收!

為什么需要動態記憶體分配?

實際編程中,不僅需要大小固定的記憶體空間,往往還需要大小可變的記憶體空間,
比如說如果我們要建立一個通訊錄,用來存放相關資訊(姓名、性別、年齡、電話等),這種復雜的資料,我們知道要用結構體型別來存盤,而且要用結構體陣列來存,但是問題是“這個陣列的大小應該多大呢?”
這個問題確實不好回答,如果長度給小了,那么就會導致資料溢位,進而引發程式崩潰,
如果給大了,又會導致存盤空間大量浪費,空間利用率低,
為了解決這一類問題,就出現了動態記憶體分配,記憶體空間按需索取,要多少給多少!

怎么建立動態記憶體分配?

通過系統提供的4個庫函式實作,malloc\calloc\realloc\free,這四個函式后面我們會詳細介紹,


二、動態記憶體函式

注意:以下說的四個函式的頭檔案均為:stdlib.h

malloc

函式原型:void * malloc(size_t size);

size_t就是unsigned int(無符號整型)

這個函式的作用就是在動態存盤區中分配一個長度為size個位元組的連續空間,并回傳指向該空間的指標,

1)如果開辟成功,則回傳一個指向開辟好空間的指標,
2)如果開辟失敗,則回傳一個NULL指標,因此malloc的回傳值一定要做檢查,
3)回傳值的型別是void * ,所以malloc函式并不知道開辟空間的型別,具體在使用的時候使用者自己來決定,
4)如果引數size為0,malloc的行為是標準是未定義的,取決于編譯器,

動態開辟的空間如何釋放和回收呢?
C語言提供了一個專門完成這個功能的庫函式-- - free


free

函式原型:void free(void* p)

free的作用就是釋放指標變數p所指向的動態空間,使這部分空間能夠重新被利用,

1)如果引數ptr指向的空間不是動態開辟的,那free函式的行為是未定義的,
2)如果引數 ptr是NULL指標,則函式什么事都不做,


現在來看一下實際的使用:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//1.通過動態開辟申請10個int型別的空間
	int* ptr = (int*)malloc(10 * sizeof(int));//通常結合sizeof一起使用
	//根據實際使用強制型別轉換為想要的型別
	//2.malloc有可能申請空間失敗,所以需要判斷一下
	if (ptr == NULL)
	{
		perror("main");//perror是一個報錯函式,實際出錯時列印效果為:main:xxxxxx(錯誤原因)
		return 0;//出錯就直接結束函式
	}
	//3.使用 給這10個整型空間賦值
	for (int i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	//列印一下
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ptr[i]);//這里可以直接使用陣列下標的形式,和指標解參考是一樣的
	}
	//4.釋放
	free(ptr);
	ptr = NULL;//需要手動置為NULL,防止非法訪問
	return 0;
}

在這里插入圖片描述

注意: 用malloc申請的空間,里面的內容是隨機值,如果不初始化的話,可能就會得到一些意想不到的值;

在這里插入圖片描述

理解:如果引數ptr指向的空間不是動態開辟的,那free函式的行為是未定義的,

在這里插入圖片描述


為什么要進行動態記憶體的釋放和回收?

記憶體空間是有限的,如果我們每次在使用的時候只是一味的向申請空間,即使空間再大,也會被用完,而且如果使用的空間不釋放會導致電腦越來越卡,程式運行越來越慢!

那釋放之后為什么要手動將指標賦值為NULL(空指標)呢?

舉一個生活中的例子吧,假設有一個男生跟他女朋友分手了,如果這個男生還一直保留這個女生的電話、微信,更有甚者,還有這個女生家里面的鑰匙,如果你是這個女生的話,你希望他仍然保留這些資訊和物品嗎?你肯定是不想對吧,指不定哪一天他不高興或者其它原因就來騷擾你,(所以才會有一句話叫做情侶分手千萬不要藕斷絲連,當然如果你是這個男生的話,你可能還想著以后和好如初,念念不忘,必有回響~haha),
言歸正傳,編程中如果指標指向的空間已經被釋放了,如果不將其置為NULL,那么其仍然保留這個地方的地址,之后仍然有可能訪問到這片空間,這個生活就是非法訪問了!

那有沒有動態分配函式在申請空間的同時就進行初始化呢 ?

答案當然是有,接下來要結束的calloc就是這樣的一個函式


calloc

函式原型:void * calloc(size_t num, size_t size);

1)函式的功能是為num 個大小為size的元素開辟一塊空間,并且把空間的每個位元組初始化為O
2)與函式ma1loc的區別只在于calloc會在回傳地址之前把申請的空間的每個位元組初始化為全0,

比如剛剛的上面的代碼,如果我們將malloc換成calloc,不進行手動初始化:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = (int*)calloc(10, sizeof(int));
	if (ptr == NULL)
	{
		perror("main");
		//perror是一個報錯函式,實際出錯時列印效果為:main:xxxxxx(錯誤原因)
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ptr[i]);
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

在這里插入圖片描述
光從上面這三個函式的介紹,我們可能并沒有深刻體會到“動態記憶體分配”的動態體現在哪,接下來要介紹的函式才是動態記憶體分配的“靈魂”-- - realloc


realloc

realloc函式的出現讓動態記憶體管理更加靈活,
有時會我們發現過去申請的空間太小了,有時候我們又會覺得申請的空間過大了,那為了合理的使用記憶體,我們一定會對記憶體的大小做靈活的調整,那rea1lloc函式就可以做到對動態開辟記憶體大小的調整,

函式原型:void * realloc(void* ptr, size_t size);

1)ptr是要調整的記憶體地址.size是調整之后新大小
2)回傳值為調整之后的記憶體起始位置
3)這個函式調整原記憶體空間大小的基礎上,還會將原來記憶體中的資料移動到新的空間,(這種移動的方式實際上就是復制拷貝,會將原內容復制拷貝到新記憶體中)
4)realloc在調整記憶體空間的是存在兩種情況︰
情況1∶原有空間之后有足夠大的空間
情況2︰原有空間之后沒有足夠大的空間

在這里插入圖片描述

當是情況1的時候,要擴展記憶體就直接原有記憶體之后直接追加空間,原來空間的資料不發生變化,
當是情況2的時候,原有空間之后沒有足夠多的空間時,擴展的方法是∶在堆空間上另找一個合適大小的連續空間來使用,這樣函式回傳的是一個新的記憶體地址,

由于上述的兩種情況,realloc函式的使用就要注意一些,

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = (int*)malloc(10 * sizeof(int));
	if (ptr == NULL)
	{
		perror("main");
		return 0;
	}
	//業務處理
	//進行擴容操作
	int* p = (int*)realloc(ptr, 100 * sizeof(int));
	//注意:不能直接將擴容之后的地址給ptr,因為存在擴容失敗的可能,會導致ptr地址丟失
	if (p == NULL)
	{
		printf("raalloc failed!\n");
		return 0;
	}
	ptr = p;
	//業務處理
	free(ptr);
	ptr = NULL;
	return 0;
}

三、常見的動態記憶體錯誤

1)對空指標NULL的解參考操作

void test()
{
	//int* p = NULL;
	int* p = (int*)malloc(INT_MAX);
	*p = 20;//如果p的值為NULL,就會出問題
	free(p);
}

2)對動態開辟空間的越界訪問

在這里插入圖片描述
當I = 10時越界訪問


3)對非動態開辟記憶體使用 free 釋放

在這里插入圖片描述


4)使用 free 釋放一塊動態開辟記憶體的一部分

在這里插入圖片描述


5)對同一塊動態記憶體的多次釋放

在這里插入圖片描述


6)動態開辟記憶體忘記釋放(導致記憶體泄露)

#include<stdio.h>
#include<stdlib.h>
void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	while (1)
	{
		test();
	}
	return 0;
}

在這里插入圖片描述


四、柔性陣列

也許你從來沒有聽說過柔性陣列(flexible array)這個概念,但是它確實是存在的,C99中,結構中的最后一個元素允許是未知大小的陣列,這就叫做『柔性陣列』成員,

其實從名字我們也可以大概知道其含義,“柔性”指柔軟的,可變動的,flexible 本就具有靈活的,可變的含義,

舉例:

struct S
{
	int n;
	int arr[];//還可以寫成這樣 int arr[0];
};

柔性陣列的特點∶
1)結構中的柔性陣列成員前面必須至少一個其他成員,(也就是說柔性陣列成員不能單獨存在)
2)sizeof回傳的這種結構大小不包括柔性陣列的記憶體,(計算大小的時候,不考慮柔性陣列成員的大小)

在這里插入圖片描述

3)包含柔性陣列成員的結構用malloc()函式進行記憶體的動態分配,并且分配的記憶體應該大于結構的大小,以適應柔性陣列的預期大小,(也就是包含柔性陣列成員的結構體型別在創建變數的時候,需要用動態記憶體開辟的方式來創建,原因是:柔性陣列的大小可變,那么其創建出來的結構體變數大小也是可變的,所以需要動態開辟的方式來創建!)

舉例:

#include<stdio.h>
struct S
{
	int n;
	int arr[];//還可以寫成這樣 int arr[0];
};
int main()
{
	//struct S s1;這種方式創建的變數無法正常使用
	//printf("%d\n", sizeof(s1));
	//假設我們期望arr的大小是10個int型別
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	return 0;
}

動態開辟記憶體分布情況如圖:
在這里插入圖片描述

正因為空間是動態開辟出來的,如果后續使用的時候,陣列arr的空間大小不夠了,可以通過realloc去動態調整,體現了其“柔性”的特點,
在這里插入圖片描述


完整的代碼:

#include<stdio.h>
struct S
{
	int n;
	int arr[];//還可以寫成這樣 int arr[0];
};
int main()
{
	//假設我們期望arr的大小是10個int型別
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	ps->n = 10;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = i;
	}
	//調整
	struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if (ptr == NULL)
	{
		perror("main");
		return 1;
	}
	ps = ptr;
	//使用
	//.....
	//釋放
	free(ps);
	ps = NULL;
	return 0;
}

柔性陣列功能的替代方法
用一個指標代替柔性陣列成員

struct S
{
	int n;
	//int arr[];//還可以寫成這樣 int arr[0];
	int* arr;//替換柔性陣列
};

柔性陣列與非柔性陣列比較

好處一:方面記憶體釋放
如果我們的代碼是在一個給別人用的函式中,你在里面做了二次記憶體分配,并把整個結構體回傳給用戶,用戶呼叫free可以釋放結構體,但是用戶并不知道這個結構體內的成員也需要free,所以你不能指望用戶來發現這個事,所以,如果我們把結構體的記憶體以及其成員要的記憶體一次性分配好了,并回傳給用戶一個結構體指標,用戶做一次free就可以把所有的記憶體也給釋放掉,
好處二 : 這樣有利于訪問速度.
連續的記憶體有益于提高訪問速度,也有益于減少記憶體碎片,(其實,我個人覺得也沒多高了,反正你跑不了要用做偏移量的加法來尋址)
(涉及到記憶體池,區域性原理:空間區域性原理、時間區域性原理,有興趣的同學可自行查找資料!)


回顧:
在這里插入圖片描述

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/290324.html

標籤:其他

上一篇:【從零開始學c++】———模擬實作string類(常用介面的實作)

下一篇:還在糾結配色問題?手把手教你用MATLAB一鍵生成高質量色卡

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more