1 返回非法值表示失败
2 返回空指针表示失败
3 返回-1表示失败
4 错误号和错误信息
5 不能根据错误号判断是否出错
6 内存映射的建立与解除
1 返回非法值表示失败
1.1 问题
如果一个函数的返回值存在确定的合法值域,那么就可以通过返回合法值域以外的值表示函数执行失败。
1.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:返回非法值表示失败
代码如下所示:
Long fsize (const char* path)
{
FILE* fp = fopen(path,”r”);
if(!fp)
return -13;
fseek(fp,0,SEEK_END);
long size = ftell(fp);
fclose(fp);
return size; }
上述代码中,以下代码:
if(!fp)
Return -13;
如果文件打开失败,则返回一个负数,因为合理的文件字节数不可能为负,负数在合理的数域之外表示失败。
1.3 完整代码
本案例的完整代码如下所示:
long fsize (const char* path)
{
FILE* fp = fopen(path,”r”);
if(!fp)
Return -13;
fseek(fp,0,SEEK_END);
long size = ftell(fp);
fclose(fp);
return size;
}
2 返回空指针表示失败
2.1 问题
如果一个函数的返回值是一个指针,那么就可以通过返回空指针(即值为0的指针,通常用NULL宏表示)表示函数执行失败。
2.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:返回空指针表示失败
代码如下所示:
const char* strmax (const char* a, const char* b)
{
return a && b ? (strcmp (a, b) > 0 ? a : b) : NULL;
}
上述代码中,以下代码:
return a && b ? (strcmp (a, b) > 0 ? a : b) : NULL;
当a、b中有一个为假时,返回NULL,表示无法比较两个字符串。
2.3 完整代码
本案例的完整代码如下所示:
const char* strmax (const char* a, const char* b)
{
return a && b ? (strcmp (a, b) > 0 ? a : b) : NULL;
}
3 返回-1表示失败
3.1 问题
返回0表示成功,返回-1表示失败,不输出数据或通过指针型参数输出数据。
3.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:返回-1表示失败
代码如下所示:
int mod (int a, int b, int* c)
{
if (b == 0)
return -1;
*c = a % b;
return 0;
}
上述代码中,以下代码:
if (b == 0)
return -1;
因为a对b求余数,余数可以是负数、零、正数,此时返回几都是合理范围内的值。此时就需要使用指针型参数c输出数据,而用0表示成功,返回-1表示失败。
3.3 完整代码
本案例的完整代码如下所示:
int mod (int a, int b, int* c)
{
if (b == 0)
return -1;
*c = a % b;
return 0;
}
4 错误号和错误信息
4.1 问题
系统预定义的整型全局变量errno中存储了最近一次系统调用的错误编号。该整数形式的错误号可以被转换为有意义的字符串。
4.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:错误号和错误信息
代码如下所示:
#include
#include
#include
#include
int main()
{
char* p = (char*)malloc (-1);
if (! p)
{
fprintf (stderr, “malloc: %s\n”, strerror (errno));
exit (EXIT_FAILURE);
}
scanf("%s", p);
printf("%s", p);
}
上述代码中,以下代码:
if (! p)
{
fprintf (stderr, "malloc: %s\n", strerror (errno));
exit (EXIT_FAILURE);
}
当申请内存失败后,系统预定义的整型全局变量errno中存储了错误编号。
注意:该编号必须包含errno.h头函数。
使用strerror函数可以将上述错误编号转为字符串。
注意:该函数必须包含string.h头函数。
4.3 完整代码
本案例中的完整代码如下所示:
#include
#include
#include
#include
int main()
{
char* p = (char*)malloc (-1);
if (! p)
{
fprintf (stderr, “malloc: %s\n”, strerror (errno));
exit (EXIT_FAILURE);
}
scanf("%s", p);
printf("%s", p);
}
5 不能根据错误号判断是否出错
5.1 问题
虽然所有的错误号都不是零,但是因为在函数执行成功的情况下错误号全局变量errno不会被修改,所以不能用该变量的值为零或非零,做为出错或没出错的判断依据。
5.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:错误做法
代码如下所示:
char* p = (char*)malloc (0xffffffff);
FILE* fp = fopen ("/etc/passwd", “r”);
if (errno)
fprintf (stderr, “无法打开口令文件!\n”);
上述代码中,以下代码:
if (errno)
不能直接使用错误号判断是否出错。
步骤二:正确做法
代码如下所示:
FILE* fp = fopen ("/etc/passwd", "r");
if(! fp)
fprintf (stderr, "无法打开口令文件:%s\n", strerror (errno));
上述代码中,以下代码:
if(! fp)
先根据函数的返回值判断是否出错,在确定出错的前提下再根据errno的值判断具体出了什么错。
5.3 完整代码
本案例中的完整代码如下所示:
#include
#include
#include
int main()
{
/错误做法****
char* p = (char*)malloc (0xffffffff);
FILE* fp = fopen ("/etc/passwd", “r”);
if (errno)
fprintf (stderr, “无法打开口令文件!\n”);
正确做法**/
FILE* fp = fopen ("/etc/passwd", “r”);
if(! fp)
fprintf (stderr, “无法打开口令文件:%s\n”, strerror (errno));
fclose(fp);
}
6 内存映射的建立与解除
6.1 问题
所谓内存分配与释放,其本质就是建立或解除从虚拟内存到物理内存的映射,并在底层维护不同形式的数据结构,以把虚拟内存的占用与空闲情况记录下来。
6.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:建立内存映射
代码如下所示:
#include
#include
#include
#include
int main()
{
char* p = (char*)mmap (NULL, 8192, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (p == MAP_FAILED)
{
perror (“mmap”);
exit (EXIT_FAILURE);
}
strcpy (p, “Hello, Memory !”);
printf ("%s\n", p);
return 0;
}
上述代码中,以下代码:
char* p = (char*)mmap (NULL, 8192, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (p == MAP_FAILED)
{
perror ("mmap");
exit (EXIT_FAILURE);
}
使用mmap函数建立虚拟内存到物理内存或文件的映射。下面为函数形参的说明:
第一个形参为映射区内存起始地址,NULL系统自动选定后返回。
第二个形参为映射区字节长度,自动按页(4K)圆整。
第三个形参为映射区访问权限,可取以下值
PROT_READ - 映射区可读
PROT_WRITE - 映射区可写
PROT_EXEC - 映射区可执行
PROT_NONE - 映射区不可访问
第四个形参为映射标志,可取以下值
MAP_ANONYMOUS - 匿名映射,将虚拟内存映射到物理内存而非文件,忽略fd和offset参数
MAP_PRIVATE - 对映射区的写操作只反映到缓冲区中,并不会真正写入文件
MAP_SHARED - 对映射区的写操作直接反映到文件中
MAP_DENYWRITE - 拒绝其它对文件的写操作
MAP_FIXED - 若在start上无法创建映射,则失败(无此标志系统会自动调整)
MAP_LOCKED - 锁定映射区,保证其不被换出
第五个参数为文件描述符。
第六个参数为文件偏移量,自动按页(4K)对齐。
步骤二:解除内存映射
代码如下所示:
#include
#include
#include
#include
int main()
{
char* p = (char*)mmap (NULL, 8192, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (p == MAP_FAILED)
{
perror (“mmap”);
exit (EXIT_FAILURE);
}
strcpy (p, “Hello, Memory !”);
printf ("%s\n", p);
if (munmap (p, 4096) == -1)
{
perror ("munmap");
exit (EXIT_FAILURE);
}
strcpy (p += 4096, "Hello, Memory !");
printf ("%s\n", p);
if (munmap (p, 4096) == -1)
{
perror ("munmap");
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:
if (munmap (p, 4096) == -1)
{
perror ("munmap");
exit (EXIT_FAILURE);
}
使用munmap函数解除虚拟内存到物理内存或文件的映射。该函数的第一个形参为映射区内存起始地址,必须是页的首地址,第二个形参为映射区字节长度,自动按页(4K)圆整。munmap函数允许对映射区的一部分解映射,但必须按页。
6.3 完整代码
本案例的完整代码如下所示:
#include
#include
#include
#include
int main()
{
char* p = (char*)mmap (NULL, 8192, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (p == MAP_FAILED)
{
perror (“mmap”);
exit (EXIT_FAILURE);
}
strcpy (p, “Hello, Memory !”);
printf ("%s\n", p);
if (munmap (p, 4096) == -1)
{
perror ("munmap");
exit (EXIT_FAILURE);
}
strcpy (p += 4096, "Hello, Memory !");
printf ("%s\n", p);
if (munmap (p, 4096) == -1)
{
perror ("munmap");
exit (EXIT_FAILURE);
}
return 0;
}