一、错误处理
1. 通过函数的返回值表示错误
1) 返回合法值表示成功,返回非法值(无效值)表示失败。
<0表示出错,>=0表示正确
例:获取文件长度函数:
long getSize(const char *filePath){
if(filePath == NULL){
return -1;
}
FILE *fp = fopen(filePath,"r");//通过函数返回值 来判断 函数调用是否成功
if(fp == NULL){
return -1;
}
fseek(fp,0,SEEK_END);//调整文件读写位置到文件末尾
long off = ftell(fp);//距离文件开始位置多少个字节
fclose(fp);
return off;
}
2) 返回有效指针表示成功, 返回空指针(NULL/0xFFFFFFFF)表示失败。
例:字符串拷贝函数
char *strcpy(char *dest,const char *src){
if(dest == NULL || src == NULL){
return NULL;
}
char *pdest = dest;
while((*dest++ = *src++)!='\0');
return pdest;
}
3) 返回0表示成功,返回-1表示失败
例:文件拷贝函数
int copyFile(const char* srcPath,const char *destPath){
FILE *fsrc = fopen(srcPath,"r");
if(fsrc==NULL){
return -1;
}
FILE *fdest = fopen(destPath,"w");
if(fdest == NULL){
fclose(fsrc);
return-1;
}
char c = 0;
while(fread(&c,1,1,fsrc)>0){
fwrite(&c,1,1,fdest);
}
fclose(fsrc);
fclose(fdest);
return 0;
}
int main(int argc,char *argv[]){
if(argc < 3){
printf("%s srcFile destFile\n",argv[0]);
return -1;
}
if(copyFile(argv[1],argv[2])==0){
printf("success");
}
if(copyFile(argv[1],argv[2])==-1){
printf("false");
}
}
4) 永远成功,如:printf
例:两个数相加
int add(int a,int b){
return a+b;
}
void show(int a){
printf("%d\n",a);
}
2. 通过errno表示错误
当调用函数失败时,基本上都会对errno进行赋值
char *strerror(int errnum);//通过错误码来获得错误信息
需要加入#include<errno.h>头文件
1) 根据errno得到错误编号
2) 将errno转换为有意义的字符串
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main ()
{
FILE* fp = fopen ("errno", "r");
if (! fp)
{
printf ("fopen: %d\n", errno);
printf ("fopen: %s\n", strerror (errno));
printf ("fopen: %m\n");
perror ("fopen");
return -1;
}
fclose (fp);
return 0;
}
3) 重要:errno是全局变量,只有当函数失败时才会修改,在函数执行成功的情况下不会被修改,因此不能以errno非零,作为发生错误判断依据,即不能通过 if(errno != 0) 来判断函数出错。
4) errno是一个全局变量,其值随时可能发生变化。
环境变量
char **environ;
char *getenv(const char *name);//根据name获取环境变量的值,如果该name不存在则返回NULL
int putenv(char *s);//以"name=value"的形式来添加或者设置环境变量 返回0表示成功,非0失败,如果环境变量name不存在则添加,name存在则覆盖value
int setenv(const char *name,const char *value,int overwrite);如果环境变量name不存在则添加,overwrite这个参数无意义,如果环境变量name存在, overwrite=0时,放弃修改,overwrite!=0时 覆盖name所对应环境变量的value,返回0表示成功,非0失败
int unsetenv(const char *name);//删除环境变量
int clearenv(void);//清空环境列表 environ = NULL
进程映像
1. 程序是保存在磁盘上的可执行文件,广义就是编写的代码
2. 运行程序时,需要将可执行文件加载到内存,形成进程,进程在内存空间的中布局就是进程映像,每个进程都有4G的虚拟内存, 4G进程地址空间分成两部分:[0, 3G)为用户空间, 如某栈变量的地址0xbfc7fba0=3,217,554,336,约3G;[3G, 4G)为内核空间。
3. 一个程序(文件)可以同时存在多个进程(内存)。
同一个程序可以同时运行多次
一个程序运行一次 可以有多个进程
一个程序同时运行多次,每运行一次至少产生一个进程
4. 进程在内存空间中的布局就是进程映像,从低地址到高地址依次为:
代码区(text):
可执行指令、字面值常量、具有常属性的全局和静态局部变量。只读。
数据区(data):
初始化的全局和静态局部变量。
BSS区:
未初始化的全局和静态局部变量。
进程一经加载此区即被清0。
数据区和BSS区有时被合称为全局区或静态区。
堆区(heap):
动态内存分配。从低地址向高地址扩展。
栈区(stack):
非静态局部变量,
包括函数的参数和返回值。
从高地址向低地址扩展。
堆区和栈区之间存在一块间隙,一方面为堆和栈的增长预留空间,同时共享库、共享内存等亦位于此。
命令行参数与环境区:
命令行参数和环境变量。
程序运行出现核心已转储 段错误的原因:
对内存的越权访问,或试图访问没有映射到物理内存的虚拟内存,将导致段错误。
malloc每运行一次,至少分配33页的内存
malloc(size) 其实申请了 size+12个字节的大小,12个字节:动态内存块的控制信息
malloc申请的动态内存会额外多出12个字节用于控制该块内存,如果该12个字节数据被意外修改将导致在释放该内存时引发段错误
例:
char *p = malloc(1);
*p 能够访问
p[1-12] 控制信息块,如果修改,后续会产生段错误
p[13- (33*4096-1)],可以访问,未分配都操作不会出现错误的区域,该区域会分配给下一次malloc申请
所谓的申请和释放动态内存其实是建立或者取消虚拟内存和物理内存之间的映射关系
brk
int brk(void *addr); 调整指针位置 释放内存 成功返回0
sbrk
void *sbrk(intptr_t increment) 返回上一次调用sbrk/brk函数内存地址的一下一个地址
intptr_t 相当于int
increment 可正可负 可为0
increment > 0 相当于申请了increment个字节的动态内存 返回该内存的首地址
increment = 0 获得之前申请动态内存的最后一个内存的下一个地址
increment < 0 相当于释放了|increment|个字节的动态内存 返回的地址是上一次调用sbrk函数返回的地址的末尾的一下个地址
失败返回 -1 (void *)-1