一、内存管理
1、你的数据放哪里?
栈空间:局部变量、函数形参、自动变量(调用后释放)
堆空间:malloc、realloc、calloc分配空间
数据段:bss:保存未初始化的全局变量
rodata:常量
.data(静态数据区):全局变量、static修饰变量(程序结束后释放)
2、内存的分配方式
(1)从全局数据区分配
(2)在栈上创建
(3)在堆上创建
3、常见的内存错误及对错
(1)内存分配未成功,却使用了它
(2)内存分配虽然成功,但是尚未初始化就引用它
(3)内存分配成功并已经初始化,但操作越过了内存的边界
(4)忘记了释放内存,造成了内存泄漏
(5)释放了内存却继续使用它
4、段错误的调试方法
详情请戳网址:
http://blog.csdn.net/wzhcalex/article/details/51866913
二、函数
函数:(1)维护性好,函数要有独立的功能
(2)迭代开发
(3)复用性好
函数的调用:
通过函数名找到函数的入口地址
给形参分配内存空间
传值:把实参变量对应的空间的值传给形参
执行函数体里的语句
函数返回并释放空间
函数的三要素:函数名、函数形参、函数返回值
1、函数名(自注释性--可读性)
1、 一定要写函数声明!
命名函数名要形成自己的风格!
命名函数名要体现功能
动词+名词:get_user、insert_user
陌生词语不要进行缩写
先写调用,再写实现
2、函数名也是指针常量,保存函数存放的地址,即函数的入口地址
2、函数参数的传递
(1)传参要用对应的类型来接
(2)代码示例:
#include<stdio.h>
void swap_int(int *left,int *right)
{
int temp;
temp = *left;
*left = *right;
*right = temp;
}
int main()
{
int left = 5;
int right = 6;
printf("before swap:left = %d\tright = %d\n",left,right);
swap_int(&left,&right);
printf("after swap:left = %d\tright = %d\n",left,right);
return 0;
}
如果只是传值而不是传地址的话,函数返回释放空间,则right、left的值并没有发生交换
这就引来了一个问题:什么时候传值,什么时候传地址呢?
读(不修改)实参变量对应的内存空间的值时,传实参变量名
写(修改)实参变量对应的内存空间的值时,传实参变量的地址(上示例代码即为传地址)
(3)传入参数&传出参数:
如果函数接口有指针参数,既可以把指针所指向的数据传给函数使用(称为传入参数),也可以由函数填充指针所指的内存空间,传回给调用者使用(称为传出参数)
代码示例:
(4)主函数的形参:(命令行参数)
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
if(argc != 2)
{
printf("please input a param!\n");
exit(1);
}
printf("argc = %d\n",argc);
int i;
for(i = 0; i < argc; i++)
{
printf("argv[%d] = %d\n",i,atoi(argv[i]));
}
return 0;
}
3、函数返回值
(1)不能返回局部变量的地址:局部变量存放于栈空间,函数调用后就释放了
(2)exit(1):结束整个程序,头文件为:#include<stdlib.h>
1:是返回给用户
通常用来做异常处理,判断哪个exit( )退出,用打印信息的方法
echo $?
(3)return 0:返回给操作系统,标志主函数结束
是规范要求,能够提高操作系统的运行效率,如果不写return 0系统要检查是否是正常退出等等
4、企业级函数编写
(1)函数的编码规范:
1、对所调用函数的错误返回码要仔细、全面地处理。
2、防止将函数的参数作为工作变量。
说明:将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
3:函数的规模尽量限制在200行以内。
说明:不包括注释和空格行。
4、一个函数仅完成一件功能。
5、为简单功能编写函数。
说明:虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。
6、不要设计多用途面面俱到的函数。
说明:多功能集于一身的函数,很可能使函数的理解、测试、维护等变得困难。
7、尽量不要编写依赖于其他函数内部实现的函数。
说明:此条为函数独立性的基本要求。由于目前大部分高级语言都是结构化的,所以通过具体语言的语法要求与编译器功能,基本就可以防止这种情况发生。但在汇编语言中,由于其灵活性,很可能使函数出现这种情况。
8、避免设计多参数函数,不使用的参数从接口中去掉。
说明:目的减少函数间接口的复杂度。
9、检查函数所有参数输入的有效性。
(2)函数入口参数检查
比如:void * find_string(char *src,char *head,char *tail)
{
if(src == NULL || head == NULL || tail == NULL )
{
printf(“the param is error!”)
exit(1);
}
}
(3)函数返回值异常处理
详情见上述exit( )
5、函数指针、函数指针数组
(1)函数指针
#include<stdio.h>
int add(int a,int b)
{
return a+b;
}
int main()
{
int num1 = 5;
int num2 = 6;
int (*p_func)(int,int);
p_func = add; //函数入口地址,倾向于此种
//p_func = &add;整个函数的地址
int result = p_func(num1,num2);
printf("result = %d\n",result);
return 0;
}
(2)回调函数
#include<stdio.h>
int add(int a,int b)
{
return a+b;
}
int sub(int a,int b)
{
return a-b;
}
int mul(int a,int b)
{
return a * b;
}
int cal(int (*p_func)(int,int),int a,int b)
{
return p_func(a,b);
}
int main()
{
int num1 = 5;
int num2 = 6;
int i;
int (*func_array[3])(int,int);
func_array[0] = add;
func_array[1] = sub;
func_array[2] = mul;
for(i = 0; i < 3; i++)
{
printf("result = %d\n",func_array[i](num1,num2));
}
return 0;
}
回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
6、可变参数
未完待续