UNIXC01 DAY03 错误处理 、动态加载02和辅助工具、内存管理与进程映射

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;

因为ab求余数,余数可以是负数、零、正数,此时返回几都是合理范围内的值。此时就需要使用指针型参数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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
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 <stdio.h>
#include <errno.h>
#include <string.h>
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 <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
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 <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
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 <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值