看了一些大佬对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 <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmemery(char *p)
{
p = "hello ";
}
main()
{
char * str;
getmemery(str);
printf("%s\n",str)
}
这个程序的意图是把这个“hello” 字符串常量的首地址赋值给p ,再打印这个字符串常量,但是这个就涉及到函数参数传递的问题了,这里传的是str 这个指针,指针指向的一定是地址(即指针的值是地址),这里想把字符串的地址赋值给他,就改变了这个指针变量的值。C语言参数都是值传递的,然而他想通过改变指针的数值来达到目的显然是不行的。
这里可以用经典的数值交换来对比下
#include <stdio.h>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmemery(char **p)
{
*p = "hello ";
}
main()
{
char ** str;
char * s = "temp"; //
str = &s; //一定要有这个初始化
getmemery(str);
printf("%s\n",*str);
}
这里传进去一个二级指针,指向的还是指针,通过改变他指向的那个指针所指向的地址,成功改变了地址。。。
这里我脑袋又抽了,指针常量作为局部变量的时候生存周期是怎样的,从结果来看应该是函数结束也不会消失,但是想弄清楚,搜了一下果然有大佬分析了这个问题:
#include<stdio.h>
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、程序代码区—存放函数体的二进制代码