首先还是复习,二维数组和指针:
Int a[2][3]; int (*p)[3] = a;
这里输出p和*p是一样的,p ==a整个数组的首地址,*p == a[0]是这个一维数组的首地址,p++就是移动int[3]个字节,(*p)++移动了4字节,但是这样是错的,相当于a[0]++。
注:如果希望通过形参修改实参的值,传递的必须是指针,函数内部一定使用的是指针指向的内存,而不是直接使用指针。如果是希望改变指针指向的值,那么形参得是二维指针。二维数组作为参数的时候,被解析为指向数组的指针。
内存管理:
auto(自动变量,C语言默认,处于栈中)
register(寄存器变量不能取地址)
静态变量(程序执行期间一直存在,而且内存地址不会改变,不能在代码块外面访问它;只初始化一次,第二次的时候不会再赋值;和全局变量相比,static只能在定义变量的文件内部使用,文件外部不可以使用,但是可以通过函数来间接访问static变量)能用局部变量解决的,就不要使用全局变量。
extern(外部变量,extern int i引用一个外部变量,和int i一样)
内存四区:是独立的区域
代码区:变量的定义不是代码,代码区不可读写,只能执行;常量字符串;假如给main函数的地址赋个其他值,会出现错误,是CPU读写的。编译完之后是固定不变的。
静态区:全部静态变量和全局变量都在这儿存放
栈区: 所有的局部自动变量,先进后出,当一个变量超出作用域的时候,自动弹出,系统自动管理,但是空间比较小;char a[n],C语言是不允许在栈里面定义动态大小的数组,在编译时必须确定栈的大小。递归层数不能过多,否则会出现栈溢出。函数参数从右到左入栈。栈顶从高地址向低地址增长。
堆区: 远远大于栈,但是内存需要自己处理。
int *p = malloc(4*sizeof(int));//单位是字节,32位系统默认是3G,要及时释放内存,清空memset(p,0,10*sizeof(int))。可以根据用户的输入来决定堆里面动态数组的大小。
Calloc(10,sizeof(int))//分配10个int,同时初始化为0.
Realloc(src,10) //重新分配10个空间,原来的内容还在
如果堆没有可分配的内存,malloc返回null,在有些linux嵌入式系统下,继续调用realloc分配内存,操作系统重新启动
注:在一个函数里面,如果函数的返回值是一个指针,就不能返回局部变量,就是不可以使用栈分配的地址,因为函数结束的时候,指针就收回了,这样得到的是一个失效地址。通过函数的参数为指针分配内存,得使用二级指针。
注:对于字符串常量和静态变量,是在静态区存放的,所以返回的地址是有效的,在程序运行期间是一直有效的。
在linux下,查看栈和堆的内存使用,在proc下面可以看到进程的PID号,然后进去之后,可以看到好多信息,栈的大小事88KB,
每个线程都有自己专属的栈。
内存的最小单位是字节,在malloc的时候,不是按你自己的意愿来分配的,这个时候操作系统是根据内存页的方式来管理内存,分配内存的时候,除非一个页的单位已满,就会开辟一个新的页。
硬盘的存储也是字节为最小单位,但是有很多个扇区,每个扇区包含很多字节。数组在内存中的存放是下标从小到达,内存也是从小到大存放,不管在堆、栈和全局变量中都是这样。和栈的变量不同,先声明的地址大,符合栈的概念。
将dest的值追加到src后面,这时候最容易造成数组的溢出,所以这时候适合使用动态数组,在函数中申请一个新的内存。使用堆动态的来分配内存。
void mystrcat(char **src,char *dest)
{
Int src_len = strlen(*src);
Int dest_len = strlen(dest);
Char *p = malloc(src_len + dest_len +1); //效率不高
Memset(p,0,src_len + dest_len +1);
Strcpy(p,*src);
/*
Char *p = realloc(*src,src_len + dest_len +1);
*/
Int i;
For(i=0;i<dest_len;i++)
*src[src_len + i] = dest[i]
Free(*src);
*src = p;
} |