C++类和动态内存分配

这篇具有很好参考价值的文章主要介绍了C++类和动态内存分配。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

类的动态内存分配和释放

C++能够在程序运行时决定内存的分配,而不是只在编译阶段,因此,就可以根据程序的需要,而不是根据一系列严格的存储类型规则来使用内存,C++使用new和delete运算符来动态控制内存,但是,在类中使用这些运算符会导致许多新的问题,在这种情况下,析构函数就是必不可少的,下面我们通过一个小程序来看一看这些问题,这个程序是在类中使用new和delete来存储释放字符串

我们将函数声明写在一个头文件,即stringbad.h

在头文件里定义了一个类,名为Stringbad

#ifndef STRINGBAD_
#define STRINGBAD_

#include<iostream>

using namespace std;

class Stringbad
{
        private:
                char *str;//字符串地址
                int len;    //长度
                static int num_strings; //字符串个数
        public:
                Stringbad(const char *s);
                Stringbad();
                ~Stringbad();
                friend ostream & operator<<(ostream &os, Stringbad & t);
};

#endif
~            

在类函数定义文件中,即stringbad.cpp

#include"stringbad.h"
#include<cstring>

int Stringbad::num_strings = 0;

Stringbad::Stringbad(const char *s)
{
        len = strlen(s);
        str = new char[len+1];
        strcpy(str,s);

        num_strings++;
        cout<<" "<<num_strings<<": \""<<str<<".\""<<endl;
}

Stringbad::Stringbad()
{
        len = 4 ;
        str = new char[4];
        strcpy(str,"C++");
        num_strings++;
}

Stringbad::~Stringbad()
{
        cout<<" \""<<str<<"\"object deleted."<<endl;
        --num_strings;
        cout<<" "<<num_strings<<" left. "<<endl;
        delete [] str;
}

ostream &operator<<(ostream & os, Stringbad & t)
{
        os<<t.str;
        return os;
}

注意:

在我们stringbad.h文件中,我们有一个静态变量num_strings用来存储字符串的个数,但是我们确是在stringbad,cpp文件中才对这个变量进行了初始化,这是因为不能在类声明中初始化静态成员变量,因为声明描述的只是如何分配内存,但是实际上并没有进行分配内存,我们可以使用这种格式来创造对象,从而分配和初始化内存

但是对于静态的类成员,我们可以在类声明之外使用单独的语句来进行初始化,因为静态类成员是单独存储的,不是类对象的组成部分,所有的类对象共用一个num_strings,并且,在我们stringbad,cpp文件中的初始化,并没有使用static关键字,只是指出了类型及使用了作用域解析运算符

初始化是在类方法的文件中,而不是在类声明的文件里,因为类声明位于stringbad.h头文件中,程序可能将头文件包含在其他几个文件里,如果在头文件中对其进行初始化,可能会出现多个初始化的副本,从而引发错误

我们在主函数文件main.cpp中,测试函数传递类的引用以及类的值 

#include"stringbad.h"
#include<iostream>

void callme1(Stringbad & st);
void callme2(Stringbad st);

int main(void)
{
        Stringbad  a1("Hello world");
        Stringbad  a2("Good morning");
        Stringbad a3;

        cout<<a1<<a2<<a3;
        callme1(a1);
        callme2(a2);
}

void callme1(Stringbad & st)
{
        cout<<"String passed by reference: "<<st<<endl;
}
void callme2(Stringbad st)
{
        cout<<"String passed by value: "<<st<<endl;
}

当我们只调用callme1时,即按引用传递

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

程序能够正常运行

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

但是,当我们调用按值传递时,程序却出现了错误

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

此时却出现了问题,在这次运行中,我们同时调用了callme1和callme2函数,即按引用传递类和直接传递类来进行输出,我们按引用进行传递时,并没有发生任何的问题,但是按值进行传递时,却出现了问题,在我们调用callme2时,我们将a2传递给了我们的callme2,在我们callme2中就使用了形参st去接收它,这就出现了我们类进行赋值的一个操作,这就是相当于是

Stringbad st = a2;
//又等价于
Stringbad st = Stringbad(a2);

两者都是类的对象,这时候不知道会去调用哪个构造函数,因为我们只有一个默认构造函数以及一个参数为const char*的构造函数,而并没有参数是类的对象的构造函数,这时候我们使用一个类的对象去初始化另一个类的对象时,编译器就会生成一个复制的构造函数

Stringbad(const Stringbad &);

因为我们使用了编译器自己创建的默认构造函数后,也会去调用析构函数,而我们的析构函数中会使用delete去释放内存,我们自己创建了3个类的对象,因此调用了3次构造函数(其中一次是默认构造函数),而我们进行值传递时,又调用了一次编译器创建的构造函数,但是,我们只有前面3次使用new来创建内存空间,而进行释放时会调用4次析构函数,即会使用4次delete来释放空间,这就导致了段错误,并且自动生成的复制构造函数并不会去更新num_strings,因此会搞乱计数方案

特殊成员函数

复制构造函数

我们上面所说的问题其实就是由特殊的成员函数引起的,这些成员函数都是编译器进行自动定义的,就Stringbad而言,这些函数的行为与我们类的设计不符,C++会自动提供下列的成员函数

1.默认构造函数,如果没有定义构造函数

2.默认析构函数,如果没有定义析构函数

3.复制构造函数

4.赋值运算符

5.地址运算符,隐式地址运算符返回调用对象的地址(this指针的值)

更准确的说,如果程序使用对象的方式要求我们这样做,编译器就是生成上述最后三个函数的定义,例如,如果我们要将一个对象赋值给另一个对象,编译器会提供赋值运算符的定义,可以看出Stringbad类中的问题是由隐式复制构造函数和隐式的赋值运算符引起的,因此,我们可以自己提供一个参数符合的复制构造函数,就不会出现这样的问题,也可以对赋值运算符进行显示的重载,防止其中的某个部分不能使用

在我们stringbad.h和stringbad.cpp中分别添加复制构造函数和赋值运算符重载的声明和定义

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

在我们进行自己添加复制构造函数和赋值运算符重载之后,编译器就不会再进行自动生成,这样我们在进行按值传递时结果也就不会出错,结果如下

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

对于复制构造函数的说明:

复制构造函数:

复制构造函数用于将一个对象复制到新创建的对象中,即用于初始化过程中,包括按值传递参数过程中,而不是常规的赋值过程中,原型通常如下

class_name(const class_name & ); //类名(const 类名 &);

复制构造函数接收一个指向类对象的常量作为参数

复制构造函数的调用时间:

即每当生成了对象的副本,编译器都会调用复制构造函数,即当函数按值传递参数时或者函数返回对象时,都会使用复制构造函数

由于按值传递对象会调用复制构造函数,这样会浪费调用构造函数的时间已经存储新对象的空间,因此应该按引用来传递

赋值运算符

对于赋值运算符重载的说明

在上面复制构造函数的时候我们说过,我们将一个类的对象,传递给我们的st

Stringbad st = a2;
//又等价于
Stringbad st = Stringbad(a2);

在这里可以理解为先使用复制构造函数创建了一个临时的对象,又将临时的对象通过赋值运算符再传递给我们的st,而赋值运算符C++能够自动给我们提供,但是可能会潜在出现问题,为了防止这些问题的发生,我们就需要在原有代码上加上重载的赋值运算符

设计的思路:

1.我们在编写代码时,可能会出现连续的赋值操作,例如 a1 = a2 = a3,因此,在这里,我们需要将函数的返回值也设置为一个类的对象的引用,而我们想要实现赋值操作,左侧为类的对象的引用,右侧也应该为类的对象的引用,因此参数就应该为一个类的对象的引用

2.在我们上述代码中,Stringbad st = Stringbad(a2),我们也可以理解为我们先创建了一个对象st,因此肯定会调用构造函数,而在我们上面所写的构造函数中,我们都使用new来分配了内存空间,因此在对象定义时就已经为其分配了内存空间,而我们想将我们产生的临时的类的对象通过重载的赋值运算符让该对象里的str指向临时的类的对象,因此我们在实现重载赋值运算符时,我们要先将我们产生的默认构造函数分配的空间进行释放,之后再计算str的长度来重新开辟内存空间并让str指向它,再将临时的类的对象拷贝到我们str中,最后返回调用对象的引用

注意:在这种情况下,我们不能传递自身,因为我们先使用了delete,为了防止这个问题,我们需要加上判断条件

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

应用实例

我们可以根据上面的知识将我们的所写的代码进行改进补充,重新体会类的各种用法,并完成一些字符串的基本操作

头文件string.h


#include<iostream>

using namespace std;

class String
{
        private:
                char *str;
                int len;
                static int num_strings;
        public:
                static const int CINLIM = 80;
                String(const char *s);
                String();
                String(const String & s);
                ~String();
                String & operator=(const String & t);
                String & operator=(const char * t);
                const char & operator[](int i) const ;
                int lengh() const //计算长度
                {
                        return len;
                }
                friend bool operator<(const String &str1, const String &str2);
                friend bool operator>(const String &str1, const String &str2);
                friend bool operator==(const String &str1,const String &str2);
                friend ostream & operator<<(ostream &os, String & t);
                friend istream & operator>>(istream &os, String &st);
                //计算对象个数
                static int howmany();
};

#endif
~              

函数定义文件string.cpp

#include"string.h"
#include<cstring>

int String::num_strings = 0;
int String::howmany()
{
        return num_strings;
}

String::String(const char *s)
{
        len = strlen(s);
        str = new char[len+1];
        strcpy(str,s);
        num_strings++;
}

String::String()
{
        len = 0 ; 
        str = new char[1];
        str[0] = '\0';
        num_strings++;
}

String::String(const String & s)
{
        len = s.len;
        str = new char[len + 1];
        strcpy(str,s.str);
        num_strings++;
}
String & String::operator=(const String & t)
{
        //防止自身传递
        if(this == &t)
                return *this;
        delete [] str;
        len = t.len;
        str = new char[len + 1];
        strcpy(str,t.str);
        return *this;
}

String & String::operator=(const char *st)
{
        delete [] str;
        len = strlen(st);
        str = new char[len+1];
        strcpy(str,st);
        return *this;
}

const char & String::operator[](int i)const
{
        return str[i];
}

String::~String()
{
        --num_strings;
        delete [] str;
}

ostream &operator<<(ostream & os, String & t)
{
        os<<t.str;
        return os;
}

bool operator<(const String &str1, const String &str2)
{
        return (strcmp(str1.str, str2.str)<0);
}
bool operator>(const String &str1, const String &str2)
{
        return str2 < str1;
}
bool operator==(const String &str1, const String &str2)
{
        return (strcmp(str1.str, str2.str) == 0 );
}

istream & operator>>(istream &is, String &str1)
{
        char temp[String::CINLIM];
        is.get(temp,String::CINLIM);

        if(is)
                str1 = temp;
        while(is && is.get() != '\n')
                continue;
        return is;
}
      

测试类的文件main.cpp

#include"string.h"
#include<iostream>

const int MAXLEN = 81;

void callme1(String & st);
void callme2(String st);


int main(void)
{
        cout<<"what is your name ?\n";
        String name;
        cin>>name;
        cout<<name<<", please enter a string: "<<endl;
        String saying;
        char temp[MAXLEN];
        cin.get(temp,MAXLEN);
        while(cin && cin.get() != '\n')
                continue;
        saying = temp;
        cout<<"here is you saying : "<<endl;
        cout<<saying<<" "<<saying[0]<<endl;

        String str = "Hello dad";
        if(saying.lengh() < str.lengh())
                cout<<"str is longer \n";
        else
                cout<<"saying long is longer\n";

        if(saying > str)
                cout<<"saying\n";
        else if(saying < str)
                cout<<"saying\n";
        else
                cout<<"=\n";

        cout<<String::howmany()<<endl;
        return 0;
}
              

在构造函数使用new的注意事项

在构造函数使用new和delete注意事项如下:

1.如果我们在构造函数中使用了new,则在析构函数中应该使用delete

2.new和delete必须兼容,new对应于delete,而new[]对应于delete[]

3.如果有多个构造函数,则必须以相同的方式去使用new,要么都带中括号,要么都不带,因为只有一个析构函数,所以的构造函数都必须与它兼容,但是,我们可以在一个构造函数中使用new初始化指针,而在另一个构造函数中将指针初始化为空,因为delete可以用于空指针,无论delete带不带中括号

(C++可以使用0,NULL,还有C++11的关键字nullptr来代表空指针)

4.我们在构造函数中使用new运算符时,应该定义一个复制构造函数,将一个对象初始化为另一个对象,应该要分配足够的内存空间来存储复制的数据,并且复制数据不仅仅是复制地址,还应该更新所有受影响的静态类成员

5.应该定义一个复制运算符,通过深度复制将一个对象传递给另一个对象,这个应该检查自我赋值的情况,释放成员指针以前指向的内存,复制数据而不是数据的地址,并返回一个调用指针的引用

有关返回对象的说明

当成员函数或者独立函数返回对象时,有几种返回方式可以选择,即可以返回对象的引用,指向对象的const引用或者const对象

返回指向const对象的引用

1.使用const引用的常见原因是为了提高效率,如果函数返回(通过调用对象的方法或者将对象作为参数)传递给它的对象,可以通过返回其引用提供效率

2.如果我们返回的是类的对象,那么返回对象会调用复制构造函,而类的引用不会

3.引用指向的对象应该在调用函数执行时存在,即我们返回引用时,不能返回临时变量的引用,因为当函数调用结束后,临时变量已经不存在了,此时返回引用没有意义

4.如果我们的调用的参数为const,返回的是参数中的一个,则返回也应该是const

返回指向非const对象的引用

有两种返回非const对象的引用的情况比较常见,一种是重载赋值运算符,另一种是重载与cout一起使用的<<运算符,返回非const对象的引用即返回值是允许被修改的

返回对象

1.如果被返回的对象是被调用函数中的局部变量,那么不能使用引用的方式去返回它,因为在被调函数执行完毕后,局部的对象会调用析构函数,因此,当控制权回到调用函数时,引用指向的对象将不再存在,在这种情况下,就应该返回对象而不是引用

提示:当进行算术运算符重载时,最后返回的是类的对象

总结

1.如果方法或者函数要返回局部对象,则应该返回对象,而不是对象的引用,在这种情况下,将使用复制构造函数来生成返回的对象

2.如果方法或者函数要返回一个没有公有复制构造函数的类的对象,它必须返回一个指向这种对象的引用,例如ostream类

3.如果有方法或函数可以返回对象,也可以返回对象的引用,则应该返回引用,因为效率高

 

使用指向对象的指针

我们可以使用一个指针来指向一个类的对象,当我们使用对象指针时,需要注意以下几点:

1.使用常规表示法指向对象的指针

String *p;

2.可以将指针初始化为指向已有的对象

String *p = &saying;
//saying是一个类的对象

3.使用new来初始化指针,会创建一个新的对象

String *p = new String;
//在这里,定义一个指向类对象的指针,开辟了String类大小的内存空间
//此时还会去调用相应的构造函数,因为没有参数,所以就会调用默认构造函数
String *p = new String(saying);
//saying为类对象,所以就会去调用参数为类对象的构造函数

4.对类使用new将会调用相应的类构造函数来初始化新创建的对象

String *p = new String("AAA");
//在这里,定义一个指向类对象的指针,开辟了String类大小的内存空间
//此时还会去调用相应的构造函数,即调用参数为字符串的构造函数

根据我们上面所写的代码,使用new来创建,分析过程如下

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

C++prime plus分析:

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

注意:如果我们使用new来创建类的对象时,会调用类构造函数,而当我们显示使用delete对我们开辟的存储类对象空间进行删除时,会去调用析构函数

定位new运算符

定位new运算符能够能够让我们在分配内存时指定内存的位置,但是,将定位new运算符用于类的情况却有所不同,我们下面通过一个测试的例子来看看

#include<iostream>
#include<string>
#include<new>

using namespace std;

const int BUF = 512;

class A
{
        private:
                string words;
                int number;
        public:
                A(const string &str = "A", int n = 0)
                {
                        words = str;
                        number = n;
                        cout<<words<<endl;
                }
                //测试析构函数是否被调用
                ~A()
                {
                        cout<<words<<" destroyed\n";
                }
                void show() const
                {
                        cout<<words<<" , "<<number<<endl;
                }
};

int main()
{
        //先进行了分配一个内存块
        char *p = new char[BUF];
        A *p1 = nullptr;
        A *p2 = nullptr;
        
        //一个类对象使用定位new运算符指定存储的的位置为p
        //调用默认构造函数
        p1 = new(p) A;
        //一个类对象使用new运算符为类A开辟内存并进行初始化
        p2 = new A("B",2);
        
        p1->show();
        p2->show();
        
        //让一个新的类对象也使用类定位运算符指向p
        A *p3 = new(p) A("C",6);
        A *p4 = new A("D",8);
        p3.show();
        p4.show();
        //测试例子
        delete p1;
        delete p3;
        return 0;
}

我们在这个程序中使用new定位运算符已经普通的new运算符来开辟内存空间,并且测试它们调用构造函数和析构函数的情况,运行代码结果如下:

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

我们可以看出,在这个例子中,我们的构造函数被调用了四次,而析构函数只被调用了一次,并且还出现了段错误,这个原因是什么呢?并且如何进行修改?

原因如下:

delete可以与常规的new运算符配合使用,但是不能与new运算符配合使用,因为常规new运算符开辟了内存空间,而定位new运算符是在别处开辟的内存空间

在这里例子里,我们使用了两次定位new运算符,并且让它们指向了同一个内存块(这样做不好),而在最后,我们对这个内存块进行了两次的delete,即我们释放了已经释放过的内存空间,我们在delete p1时,就已经释放了一次,而我们调用delete p2时,又释放了一次,因此就出现了段错误,并且,我们在分配内存空间时,我们使用的是new[],因此,我们释放内存空间时也需要对整个内存块进行释放

我们在使用类对象的指针时说过,当我们使用new去创建类的对象时,会去调用构造函数,只有当我们显式的去使用delete时,程序才会去调用析构函数,因为我们要修改后的代码为

delete []p;
delete p2;
delete p4;

程序运行后的结果为

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

此时我们可以看出,析构函数被调用了两次,即p2和p4调用了析构函数,因为delete [] p释放使用常规new运算符分配的整个内存块,但它并没有为定位new运算符在该内存块中创造的对象调用析构函数,因此,我们需要显式地为定位new运算符创建的对象调用析构函数,显示调用析构函数时,必须知道要销毁的对象,在我们添加上之后

delete p2;
delete p4;
p3->~A();
p1->~A();
delete [] p;

程序的运行结果如下所示:

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

在这里,我们可以看出,p3的C将原来p1里的A给覆盖掉了,这是因为我们两次使用new定位运算符时指向的内存空间的起始位置是一样的,所以后面p3调用时就会将原本的内容进行覆盖,要解决这个问题,我们只需要在p3使用new定位运算符时,让其进行偏移一个类的大小就可以解决

A *p3 = new(p + sizeof(A)) A("C",6);

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

提示:

对于我们使用定位new运算符创建的对象,应该使用与创建顺序相反的顺序进行删除,因为晚创建的对象可能依赖于早创建的对象,并且需要在所有的对象都被销毁后,才能释放用于存储这些对象的缓冲区

成员初始化列表

当我们在类的私有成员里设置了一个const常量,那么我们就必须在构造函数的初始化列表中对其进行初始化,否则就会出错

例如我们有如下的代码:

class A
{
        private:
                int a;
                const int num;
        public:
                A(int anum = 0);
                friend ostream &operator<<(ostream & os, A & t);
};

A::A(int anum)
{
        a = 0;
        num = anum;
}

我们定义了一个类A,当我们尝试去使用这个类去创造一个对象时,编辑就会出错,因为我们在 A 类的构造函数中,没有对常量成员变量 num 进行初始化,并且在构造函数中,我们尝试去修改num的值,而num是一个常量

C++类和动态内存分配,C++ prime plus 学习笔记,c++,开发语言

对于我们的代码而言,num是一个常量,所以我们可以对其进行初始化,但是不能给它赋值,当我们调用构造函数时,对象会将在括号中的代码执行前就被建立,即我们没有进行a = anum;和num = anum + 1;操作之前,我们的程序就已经为a和num分配了内存空间,这个分配内存空间的过程实际就是相当于我们对a和num进行初始化的过程,只是我们没有给它们一个确定的值,而当我们开始执行a = anum;和num = anum + 1;时,我们进行的是常规的赋值操作,而不是初始化,因此,对于const数据成员,必须在执行到构造函数之前就对其进行初始化

C++提供了一种特殊的语法来完成这个过程,即成员列表初始化

成员列表初始化由逗号分隔的初始化列表列表组成,前面要加冒号,它位于参数列表的右括号之后,函数体的左括号之前,因此,我们的代码应该修改为

A::A(int anum) :num(anum)
{
        a = 0;
}

成员初始化列表的要求如下

1.对于我们上面所修改的代码,这种写法只能在构造函数中出现

2.在C++11之前必须用这种格式来初始化非静态const数据成员

3.必须用这种格式来初始化引用数据成员

       文章来源地址https://www.toymoban.com/news/detail-853290.html

到了这里,关于C++类和动态内存分配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态分配内存与释放

    1.malloc malloc()可以找到一个大小合适的块。 内存是匿名的,也就是说,malloc()分配了内存,但没有为它指定名字。 格式如下: double*ptd; ptd=(double*)malloc(30*sizeof(double)); ps:ptd可以看成是一个数组。 malloc()可能分配不到所需的内存。在这种情况下,该函数返回空指针。

    2024年01月17日
    浏览(63)
  • 用指针实现内存动态分配

    导引 :已知:变量在使用前必须被定义且安排好存储空间。且变量有这么一些分类:全局变量、静态局部变量【它们的储存一般是在编译时确定,在程序开始执行前完成。】自动变量【在执行进入变量定义所在的复合语句时为它们分配存储,变量的大小也是静态确定的。临时

    2023年04月09日
    浏览(80)
  • 内存动态分区分配算法

    所谓动态分区分配,就是指 内存在初始时不会划分区域,而是会在进程装入时,根据所要装入的进程大小动态地对内存空间进行划分,以提高内存空间利用率,降低碎片的大小 动态分区分配算法有以下四种: 1. 首次适应算法(First Fit) 空闲分区以 地址递增的次序链接 。分

    2024年02月11日
    浏览(50)
  • 【进阶C语言】动态内存分配

    本章大致内容介绍: 1.malloc函数和free函数 2.calloc函数 3.realloc函数 4.常见错误案例 5.笔试题详解 6.柔性数组 1.malloc函数 (1)函数原型 函数参数: 根据用户的需求需要开辟多大的字节空间,为无符号的字节。 返回值: malloc函数成功开辟内存后,会返回该内存的起始地址,可

    2024年02月07日
    浏览(54)
  • 超详细——动态内存分配+柔性数组

    ☃️个人主页:fighting小泽 🌸作者简介:目前正在学习C语言和数据结构 🌼博客专栏:C语言学习 🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻 我们已经学会的内存开辟方式有:创建一个变量,创建一个数组 我们创建一个整形变量就会申请4个字节,创建个数组就会申请

    2023年04月15日
    浏览(49)
  • 数据结构:动态内存分配+内存分区+宏+结构体

    1.定义一个学生结构体,包含结构体成员:身高,姓名,成绩;定义一个结构体数组有7个成员,要求终端输入结构体成员的值,根据学生成绩,进行冒泡排序。 1.申请一个10个int类型的堆区空间,并实现选择排序(需要导入头文件 #include stdlib.h) 2.用##拼接带参宏的参数 3.宏函

    2024年02月20日
    浏览(44)
  • 深入挖掘C语言 ----动态内存分配

    开篇备忘录: \\\"自给自足的光, 永远都不会暗\\\" 正文开始 C语言提供了一个动态开辟内存的函数; 这个函数向内存申请一块连续可用的空间, 并返回指向这块空间的指针. 如果内存开辟成功, 则返回一个指向开辟好空间的指针 如果开辟失败, 则返回一个NULL指针, 因此malloc的返回值一

    2024年04月28日
    浏览(45)
  • 详解C语言—动态内存分配(二)

    目录 前言: 几个经典的例题题 例一: 例二: 例三: 例四: 例五:   C/C++程序的内存开辟 柔性数组 柔性数组的特点: 柔性数组的使用:  柔性数组的代替: 柔性数组的优势: 小结: 希望在复习完详解C语言—动态内存分配(一)​​​​​​​,阅读此篇文章会进一步提升

    2024年02月08日
    浏览(48)
  • 【C\C++】内存分配 和 动态内存管理方式

    如上图所示:在C/C++中,有几个重要的内存区域,每个区域都有不同的意义和用途。我们从内存分配的角度来分析C++各个内存区域的含义: 栈(Stack) :栈是用于 存储 局部变量、函数参数以及函数调用信息 的内存区域 。它的特点是 自动分配和释放 ,并且遵循 后进先出的原

    2024年02月08日
    浏览(44)
  • C语言 malloc动态内存分配函数

    malloc函数:malloc时动态内存分配函数,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址,就是当数组创建长度不一定 害怕数据存储不够或者不能浪费时间 在使用malloc开辟空间时,使用完成一定要释放空间,如果不释放会造内存泄漏。n在使用ma

    2024年02月07日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包