C语言内存管理和参数传递浅析

看了一些大佬对C语言细节的分析,觉得自己的C真是白学了,从新开始记录C语言的学习。
首先看一个例子:(内存管理

char *getmemery()  
{  
    char p[] = "hello world!";  
    return p;  
}  

main()  
{  
    char *str = NULL;  
    str = getmemery();  
    printf("%s\n",str);  
}  

这个程序的输出会是Segmentation fault 。 (分段错误) ,原因是这是一个指针函数,返回的是一个地址,p[] 是局部变量,在程序结束后就被释放了,但是却返回了它的地址,结果就是指向了一个未知的世界。
现在修改下:

char *getmemery()  
{  
    char *p = "hello world!";  
    return p;  
}  

main()  
{  
    char *str = NULL;  
    str = getmemery();  
    printf("%s\n",str);  
}  

这里只是改动了 p[] –> *p ,将数组变成了指针。原因在于 给数组初始化的时候 “hello world!” 相当于 一个个字符组成的初始化列表,它的每个字符挨个放入数组中相应的地址,然而当用它付给指针的时候,它是相当于一个字符串常量的,为什么相当于常量呢,我觉得可以这样理解:一个指针只能指向一个地址,而把这一串字符付给它,如果不是常量的话 ,岂不是只有第一个字符有效。所以他是常量,把他的首地址赋给指针。

如果用数组的话,也是可以的,将这个数组用static定义为静态变量,这样这个数组存储的地方是静态存储区 ,而不是栈区,生存周期是程序开始到结束。 static的作用可以主要分为两种,1)限制变量作用区域,2)限制生存周期。


再来看看函数传参的问题:

include  
#include  
#include  

void getmemery(char *p) 
{ 
    p = "hello "; 
}
 main()
{
    char * str;
    getmemery(str); 
    printf("%s\n",str)
}

这个程序的意图是把这个“hello” 字符串常量的首地址赋值给p ,再打印这个字符串常量,但是这个就涉及到函数参数传递的问题了,这里传的是str 这个指针,指针指向的一定是地址(即指针的值是地址),这里想把字符串的地址赋值给他,就改变了这个指针变量的值。C语言参数都是值传递的,然而他想通过改变指针的数值来达到目的显然是不行的。
这里可以用经典的数值交换来对比下

#include 
void change(int *a,int *b)
{
    int c ;
    c = *a;
    *a = *b;
    *b = c;
}
void main()
{
    int m = 1,n = 2;
    change(&m,&n);
    printf("%d,%d",m,n);
}

可以看到,这个例子成功的原因是因为他改变的是指针指向的地址里面的数值,而不是改变地址本身。这里我脑袋抽风了,如果必须得改变地址怎么办呢,然后我想到了既然指针指向的地址的内容可以更改,那么我再用一个指针来指向这个地址不就得了,二级指针就可以解决了:

#include  
#include  
#include  

void getmemery(char **p) 
{ 
    *p = "hello "; 
}
 main()
{
    char ** str;
    char * s = "temp";  //
    str = &s;   //一定要有这个初始化
    getmemery(str); 
    printf("%s\n",*str);
}

这里传进去一个二级指针,指向的还是指针,通过改变他指向的那个指针所指向的地址,成功改变了地址。。。
这里我脑袋又抽了,指针常量作为局部变量的时候生存周期是怎样的,从结果来看应该是函数结束也不会消失,但是想弄清楚,搜了一下果然有大佬分析了这个问题:

#include
void a()
{
  char *s1="hello";
  printf("%0x\n",s1);
  s1="world";
}
void b()
{
   char *s2="hello";
   printf("%0x\n",s2);
   s2="world";
}
int main()
{
 char *s3=(char *)0;
 a();
 b();
 s3="hello";
 printf("%0x\n",s3);
 return 0;
}

结果是三个地址一样,这样就说明了常量一旦申明就不会消失,直到程序结束。
最后附上不同变量的存储形式和周期:
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码

你可能感兴趣的:(C基础)