-
c++编程知识详解(二)动态内存分配
2019-03-13 15:33:44通常定义变量(或对象),编译器在编译时都可以根据该变量(或对象)的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。这种内存分配称为静态存储分配; 有些操作对象...一、动态内存分配的引入
一般,变量和对象在内存中的分配都是编译器在编译程序时安排好了的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。动态内存分配解决了这个问题。
通常定义变量(或对象),编译器在编译时都可以根据该变量(或对象)的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。这种内存分配称为静态存储分配;
有些操作对象只在程序运行时才能确定,这样编译时就无法为他们预定存储空间,只能在程序运行时,系统根据运行时的要求进行内存分配,这种方法称为动态存储分配。所有动态存储分配都在堆区中进行。
当程序运行到需要一个动态分配的变量或对象时,必须向系统申请取得堆中的一块所需大小的存贮空间,用于存贮该变量或对象。当不再使用该变量或对象时,也就是它的生命结束时,要显式释放它所占用的存贮空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。
二、对空间的申请和释放
在C++中,申请和释放堆中分配的存贮空间,分别使用new和delete的两个运算符来完成:
指针变量名=new 类型名(初始化式); delete 指针名;
例如: int *pi=new int(0); 等价于int ival=0, *pi=&ival;
区别:pi所指向的变量是由库操作符new()分配的,位于程序的堆区中,并且该对象未命名。
当pi生命周期结束时,必须释放pi所指向的目标:delete pi;
注意:这时释放了pi所指的目标的内存空间,但指针pi本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放。
1、new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而且动态创建的对象本身没有名字。
2、一般定义变量和对象时要用标识符命名,称命名对象,而动态的称无名对象(请注意与栈区中的临时对象的区别,两者完全不同:生命期不同,操作方法不同,临时变量对程序员是透明的)。
动态分配的变量或对象的生命期。无名对象的生命期并不依赖于建立它的作用域,比如在函数中建立的动态对象在函数返回后仍可使用。但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放是一件很容易失控的事,往往会出错。
3、堆区是不会在分配时做自动初始化的(包括清零),所以必须用初始化式(initializer)来显式初始化。new表达式的操作序列如下:从堆区分配对象,然后用括号中的值初始化该对象。
三、指针数组(数组里存放的都是同一个类型的指针)
int * a[2];//它里边放了2个int * 型变量
a[0]= new int[3];
a[1]=new int[3];
delete a[0];
delete a[1];注意这里是一个数组,不能delete [] ;
四、数组指针(指向一位或多维数组的指针)
1、申请数组空间:
指针变量名=new 类型名[下标表达式];
注意:“下标表达式”不是常量表达式,即它的值不必在编译时确定,可以在运行时确定。
没有初始化式(initializer),不可对数组初始化
2、释放数组空间:
delete [ ]指向该数组的指针变量名;
注意:方括号非常重要的,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。delete [ ]的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。
int * b=new int[10];//指向一维数组的指针b ;
int (*b2)[10]=new int[10][10]; // b2指向了一个二维int型数组的首地址.
//int (*b2)[必须为常量表达式]
注意:在这里,b2等效于二维数组名,但没有指出其边界,即最高维的元素数量,但是它的最低维数的元素数量必须要指定!就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。int(*b3) [30] [20]; //指向三维数组的指针;
b3=new int [1] [20] [30];
删除这两个动态数组可用下式:delete [] b2; //删除(释放)二维数组;
delete [] b3; //删除(释放)三维数组;动态一维数组的创建:
示例:
#include <iostream>
#include <string.h>
using namespace std;
void main()
{
int n;
char *pc;
cout<<"请输入动态数组的元素个数"<<endl;
cin>>n; //n在运行时确定,可输入
pc=new char[n]; //申请个字符(可装个汉字和一个结束符)的内存空间
strcpy(pc,"堆内存的动态分配");//
cout<<pc<<endl;
delete []pc;//释放pc所指向的n个字符的内存空间
}
动态二维数组的创建:
#include <iostream>
#include <string.h>
using namespace std;
void display(double **data)
{
for(int i=0;i<4;i++)
{
for(int j=0;j<6;j++)
cout<<data[i][j]<<" ";
cout<<endl;
}
}
void main()
{
cout<<"请输入数组的行数和列数"<<endl;
int m,n;
cin>>m>>n;
double **data;
data = new double*[m]; //申请行
if ((data ) == 0)
{
cout << "Could not allocate. bye ...";
exit(-1);
}
for(int j=0;j<m;j++)
{
data[j] = new double[n]; //设置列
if (data[j] == 0)
{
cout << "Could not allocate. Bye ...";
exit(-1);
}
} //空间申请结束,下为初始化
for (int i=0;i<m;i++)
for (int j=0;j<n;j++)
data[i][j]=i*n+j;
display(data); //2、二维数组的输出。
//3、再看二维数组的撤销与内存释放:
for(int i=0;i<m;i++)
delete[] data[i];
delete[] data;
}
五、堆对象与构造函数
通过new建立的对象要调用构造函数,通过deletee删除对象也要调用析构函数。
CGoods *pc;
pc=new CGoods; //分配堆空间,并构造一个无名的CGoods对象;
delete pc; //先析构,然后将内存空间返回给堆;
正因为构造函数可以有参数,所以new后面类(class)类型也可以有参数。这些参数即构造函数的参数。
但对创建数组,则无参数,并只调用缺省的构造函数。
class CGoods{
char Name[21];
int Amount;
float Price;
float Total value;
public:
CGoods(){}; //缺省构造函数。因已有其他构造函数,系统不会再自动生成缺省构造,必须显式说明。
CGoods(char* name,int amount ,float price){
strcpy(Name,name);
Amount=amount;
Price=price;
Total_value=price*amount; }
……
};//类声明结束
//下面注意如何使用:
void main(){
int n;
CGoods *pc,*pc1,*pc2;
pc=new CGoods(“夏利2000”,10,118000); //调用三参数构造函数
pc1=new CGoods(); //调用缺省构造函数
cout<<’输入商品类数组元素数’<<endl;
cin>>n;
pc2=new CGoods[n]; //动态建立数组,不能初始化,调用n次缺省构造函数
……
delete pc;
delete pc1;
delete []pc2; }
此例告诉我们堆对象的使用方法:申请堆空间之后构造函数运行;释放堆空间之前析构函数运行;
再次强调:由堆区创建对象数组,只能调用缺省的构造函数,不能调用其他任何构造函数。如果没有缺省的构造函数,则不能创建对象数组。
堆和栈的区别?
1、申请方式不同。
栈由系统自动分配。堆由程序员手动分配。
2、申请大小限制不同。
栈顶和栈底是之前预设好的,大小固定,可以通过ulimit -a查看,由ulimit -s修改。堆向高地址扩展,是不连续的内存区域,大小可以灵活调整。
3、申请效率不同。
栈由系统分配,速度快,不会有碎片。堆由程序员分配,速度慢,且会有碎片
-
动态内存分配(new delete)
2012-04-24 15:50:55通常定义变量(或对象),编译器在编译时都可以根据该变量(或对象)的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。这种内存分配称为静态存储分配; 有些操作对象一、动态内存分配的引入
一般,变量和对象在内存中的分配都是编译器在编译程序时安排好了的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。动态内存分配解决了这个问题。
通常定义变量(或对象),编译器在编译时都可以根据该变量(或对象)的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。这种内存分配称为静态存储分配;
有些操作对象只在程序运行时才能确定,这样编译时就无法为他们预定存储空间,只能在程序运行时,系统根据运行时的要求进行内存分配,这种方法称为动态存储分配。所有动态存储分配都在堆区中进行。
当程序运行到需要一个动态分配的变量或对象时,必须向系统申请取得堆中的一块所需大小的存贮空间,用于存贮该变量或对象。当不再使用该变量或对象时,也就是它的生命结束时,要显式释放它所占用的存贮空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。
二、对空间的申请和释放
在C++中,申请和释放堆中分配的存贮空间,分别使用new和delete的两个运算符来完成:
指针变量名=new 类型名(初始化式); delete 指针名;
例如: int *pi=new int(0); 等价于int ival=0, *pi=&ival;
区别:pi所指向的变量是由库操作符new()分配的,位于程序的堆区中,并且该对象未命名。
当pi生命周期结束时,必须释放pi所指向的目标:delete pi;
注意:这时释放了pi所指的目标的内存空间,但指针pi本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放。
1、new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而且动态创建的对象本身没有名字。
2、一般定义变量和对象时要用标识符命名,称命名对象,而动态的称无名对象(请注意与栈区中的临时对象的区别,两者完全不同:生命期不同,操作方法不同,临时变量对程序员是透明的)。
动态分配的变量或对象的生命期。无名对象的生命期并不依赖于建立它的作用域,比如在函数中建立的动态对象在函数返回后仍可使用。但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放是一件很容易失控的事,往往会出错。
3、堆区是不会在分配时做自动初始化的(包括清零),所以必须用初始化式(initializer)来显式初始化。new表达式的操作序列如下:从堆区分配对象,然后用括号中的值初始化该对象。
三、指针数组(数组里存放的都是同一个类型的指针)
int * a[2];//它里边放了2个int * 型变量
a[0]= new int[3];
a[1]=new int[3];
delete a[0];
delete a[1];注意这里是一个数组,不能delete [] ;
四、数组指针(指向一位或多维数组的指针)
1、申请数组空间:
指针变量名=new 类型名[下标表达式];
注意:“下标表达式”不是常量表达式,即它的值不必在编译时确定,可以在运行时确定。
没有初始化式(initializer),不可对数组初始化
2、释放数组空间:
delete [ ]指向该数组的指针变量名;
注意:方括号非常重要的,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。delete [ ]的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。
int * b=new int[10];//指向一维数组的指针b ;
int (*b2)[10]=new int[10][10]; // b2指向了一个二维int型数组的首地址.
//int (*b2)[必须为常量表达式]
注意:在这里,b2等效于二维数组名,但没有指出其边界,即最高维的元素数量,但是它的最低维数的元素数量必须要指定!就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。int(*b3) [30] [20]; //指向三维数组的指针;
b3=new int [1] [20] [30];
删除这两个动态数组可用下式:delete [] b2; //删除(释放)二维数组;
delete [] b3; //删除(释放)三维数组;动态一维数组的创建:
示例:
#include <iostream>
#include <string.h>
using namespace std;
void main()
{
int n;
char *pc;
cout<<"请输入动态数组的元素个数"<<endl;
cin>>n; //n在运行时确定,可输入
pc=new char[n]; //申请个字符(可装个汉字和一个结束符)的内存空间
strcpy(pc,"堆内存的动态分配");//
cout<<pc<<endl;
delete []pc;//释放pc所指向的n个字符的内存空间
}
动态二维数组的创建:
#include <iostream>
#include <string.h>
using namespace std;
void display(double **data)
{
for(int i=0;i<4;i++)
{
for(int j=0;j<6;j++)
cout<<data[i][j]<<" ";
cout<<endl;
}
}
void main()
{
cout<<"请输入数组的行数和列数"<<endl;
int m,n;
cin>>m>>n;
double **data;
data = new double*[m]; //申请行
if ((data ) == 0)
{
cout << "Could not allocate. bye ...";
exit(-1);
}
for(int j=0;j<m;j++)
{
data[j] = new double[n]; //设置列
if (data[j] == 0)
{
cout << "Could not allocate. Bye ...";
exit(-1);
}
} //空间申请结束,下为初始化
for (int i=0;i<m;i++)
for (int j=0;j<n;j++)
data[i][j]=i*n+j;
display(data); //2、二维数组的输出。
//3、再看二维数组的撤销与内存释放:
for(int i=0;i<m;i++)
delete[] data[i];
delete[] data;
}
五、堆对象与构造函数
通过new建立的对象要调用构造函数,通过deletee删除对象也要调用析构函数。
CGoods *pc;
pc=new CGoods; //分配堆空间,并构造一个无名的CGoods对象;
delete pc; //先析构,然后将内存空间返回给堆;
正因为构造函数可以有参数,所以new后面类(class)类型也可以有参数。这些参数即构造函数的参数。
但对创建数组,则无参数,并只调用缺省的构造函数。
class CGoods{
char Name[21];
int Amount;
float Price;
float Total value;
public:
CGoods(){}; //缺省构造函数。因已有其他构造函数,系统不会再自动生成缺省构造,必须显式说明。
CGoods(char* name,int amount ,float price){
strcpy(Name,name);
Amount=amount;
Price=price;
Total_value=price*amount; }
……
};//类声明结束
//下面注意如何使用:
void main(){
int n;
CGoods *pc,*pc1,*pc2;
pc=new CGoods(“夏利2000”,10,118000); //调用三参数构造函数
pc1=new CGoods(); //调用缺省构造函数
cout<<’输入商品类数组元素数’<<endl;
cin>>n;
pc2=new CGoods[n]; //动态建立数组,不能初始化,调用n次缺省构造函数
……
delete pc;
delete pc1;
delete []pc2; }
此例告诉我们堆对象的使用方法:申请堆空间之后构造函数运行;释放堆空间之前析构函数运行;
再次强调:由堆区创建对象数组,只能调用缺省的构造函数,不能调用其他任何构造函数。如果没有缺省的构造函数,则不能创建对象数组。
-
关于c++中动态分配以及删除内存的指令new,delete
2019-07-12 14:05:52对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在 编译程序的时候安排好的,这带来了极大的不变,如数组必须大开小用,指针必须指向一个已经存在的变量或对象,对于不能确定需要占用多少内存的情况,...关于new和delete运算符
对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在 编译程序的时候安排好的,这带来了极大的不变,如数组必须大开小用,指针必须指向一个已经存在的变量或对象,对于不能确定需要占用多少内存的情况,动态内存分配解决了这个问题。
new 和delete运算符是用于动态内存分配和撤销内存的运算符。
一、new用法
1.开辟单变量地址空间
使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值。
new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有标识符名。
一般使用格式:
格式1:指针变量名=new 类型标识符;
格式2:指针变量名=new 类型标识符(初始值);
格式3:指针变量名=new 类型标识符 [内存单元个数];
说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;但是格式2在内存分配成功后,同时将一初值存放到该内存单元中;而格式3可同时分配若干个内存单元,相当于形成一个动态数组。例如:
1)new int; //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址。int *a = new int 即为将一个int类型的地址赋值给整型指针a
2)int *a = new int(5) 作用同上,但是同时将整数空间赋值为5
2.开辟数组空间
对于数组进行动态分配的格式为:
指针变量名=new 类型名[下标表达式];
delete [ ] 指向该数组的指针变量名;
两式中的方括号是非常重要的,两者必须配对使用,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的指针,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。
delete []的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。
请注意“下标表达式”不必是常量表达式,即它的值不必在编译时确定,可以在运行时确定。
一维: int *a = new int[100]; //开辟一个大小为100的整型数组空间
二维: int **a = new int[5][6]
三维及其以上:依此类推.
一般用法: new 类型 (初值)
二、delete用法-
删除单变量地址空间
int *a = new int;
delete a; //释放单个int的空间 -
删除数组空间
int *a = new int[5];
delete []a; //释放int数组空间
三、使用注意事项 -
new 和delete都是内建的操作符,语言本身所固定了,无法重新定制,想要定制new和delete的行为,徒劳无功的行为。
-
动态分配失败,则返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。
-
指针删除与堆空间释放。删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放),释放堆空间后,p成了空指针。
-
内存泄漏(memory leak)和重复释放。new与delete 是配对使用的, delete只能释放堆空间。如果new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。
-
动态分配的变量或对象的生命期。我们也称堆空间为自由空间(free store),但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放,往往会出错。
-
要访问new所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问。
用new和delete可以动态开辟和撤销地址空间。在编程序时,若用完一个变量(一般是暂时存储的数据),下次需要再用,但却又想省去重新初始化的功夫,可以在每次开始使用时开辟一个空间,在用完后撤销它。/关于c++中new和delete的用法/
#include
using namespace std;int main()
{
//动态分配和删除单变量地址空间
int *a=new int;
*a=100;
cout<<*a<<endl;
delete a;
//动态分配以及删除数组空间
int *b=new int [5];
b[0]=1;
b[1]=2;
cout<<b[0]<<b[1]<<b[2]<<b[3]<<endl;
delete []b;
}
-
-
Linux——进程间通信(共享内存,消息队列)
2020-11-18 18:44:37共享内存通过内核对象,使得不同的进程在自己的虚拟地址空间上分配一块空间映射到相同的物理内存空间上,这块物理内存空间对于映射到上面的每个进程而言都是可以访问的。(临界资源) 共享内存就是允许两个不相关的...共享内存
共享内存通过内核对象,使得不同的进程在自己的虚拟地址空间上分配一块空间映射到相同的物理内存空间上,这块物理内存空间对于映射到上面的每个进程而言都是可以访问的。(临界资源)
共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。共享内存的使用
与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h 中。
1.获取或创建内核对象,并且制定共享内存的大小(系统分配物理空间是,按照页进行分配)int shmget(key_t key, int size, int flag);
只是创建内核对象,并申请物理空间
key_t key
:与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget()函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.
不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget()函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget()函数的返回值),只有shmget()函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。
int size
:size以字节为单位指定需要共享的内存容量int flag
:falg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。
2.分配自己虚拟地址空间映射到共享内存的物理空间上
void *shmat(int shmid, void *addr, int flag);
shmid
:shmid是由shmget()函数返回的共享内存标识。void *addr
:addr指定共享内存连接到当前进程中的地址位置,通常为NULL,表示让系统来选择共享内存的地址。int flag
:flag是一组标志位,通常为0。
调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.
3.断开当前进程与共享内存的映射
int shmdt(const void *addr);
4.操作共享内存的方法
int shmctl(int shmid, int cmd, struct shmid_t *buf);
int shmid
:shmid是shmget()函数返回的共享内存标识符。int cmd
:command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段struct shmid_t *buf
:buf是一个结构指针,它指向共享内存模式和访问权限的结构
因为有连接计数器,除非最后一个进程与该共享段断开连接,则删除该共享段。否则,并不会真正删除该共享段,但是共享内存的内核对象会被立即删除,不能使用shmat方法与该段连接。
一个进程调用该方法删除后,不会影响之前已经和该共享存储段连接的进程#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> #include"sem.h" #define READSEM 1 #define WRITESEM 0 int main() { int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT); assert(shmid != -1); char *ptr = (char*)shmat(shmid,NULL,0); assert(ptr != (char*)-1); int initVal[] = {1,0}; int semid = SemGet(1234,intVal,2); assert(semid != -1); //A进程写 while(1) { SemP(semid,WRITESEM); printf("Input:"); fgets(ptr,127,stdin); SemV(semid,READSEM); if(strncmp(ptr,"end",3) == 0) { break; } } shmdt(ptr); exit(0); }
#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> #include"sem.h" #define READSEM 1 #define WRITESEM 0 int main() { int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT); assert(shmid != -1); char *ptr = (char*)shmat(shmid,NULL,0); assert(ptr != (char*)-1); int initVal[] = {1,0}; int semid = SemGet(1234,intVal,2); assert(semid != -1); //B进程读 while(1) { SemP(semid,READSEM); if(strncmp(ptr,"end",3) == 0) { break; } int i = 0; for(;i < strlen(ptr) - 1;i++) { printf("%c",toupper(ptr[i])); fflush(stdout); sleep(1); } printf("\n"); SemV(semid,WRITESEM); } shmdt(ptr); exit(0); }
从上面的代码中我们可以看出:
共享内存是最快的IPC,在通信过程中少了两次数据的拷贝。(相较于管道)命令管理共享内存
查看 ipcs -m
删除 ipcrm -m shmid消息队列
管道和共享内存:字节流数据
消息:数据报(类型+数据)
队列:优先级队列
可以指定类型来读取,在相同类型下,按照先进先出的顺序
消息队列的操作:1.创建和访问一个消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflag);
key
:某个消息队列的名字,用ftok()产生msgflag
:有两个选项IPC_CREAT和IPC_EXCL,单独使用IPC_CREAT,如果消息队列不存在则创建之,如果存在则打开返回;单独使用IPC_EXCL是没有意义的;两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回。返回值
:成功返回一个非负整数,即消息队列的标识码,失败返回-1
2.把一条消息添加到消息队列中
int msgsnd(int msqid, const void *msqp, size_t msqsz, int msqflg);
msgid
:由msgget函数返回的消息队列标识码msqp
:指针指向准备发送的消息msqsz
:msqp指向的消息的长度(不包括消息类型的long int长整型)msgflg
:默认为0返回值
:成功返回0,失败返回-1
消息结构一方面必须小于系统规定的上限,另一方面必须以一个long int长整型开始,接受者以此来确定消息的类型
struct msgbuf {
long mtye;
char mtext[1]; };3.从一个消息队列接受消息
ssize_t msgrcv(int msqid, void *msqp, size_t msqsz, long msqtyp, int msqflg);
参数:与msgsnd相同
返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1
如果指定的空间不能将消息的数据完全存储下来,则本次读取不成功4.消息队列的控制函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid
:由msgget函数返回的消息队列标识码cmd
:有三个可选的值
IPC_STAT 把msqid_ds结构中的数据设置为消息队列的当前关联值
IPC_SET 在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值
IPC_RMID 删除消息队列返回值
:成功返回0,失败返回-1
msg.h
#program once typedef struct msgbuf { long mtype; char mtext[128]; }MsgBuf;
发送
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> #include<unistd.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #include"msg.h" int main(int argc,char *argv[]) { //argv[1]:type //argv[2]:data if(argc < 3) { printf("Please input type and data\n"); exit(0); } MsgBuf mess; memset(&mess,0,sizeof(mess)); sscanf(argv[1],"%d",&mss.mtype); strcpy(msss.mtext,argv[2]); int msgid = msgget((key_t)1234,IPC_CREAT | 0664); assert(msgid != -1); msgsnd(msgid,&mess,strlen(mess,mtext),0); exit(0); }
接收(注意读取问题)
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> #include<unistd.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #include"msg.h" int main(int argc,char *argv[]) { if(argc < 2) { printf("please input type\n"); exit(0); } MsgBuf mess; memset(&mess,0,sizeof(mess)); long type = 0; sscanf(argv[1],"%d",&type); int msgid = msgget((key_t)1234,IPC_CREAT | 0664); assert(msgid != -1); msgrcv(msgid,&mess,127,type,0);//改成5看看结果,消息截取 printf("type:%d,data :%s\n",mess.mtype,mess.mtext); exit(0); }
-
内存优化之非内置类型的全局变量的初始化过程
2020-02-29 22:46:57G++在编译时,为这些非内置类型的全局变量,在.bss节预留了内存空间;同时在.init_array节安排了全局变量的构造函数 在程序运行时,在mmap将数据段映射进入内存之后,调用位于.init_array节的全局对象的构造函数,将... -
java初学者笔记之2.内存解析
2008-07-22 12:12:00内存结构引入:在程序运行时,对象是如何放置安排计的呢?算机内存是如何分配的呢?弄清楚这些问题将会使你的java水平提高一大块 计算机中6个可以存储数据的地方 寄存器 由于它位于计算机的处理器的内部所以它是... -
《深入Java虚拟机》导读之六: 类和对象生命周期
2010-09-09 18:00:00类的生命周期http://hi.csdn.net/attachment/201009/9/0_12840264063r7D.gifjvm通过加载(loading), 链接(linking), 初始化... 准备是为该类型分配内存空间; 解析是把指向常量池的符号引用变成直接引用, 而 -
c++中new和delete的使用方法
2017-10-09 21:13:51对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态... -
new和delete的用法(摘自百度知道)
2018-07-22 17:26:10对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态... -
C++:new和delete的用法
2017-04-18 21:13:35对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态... -
newDelete用法
2018-08-29 17:19:00对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态... -
new && delete 使用
2018-08-01 22:29:37对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态... -
面试题----new/delete、malloc/free、new[]/delete[]的用法与区别以及new的三种使用方法
2017-07-18 09:29:37new和delete的用法: 对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用... -
c++中new和delete的用法
2016-05-10 15:04:16对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态... -
C++中new,delete详解及其与malloc,free的区别
2016-11-03 14:52:44对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态... -
new和delete详解
2017-08-29 09:21:50对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量和对象。对于不能确定需要占用多少内存的情况,动态内存... -
多线程的基础知识
2018-02-09 12:38:461. 线程由两部分组成:&...线程总是在某个进程环境中创建,系统从进程的地址空间中分配内存,供线程的栈使用.线程运行:操作系统为每一个运行线程安排一定的CPU时间—时间片,系统通过一种循环的方式为线程提供时间片,线... -
最权威的C++教程_C++_Primer_Plus中文第五版+C++_Primer中文第四版(都含源码+习题)(共4分卷)分卷1
2010-06-23 17:33:55本章讨论如何创建多文件程序,介绍分配内存的各种方式、管理内存的各种方式以及作用域、链接、名称 空间,这些内容决定了变量在程序的哪些部分是可见的。 第10章:对象和类 类是用户定义的类型,对象(如变量)是... -
最权威的C++教程_C++_Primer_Plus中文第五版+C++_Primer中文第四版(都含源码+习题)(共4分卷)分卷2
2010-06-23 17:47:19本章讨论如何创建多文件程序,介绍分配内存的各种方式、管理内存的各种方式以及作用域、链接、名称 空间,这些内容决定了变量在程序的哪些部分是可见的。 第10章:对象和类 类是用户定义的类型,对象(如变量)是... -
最权威的C++教程_C++_Primer_Plus中文第五版+C++_Primer中文第四版(都含源码+习题)(共4分卷)分卷3
2010-06-23 18:03:39本章讨论如何创建多文件程序,介绍分配内存的各种方式、管理内存的各种方式以及作用域、链接、名称 空间,这些内容决定了变量在程序的哪些部分是可见的。 第10章:对象和类 类是用户定义的类型,对象(如变量)是... -
c++ 程序设计
2019-01-20 22:53:377.1.7 动态分配和撤销内存的运算符new和delete 7.2 共用体 7.2.1 共用体的概念 7.2.2 对共用体变量的访问方式 7.2.3 共用体类型数据的特点 7.3 校举类型 7.4 用typedef声明类型 习题 第3篇 基于对象的程序设计 第8... -
AIC的Java课程7-12章
2007-07-29 20:55:11 掌握通过引用同类型对象(指针)实现链表,动态分配内存空间构建链表。 理解运用继承和组合两种重用方式定义堆栈和队列,知道两种重用方式的适用场合。 理解使用递归方法构建二叉排序树,前序、... -
传智播客扫地僧视频讲义源码
2019-04-03 21:54:2212_直接通过内存标号操作内存空间_课堂答疑 13_中午课程回顾 14_内存四区基本原理_全局区案例理解 15_内存四区_堆栈案例理解 16_课堂答疑_理解指针的关键关键在内存 17_vs20102013上配置#系列快捷方式 18_栈的属性和... -
21天学通C++ (中文第五版)
2010-06-23 16:57:038.3.1 使用关键字new来分配内存 8.3.2 使用关键字delete归还内存 8.4 再谈内存泄漏 8.5 在自由存储区上创建对象 8.6 删除自由存储区中的对象 8.7 访问数据成员 8.8 在自由存储区中创建成员数据 8.9 this指针 ... -
Visual C++ 2005 入门经典 详细书签版
2013-02-02 16:39:438.1.3 析构函数与动态内存分配 372 8.2 实现复制构造函数 375 8.3 在变量之间共享内存 377 8.3.1 定义联合 377 8.3.2 匿名联合 379 8.3.3 类和结构中的联合 379 8.4 运算符重载 379 8.4.1 实现重载... -
Visual C++ 2005入门经典--源代码及课后练习答案
2013-02-02 16:42:048.1.3 析构函数与动态内存分配 372 8.2 实现复制构造函数 375 8.3 在变量之间共享内存 377 8.3.1 定义联合 377 8.3.2 匿名联合 379 8.3.3 类和结构中的联合 379 8.4 运算符重载 379 8.4.1 实现重载... -
Visual C++ 2010入门经典(第5版)--详细书签版
2012-10-16 21:04:298.1.3 析构函数与动态内存分配 366 8.2 实现复制构造函数 369 8.3 在变量之间共享内存 370 8.3.1 定义联合 371 8.3.2 匿名联合 372 8.3.3 类和结构中的联合 372 8.4 运算符重载 373 8.4.1 实现重载的运算符 ...