精华内容
下载资源
问答
  • KMSAN(KernelMemorySanitizer)KMSAN是Linux内核未初始化内存使用的检测器。 目前正在开发中。 联系人:[受电子邮件保护]代码带有KMSAN补丁内核分支可用KMSAN(KernelMemorySanitizer)KMSAN是Linux内核未初始化...
  • 如图:我定义了一个简单的函数传递操作,在另一处赋值,结果vs报错“使用未初始化的局部变量” ![图片说明](https://img-ask.csdn.net/upload/202003/25/1585145597_662090.png) ==========================...
  • C++学习之内存的分配和初始化

    千次阅读 2018-10-06 13:46:50
    C++定义了2个运算符来分配和释放动态内存。... // pi指向一个动态分配的、未初始化的无名对象 默认情况下,动态分配的对象是默认初始化,内置类型(int, double)或组合类型(struct)的对象的值是未定义...

    C++定义了2个运算符来分配和释放动态内存。new分配内存,delete释放内存。

    1. 使用new动态分配和初始化对象

    在自由空间分配的内存是无名的,new返回一个指向分配的对象的指针。

    	int *pi = new int; // pi指向一个动态分配的、未初始化的无名对象
    

    默认情况下,动态分配的对象是默认初始化,内置类型(int, double)或组合类型(struct)的对象的值是未定义的,十分危险。类类型(vector、string)对象使用默认构造函数进行初始化。

    	string *ps = new string; // string初始化为空
    	int *pi = new int; // pi指向一个未初始化的int
    

    使用圆括号对对象进行初始化

    	int *q1 = new int(1024);  // q1指向的对象的值为1024
    	string *ps = new string(10, '9'); // 初始化为"9999999999"
    

    使用列表初始化(花括号)

    	vector<int> *pv = new vector<int>{0,1,2,3,4,5,6,7,8,9};
    

    对动态分配的对象进行值初始化,只需在类型名之后跟一对空括号即可

    	string *ps1 = new string; // 默认初始化为空string
    	string *ps2 = new string();  // 值初始化为空string
    	int *pi1 = new int;   // 默认初始化,*pi1的值未定义
    	int *pi2 = new int(); // 值初始化为0, *pi2为0
    

    对于定义了自己的构造函数的类类型来说,对象都会通过默认构造函数进行初始化。而内置类型则不具备这个功能,需要我们对其初始化。
    若我们提供了一个括号包围的初始化器,则可以使用auto根据初始化器来自动推断我们想分配的对象的类型。由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才可以使用auto:

    auto p1 = new auto(pi1);    //  与pi1类型相同的对象,该对象用pi1进行初始化,p1是int *
    auto p2 = new auto(pi1,pi2);  //  ERROR:括号中只能由单个初始化器
    

    动态分配的const对象必须初始化

    用new分配const对象。

    	const int *pci = new const int(1024);   // 分配并初始化一个const int
    	const string *pcs  = new const string;
    

    若系统内存耗尽,则内存分配失败,new会抛出bad_alloc的异常。可以使用定位new来阻止抛异常:

    	int *p3 = new (nothrow) int; // 若分配失败,new返回一个空指针
    

    2、释放动态内存

    动态内存使用完毕后,使用delete将内存归还给系统。delete执行2个动作:销毁给定的指针指向的对象;释放对应的内存。传递给delete的指针必须指向动态分配的内存或一个空指针。

    	delete p3;
    

    const对象的值不能被改变,但是其本身可以被销毁。

    	const int *pci = new const int(1024);  // 分配并初始化一个const int
    	delete pci;  // 释放一个const对象
    

    动态对象的生存期

    对于一个由内置指针管理的动态对象,直到被显式释放之前都是存在的。
    动态内存的使用存在的三个常见问题:

    1. 忘记delete内存
    2. 使用已经释放的对象
    3. 同一内存多次释放

    delete一个指针之后,指针值变得无效,但是很多机器上依然保存着动态内存的地址,指针变成了空悬指针:指向一块曾经保存数据但是现在已经无效的内存的指针。
    避免空悬指针:在指针即将离开其作用域之前释放掉关联的内存,若需要保留指针,可以在delete之后将nullptr赋予指针,表明指针不指向任何对象。

    使用智能指针可以避免这些问题。

    3、shared_ptr和new的结合

    若不初始化一个智能指针,则它会初始化为一个空指针,可以用new返回的指针来初始化智能指针。

    	shared_ptr<double> p4;  // shared_ptr指向一个double
    

    接收指针参数的智能指针构造函数是explicit的,不能将一个内置指针转换为智能指针,必须采用直接初始化:

    	shared_ptr<int> p5 = new int(42); // ERROR
    	shared_ptr<int> p5(new int(42)); // p2指向一个值为42的int
    

    一个返回shared_ptr的函数不能在其返回语句中隐式转换一个普通指针:

    shared_ptr<int> clone(int p){
    	return new int(p);   // error: 隐式转换为shared_ptr<int>
    }
    
    shared_ptr<int> clone(int p){
    	return shared_ptr<int>(new int(p)); // 正确:显式用int*创建shared_ptr<int>
    }
    

    一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放它关联的对象,可以将智能指针绑定到一个指向其他类型的资源的指针上。
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 在c语言中,我们通常都是用memset来初始化一个结构体。但是在c++中,使用memset来初始化一个结构体,却...memset初始化的时候,是将一块固定大小的内存置空,而string,vector等的大小并不是固定的,而是会动态变化...

    在c语言中,我们通常都是用memset来初始化一个结构体。但是在c++中,使用memset来初始化一个结构体,却经常得不到正常的结果。这是为什么呢?
    问题其实是出在了,结构体成员变量类型上了,因为使用了非ADT类型(大致就是c++一些特有的类型,如string,vector等等)。memset初始化的时候,是将一块固定大小的内存置空,而string,vector等的大小并不是固定的,而是会动态变化的,所以当大小发生了变化,这是memset的初始化便是未定义的行为了,所以有时会导致严重的bug。这也是c++在提供接口时,必须使用ADT类型的原因。比如要把string换成char数字,vector换成数组等。
    那么在c++中如何初始化这些特殊的结构体呢?在c++中struct和class除了权限属性不一样外,其他具有一样的属性。
    所以可以模仿class初始化来初始化这些结构体。

        struct stStudent
        {
        	public:
        	stStudent()
        	{
        		age = 0;
        	}
    	
    	string name;//name不初始化不会影响结果,因为string是有构造函数的,
    						//在stStudent构造的时候,是会调用string的构造的
    	int age;
    };
    
    展开全文
  • C++认识初始化

    千次阅读 2015-08-29 18:32:55
    初始化是程序设计中一项重要的...使用未初始化的变量(或内存区域)是程序产生bug的重要原因之一。正确理解和使用初始化操作,要弄清以下几个问题。1.什么是初始化在给初始化下定义前。先弄清楚两个概念。申明与定义。

    代码编译运行环境:VS2017+Win32+Debug


    初始化是编码过程中的重要操作,往往由于被忽略,导致使用未初始化的变量(或内存区域),将程序置于不确定的状态,产生各种bug,严重影响的程序的健壮性。正确地理解和使用初始化操作,是对每一位合格程序员的基本要求。

    1.什么是初始化

    在给初始化下定义前。先弄清楚两个概念:申明与定义。编程过程中申明与定义包括变量、函数和类型的申明和定义。具体含义参见我的另一篇blog:申明与定义的区别

    变量的申明:指明变量所属类型与变量名称的过程。如:extern int a;

    变量的定义:指明变量所属类型、变量名称、分配空间以及完成初始化操作的过程。如:int a=1;或者int a(1);

    变量的初始化:为数据对象或变量赋初值的做法。可以看出,初始化是变量定义的一部分。定义一个变量时,一定会包括变量的初始化操作。

    观察以上概念的定义,可以清楚地看出变量的申明、定义和初始化的区别与联系,请牢记在心,切勿混淆。

    2.初始化与赋值的区别

    初始化与赋值是不同的操作。初始化是使变量(对象)第一次具备初值的过程。而赋值则是改变一个已经存在的变量(对象)的值的过程。

    对于基本数据类型的变量来说,变量的初始化与赋值的实现方式上差不多,如:

    	int i=5;     //初始化
    	int i; i=5;  //赋值
    

    都是利用赋值符号将特定的值写入变量i中。但对于构造数据类型的对象,初始化和赋值的操作在实现方式上有很大的区别。以类的对象的举例如下:

    #include <iostream>
    using namespace std;
    
    class String
    {
    private:
    	char* s;
    	unsigned int len;
    	unsigned int capacity;
    
    public:
    	String(char* str)
    	{
    		len=strlen(str);
    		capacity=len+1;
    		s=new char[capacity];
    		strcpy(s,str);
    	}
    
    	String& operator=(char* str)
    	{
    		if(strlen(str)+1>capacity)
    		{
    		    delete[] s;
    		    capacity=strlen(str)+1;
    		    s=new char[capacity];
    		}
    		strcpy(s,str);
    		len=strlen(str);
    		return *this;
    	}
    	void show()
    	{
    		cout<<s<<endl;
    	}
    };
    
    int main(int argc,char* argv[])
    {
    	String name("John");
    	name.show();
    	name="Johnson";
    	name.show();
    	
    	getchar();
    }
    

    这个程序实现了非标准的String类。该对象实现的功能有C风格的字符串初始化、C风格的字符串的赋值和输出的功能。

    对于对象来说,初始化语句的语法形式与赋值不同。赋值只能通过赋值操作符“=”进行,对象的初始化必一般采用在圆括号中给出初始化参数的形式来完成。

    赋值操作是使用默认的按位复制的方式或者是由重载operator=操作符来完成,而对象的初始化必须由构造函数来完成。

    在以上String类的设计中,构造函数只需要根据传入的参数字符串的长度来分配空间就可以了,而赋值操作符重载函数则需要考虑传入的参数字符串的长度,然后决定是否要释放原来空间并申请新的空间。可见,构造函数和赋值操作的逻辑也是有很大的差别。

    C++中,基本类型的变量也可以当做对象来处理,因此基本类型的变量可以采用类似默认构造函数的形式进行初始化。例如int i(2);和double d(2.5);等。

    3.未初始化带来的问题

    C/C++规定了变量的定义一定要完成初始化操作,通常情况下,并没有规定初始化操作必须由程序员来完成,如果编码者在定义变量时未赋予有意义的初始值,那么变量的初始化则由编译器来完成,变量的初始值将处于不确定状态。使用初始值不确定的变量,会带来巨大的风险,例如使用未初始化的指针变量往往会导致程序崩溃。如果一个指针既不为空,也没有被设置为指向一个已知的对象,则这样的指针称为悬挂指针(Dangling Pointer),有时也称为野指针(Wild Pointer),即“无法正常使用”之意。如果使用,则给程序的运行带来不稳定性和不可预知的错误。

    #include <iostream>   
    using namespace std;   
    void f(int *p);  
    int main()   
    {   
        //int a = 10;   
        int *i;  
        //i = &a;   
        f(i);  
        cout<<*i;  
        return 0;  
    }   
    void f(int *p)
    {  
        cout<<p;  
        if(p!=0)
        {
            *p = 100;
    	}
    } 
    

    当控制函数执行到f()中时候,f()不能判断指针的合法性,将会产生很严重的错误,但编译可以通过。最好的解决方法是使用指前,将其指向一个对象,即去掉注释部分。

    4.编译期与初始化相关的错误

    在某些时候,初始化强制由编码者来完成,没有初始化会导致编译错误。如:
    (1)定义常变量,必须同时完成初始化;
    (2)由于引用本质是指针常量,所以定义引用时也必须同时初始化;
    (3)定义构造类型的常对象时,相应的构造函数必须存在。考察如下程序:

    class A
    {
    	int num;
    public:
    	void show()const
    	{
    		cout<<num<<endl;
    	}
    };
    
    int main(int argc,char* argv[])
    {
    	const A a;
    	a.show();
    }
    

    此程序定义了一个常对象a,然后调用其常函数show()。但是类A并没有显示定义参数为空的构造函数,而编译器也并非在未显示定义任何构造函数时一定为类合成默认的构造函数,即使合成了默认的构造函数,对成员变量初始化的值也是随机的,没有意义的。所以,在很多编译器(如GCC)下,以上程序无法通过编译,但在VC++中,程序能够通过编译,但运行结果没有任何意义。所以,如果要生成常对象,必须显示定义其对应的构造函数,完成对象的初始化工作。

    还有一种情况,由于程序的控制结构可能导致某些变量无法初始化,也将引起编译错误。最常见的就是goto语句与switch语句。见如下程序:

    int main(int argc,char* argv[])
    {
    	int i;
    	cin>>i;
    	if(i==8)
    		goto disp;
    	int j=9;
    disp:
    	cout<<i+j<<endl;
    	getchar();
    }
    

    这个程序在很多编译器下无法通过编译,即使通过编译,运行时也会出现问题。原因是goto语句会跳过变量j的初始化语句,即使j被分配空间(很多编译器集中分配临时变量的空间),也无法获得初值。

    再看另外一例子:

    int main(int argc,char* argv[])
    {
    
    	int i;
    	cin>>i;
    	switch(i)
    	{
    		case 1:int j=5;break;
    		case 2:cout<<"Hello"<<endl;
    	}
    }
    

    GNU C++和VC++下编译时都会报类似于“j的初始化操作由case标签跳过”的错误。由于C++没有强制switch语句的各case分支使用break,所以在一个case分支中定义的变量是可能被其他分支的语句使用的。由于case分支被执行的随机性,无法保证变量获得初值。解决办法:
    (1)除非只有一个case分支,否则不要在case分支中定义局部变量;
    (2)可以将case分支至于代码块中,用大括号包围,限制case分支定义的变量的作用域在代码块作用域中。
    修改为:

    case 1:
    { 
    	int j=5;
    	break;
    }
    

    参考文献

    [1] C++高级进阶教程[M].陈刚.武汉大学出版社.2.8.关于初始化.P75-P79
    [2] C++中的作用域与生命周期
    [3]悬挂指针.百度百科

    展开全文
  • 对于函数局部变量,编译器不会为基本类型赋予默认初始值,新手经常会使用未初始化的指针访问内存,导致程序崩溃。对于类对象,编译器将使用类的默认构造函数对对象进行初始化。而在java中,对于方法的局部变量,java...

    java尽力保证:所有变量在使用前都能得到恰当的初始化

    ①函数/方法局部变量的初始化

    在C/C++中,变量的初始化还是得依赖于程序员的自觉性。对于函数局部变量,编译器不会为基本类型赋予默认初始值,新手经常会使用未初始化的指针访问内存,导致程序崩溃。对于类对象,编译器将使用类的默认构造函数对对象进行初始化。而在java中,对于方法的局部变量,java以编译时错误来保证变量在使用前都能得到恰当的初始化。

    void f(){
        int i ;
        i ++ ; //Error- - i not initialized
    }

    尽管java编译器也可以为方法的局部变量赋予一个初值,但局部变量未初始化更有可能是程序员的疏忽,采用默认值反而会掩盖这种错误。

    ②类数据成员的初始化

    C++程序员刚接触到java的类时可能会很不习惯,java类的数据成员居然可以在定义时就初始化:

    public class InitialValues{
    boolean bool = true;
    char ch = ‘x’;
    int i = 999;
    double d = 3.14;
    Depth d = new Depth();
    }

    这种方式在java中称为指定初始化。在指定初始化之前,编译器还会为这些数据成员进行默认初始化,实际上是把刚分配的对象内存都置零。

    // java数据成员的默认初始化
    public class InitialValues{
        boolean t;  //flase
        char c;     //[]
        short s;    //0
        byte b;   //0
        int i; //0
        long l; //0
        float f; //0.0
        double d; //0.0
    }

    在对象里定义一个引用,且不将其初始化时,默认初始化为null。这种默认初始化的实现是,在创建(new)一个对象时,在堆上对对象分配足够的空间之后,这块存储空间会被清零,这样就自动把基本类型的数据成员都设置成了默认值。默认初始化动作之后,才执行指定初始化。也就是说下面的i经历过被初始化为0后,再赋值为999的过程。

    public class InitialValues{
    int i = 999;
    }

    java也可以使用构造函数来进行初始化,但构造函数的初始化无法阻止指定初始化和默认初始化的进行,而且总是在它们之后,才会执行构造函数初始化。总结起来说,java中数据成员的初始化过程是:

    • ① 先默认初始化
    • ② 进行定义处的初始化(指定初始化)
    • ③ 构造函数初始化

    C++禁止在定义数据成员时就进行指定初始化,而且C++也没有默认初始化。有人会问,下面这段代码不是默认初始化了吗?

    class Test {
    public:
        int i;
        double b;
        char ch;
    };
    int main()
    {
        Test *t = new Test();
        cout << t->b; //输出0
        cout << t->i; //输出0
        cout << t->ch; //输出[]
        return 0;
    }

    这实际上是C++的默认构造函数进行的构造函数初始化。当类没有构造函数时,编译器会为类声明并实现一个默认构造函数,默认构造函数将数据成员初始化为默认值。所以C++数据成员的初始值,只能依赖:

    • 成员初始化列表
    • 构造函数

    成员初始化列表与java的指定初始化相似,也是在进入构造函数函数体之前,对数据成员进行的初始化。在数据成员的初始化顺序上,java与C++倒是一致的,定义的顺序决定了初始化的顺序。

    关于static成员的初始化

    在java中不允许有static的局部变量,只能够有static的域,如static数据成员。static数据成员在对象被第一次创建时才会被实例化,而且只实例化一次。例如:

    class StaticTest{
        int _a ;
        StaticTest (int a ){
            _a = a;
            System.out.println("StaticTest("+a+")");
        }
    }
    
    class Test{
        static StaticTest st1= new StaticTest(1);
        StaticTest nonSt = new StaticTest(0);     
        static StaticTest st2= new StaticTest(2);
    }
    
    public class Main {
        public static void main(String[] args) throws Throwable {
            Test t = new Test(); //直到这个时候, st1与st2才会被实例化
        }
    }
    
    //程序输出
    StaticTest(1)
    StaticTest(2)
    StaticTest(0)
    

    从输出可以看出在java中,初始化顺序是这样的:先初始化静态数据成员,再初始化非静态数据成员。

    在C++中,static数据成员必须在类之外初始化。关于C++的static,http://www.cnblogs.com/QG-whz/p/4473384.html 我以前的总结放在这里挺合适的。

    回到篇首的话。

    java尽力保证:所有变量在使用前都能得到恰当的初始化(《java编程思想》)

    java在变量初始化上,普通变量以编译错误、成员变量以默认初始化等手段,尽力使所有的变量在使用前都可得到初始化,在安全性上大大强于C++。

    610439-20170316164953963-1467453066.jpg

    展开全文
  • Capacity:vector容器的容量指的是我们一共给vector容器预留的总的内存空间,当然了总的内存空间包括“没有被使用的(初始化前不可以访问)“和”已经使用了的(这部分内存已经被初始化)“; Reverse成.
  • C++中,对象的定义和初始化是两码事。 普通对象的初始化 例如,如果写出这样的代码: int x; 编译器将仅仅为变量x在栈上分配一块...因此,仅定义的一个数组是仅定义而初始化的;但是STL中的各种容器都实现了自
  • 有堆区,栈区,BBS区(未初始化数据区),数据区(初始化了),代码区。 其中: 栈区:由编译器自动分配释放,存放函数参数值、返回值和局部变量,在程序运行过程中实时分配和释放,栈区由操作系统自动管理,...
  • C++中数组定义及初始化

    万次阅读 2018-03-04 11:26:29
    C++中数组的定义和初始化 一维数组: ...3、动态 int* array =new int [100] ,在堆上分配内存,使用new关键字分配的内存,返回值都是指针,所以数组声明为 int* 类型。需要调用delete函数 进行删除:...
  • 一切从白纸开始:未初始化的变量是C和C++程序中错误的常见来源。养成在使用内存之前先清除的习惯,可以避免这种错误,在定义变量的时候就将其初始化. 关于未初始化变量有一个常见误解,它们会使程序崩溃,但事实...
  • 指针使用前先被初始化指向一块特定的内存地址...可以使用NULL这个C++特殊值来标记没被初始化的指针。每新建一个指针,你应该先将它的设置为NULL,这样可以方便以后检查 int *p_int = NULL; //可能设置,也可能不...
  • 不仅如此,在调用对象自身构造函数之前,编译器已经对名字空间级对象做了”零初始化”,即将内存值全部置零(不同于未初始化)。因此,绝对不能假定名字空间级对象初始化顺序,更不能让名字空间级对象初始化过程...
  • 最常见的内存访问bug有内存泄漏(memory leak),内存管理的错误使用(incorrect use of memory management),缓冲区溢出(buffer overrun)和读取未初始化的内存(reading unnitialized memory)。 内存泄漏:在...
  •  Valgrind 可以用来检测程序是否有非法使用内存的问题,例如访问未初始化的内存、访问数组时越界、忘记释放动态内存等问题。在 Linux 可以使用下面的命令安装 Valgrind: 1 2 3 4 5 ...
  • 一切从白纸开始,未初始化的变量是C和C++程序中错误的常见来源。养成在使用内存之前先清除的习惯,可以避免这种错误,在定义变量的时候就将其初始化。 按照C和C++相同的低层高效率传统,通常并不要求编译器初始化...
  • 文章目录1 构造函数和析构函数1.1 对象初始化和清理背景1.2 构造函数和析构...①对象或变量使用前若未初始化,则程序运行结果存在不确定性; ②对象或变量使用完毕后若未及时清理,则会导致内存泄露。 1.2 构造函数
  • c++内存泄露检测

    2018-04-25 08:57:54
    (1)使用未初始化的内存 提示为 Use of uninitialised value of size (2) 对释放后内存的读/写 Invalid read of size 3) 对已分配内存块尾部的读/写 Invalid read of size 1 (4) 内存泄露 1 bytes ...
  • 我们知道当变量被定义之后,编译器就会在内存中预留一段存储空间。C++和C语言一样,当这段内存空间被预留出来之后,如果...未初始化的变量是危险的,因为当你不小心使用了一个未初始化,也未赋值的变量,程序的运行结
  • C++的内存管理

    2018-08-26 17:40:16
    静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。 堆:使用new分配的内存块。 栈:在执行函数时...
  • c++ 内存使用

    2010-07-07 11:13:00
    用户存储区:1.代码区12.常量区23.数据区 3.1 静态存储区----存放编译时安排静态和外部变量3 3.2 动态存储区 3.2.1 (堆)栈区----自动型变量,断点地址等,将会在... //1全局未初始化区 int main() {  int b; 
  • C++的内存分区介绍

    2020-12-31 07:46:54
    C++的内存划分为栈区、堆...未初始化的全局变量和静态变量是在相邻的空间中。 说明:全局变量和静态全局变量的存储方式是一致的,但是其区别在于,全局变量在整个源代码中都可以使用,而静态全局变量只能在当前文件中有
  • ID: 457 类型:变量 结构:简单 状态:草稿 ...代码使用没有初始化的变量,这可能导致不可预知的结果。...在某些诸如C和C++中,...在其他语言或条件中,根据程序的逻辑,显式初始化的变量可...
  • 1.典型的内存分布情况: ... 未初始化数据段[.bbs]: 未初始化的全局变量/未初始化的(局部/全局)静态变量.  4). 堆(从低地址往高低至增长): 所使用的局部变量还是在栈上,内容则在堆上.手动释放或

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 424
精华内容 169
关键字:

c++使用未初始化的内存

c++ 订阅