C语言 形参/实参的传递+ 指针+ 内存认知/使用

目标:

  1. 指针的理解
  2. 变量的生存周期及作用范围
  3. 形参/实参体会
  4. 良好的动态内存申请和释放

程序的内存分配

一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的,全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

下边用一段代码解释下:

int a = 0; 全局初始化区  
char *p1; 全局未初始化区  
main(){  
     int b;      //栈  
     char s[] = "abc";    //栈  abc\0在常量区
     char *p2;      //栈  
     char *p3 = "123456";   // 123456\0在常量区,p3在栈上。  
     static int c =0//全局(静态)初始化区  
     p1 = (char *)malloc(10);    //堆  
}  

GetMemory1: 指针作为参数传入,并修改,并没有修改指针指向的内容

void GetMemory1(char *p)    
{    
    p = (char *)malloc(100);    
}    

void Test1(void)    
{    
    char *str = NULL;    
    GetMemory1(str);      
    strcpy(str, "hello world");    
    printf(str);  //str一直是空,程序崩溃     
}    

结果:
C语言 形参/实参的传递+ 指针+ 内存认知/使用_第1张图片

分析:
毛病出在函数GetMemory1 中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把 _p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory1就会泄露一块内存,因为没有用free释放内存。Test1中调用GetMemory1时,函数参数为str的副本不是str本身。

GetMemory2: malloc 配对使用 free,注意释放指针,并将free后的指针赋值为NULL

void GetMemory2(char **p, int num)    
{    
    *p = (char *)malloc(num);    
}    

void Test2(void)    
{    
    char *str = NULL;    
    GetMemory2(&str, 100);    
    strcpy(str, "hello");      
    printf(str);        
}    

结果:输出hello
分析:动态分配的内存不会自动释放;
没有测试是否成功分配了内存,应该有if (*p == NULL) { ……} 之类的语句处理内存分配失败的其情况。

GetMemory3

char * GetMemory3(void)    
{      
     char p[] = "hello world";    
     return p;    
}    
void Test3(void)    
{    
     char *str = NULL;    
     str = GetMemory3();        
     printf(str);    
}    

结果:输出乱码。
分析:字符数组p存在于栈空间,是局部变量,函数返回后,内存空间被释放,因此输出无效值。字符数组的值是可以修改的,例如p[0] = ‘t‘。

GetMemory4

char *GetMemory4(void)    
{    
    char *p = "hello";    
    return p;    
}    

void Test4(void)    
{    
    char *str = NULL;    
    str = GetMemory4();   
    cout<< str << endl;    
}    

结果:输出hello
分析:p指向的是字符串常量,字符串常量保存在只读的数据段,是全局区域,但不是像全局变量那样保存在普通数据段(静态存储区)。无法对p所指的内存的内容修改,例如p[0] = ‘y;这样的修改是错误的。

野指针

野指针只会出现在像C和C++这种没有自动内存垃圾回收功能的高级语言中, 所以java或c#肯定不会有野指针的概念. 当我们用malloc为一个指针分配一个空间后, 用完这个指针,把它free掉,但是没有让这个指针指向NULL或某一个特定的空间。如上面程序一样,将str进行free后,只是释放了指针所指的内存,但指针并没有释放掉,此时指针所指的是垃圾内存;这样的话,if语句永为真,if判断无效。delete也存在同样的问题。
防止产生野指针:
(1)指针变量一定要初始化为NULL,因为任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的。(2)当free或delete后,将指针指向NULL。通常判断一个指针是否合法,都是使用if语句测试该指针是否为NULL。

参考:
一定要弄懂GetMemory

你可能感兴趣的:(C)