欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言学习

程序员文章站 2022-07-15 09:07:33
...

C语言学习

一.悬空指针(一个指针的指向对象已被删除)与野指针(未初始化的指针)

int main()
{
    char*dp = NULL;
    for(i=0;i<1;i++) 
    {
        char c;
        dp =&c;
	}
/* 注意c的声明周期 */
/* dp 此时为悬空指针 */
}
//这里for循环只是说明c是局部变量
void f()
{
    char*dp;
    /* dp 未初始化,是野指针 */
}
//有时也把野指针和悬空指针通称悬空指针

二.内存泄漏

  1. 指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
  2. 程序中动态分配的存储空间,在程序执行完毕后需要进行释放。没有释放动态分配的存储空间而造成内存泄漏,是使用动态存储变量的主要问题。一般情况下,开发人员使用系统提供的内存管理基本函数,如malloc、recalloc、calloc、free等,完成动态存储变量存储空间的分配和释放。但是,当开发程序中使用动态存储变量较多和频繁使用函数调用时,就会经常发生内存管理错误,例如:
    • 分配一个内存块并使用其中未经初始化的内容;
    • 释放一个内存块,但继续引用其中的内容;
    • 子函数中分配的内存空间在主函数出现异常中断时、或主函数对子函数返回的信息使用结束时,没有对分配的内存进行释放;
    • 程序实现过程中分配的临时内存在程序结束时,没有释放临时内存。内存错误一般是不可再现的,开发人员不易在程序调试和测试阶段发现,即使花费了很多精力和时间,也无法彻底消除。

三.未定义行为(语言标准未做规定的行为)

1.定义:

未定义行为,无法预估Runtime会发生什么,同时,标准也从没要求编译器判断未定义行为,所以这些行为有编译器自行处理,在不同的编译器可能会产生不同的结果,又或者如果程序调用未定义的行为,可能会成功编译,甚至一开始运行时没有错误,只会在另一个系统上,甚至是在另一个日期运行失败。

2.类型:

  1. 解引用空指针、非法迭代器或者尾后迭代器都是未定义行为

  2. 访问一个无效数组索引,下标越界

  3. 函数体之内定义的变量:未初始化(uninitialized),其值undefined。

  4. 未定义的结果当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的。

    signed char c[2] = '256'; // c2的值是未定义
    
  5. 释放一个非new分配的内存,或者将相同的指针值释放多次,其行为是未定义的。

  6. string s(s2,pos2); // s是string s2从下标pos2开始的字符拷贝,如果pos2>s2.size(),构造函数的行为未定义

  7. 试图比较两个无关地址是未定义行为

  8. 算术表达式有可能产生未定义的结果,除数为0

  9. 对于那些没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为

    int i=0;
    cout<" "<<++i<// 未定义// 编译器可能先求++i的值,再求i的值;也可能先求i的值,再求++i的值。注意与print函数的区别。
    *beg=toupper(*beg++); // 未定义对有符号数进行左移操作可能会改变符号位的值,因此是一种未定义的行为。移位运算符右侧的运算对象一定不能为负,而且值必须严格小于结果的位数,否则就会产生未定义的行为。
    
  10. 使用static_cast将void*转换成其他类型指针,必须确保转换后所得的类型就是指针所指的类型。类型一旦不符,将产生未定义行为。

    double d;
    void * p=&d;
    double *dp= static_cast < double *>(p);
    
  11. const_cast只能改变运算对象的底层const,如果对象本身是一个常量,使用const_cast执行写操作就会产生未定义行为。

  12. 不要使用get初始化另一个智能指针或为智能指针赋值,否则将会产生两个独立的shared_ptr指向相同的内存,这将产生未定义行为。

  13. delete []p;如果忘记[],其行为是未定义的。 删除单一对象的指针加[],其行为也是未定义的。

  14. 在两个异常同时存在的情况下,程序若不是结束执行就会导致未定义行为。

  15. 当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果是未定义的。实际执行时通常发生的是对象的derived成员没有被销毁。

  16. 溢出;

四.辨析几个概念

  1. int *p[3] 是一个数组,有3个成员,其成员是指向整型的指针。
  2. int (*P)[3]是一个指针,它指向一个有3个整型的数组。
  3. int *p(int);一个形参为int型的返回值为int型指针值的函数。
  4. int *(*p(int))定义一个指针指向一个形参为int型的返回值为int型指针值的函数。
  5. int *(*p(int))[3]定义三个指针指向一个形参为int型的返回值为int型指针值的函数。
相关标签: C语言学习