c库(下)----小话c语言(18)

[Mac  10.7.1  Lion  Intel-based  x64  gcc4.2.1  xcode4.2  ]


Q: 如何解决abs函数传入一个整形数最小值返回溢出的数?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#define PRINT_D(intValue)       printf(#intValue" is %d\n", (intValue));
#define PRINT_STR(str)          printf(#str" is %s\n", (str));

int main()
{
    int ret = abs(INT_MIN);
    PRINT_D(ret)
    return 0;
}

输出:
ret is -2147483648

A:  显然是溢出了。查看abs的源代码(可能实际会有区别:查看的是苹果开源代码libc-763.12):

int
abs(j)
	int j;
{
	return(j < 0 ? -j : j);
}

显然,结果必然会溢出。一个解决方法,单独处理负数最大值的情况,且提高返回值类型最大值范围,如下:
unsigned abs(int n)
{
    if(n == INT_MIN)
        return INT_MAX + 1U;
    return (n < 0 ? -n : n);
}

Q: 对于随机数,如何产生?

A: 采用固定算法产生的随机数必然不能是真正意义上的随机,也称为伪随机数。不过再伪,有时也没关系了,上层觉得很像随机数就ok了。

    很多随机数都采用如下的算法:

 

Rand_Number = (Rand_Seed * X + Y) mod Z

下面可以参考源代码:
void
srand(seed)
u_int seed;
{
	next = seed;
}
static int
do_rand(unsigned long *ctx)
{
#ifdef  USE_WEAK_SEEDING
/*
 * Historic implementation compatibility.
 * The random sequences do not vary much with the seed,
 * even with overflowing.
 */
	return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)RAND_MAX + 1));
#else   /* !USE_WEAK_SEEDING */
/*
 * Compute x = (7^5 * x) mod (2^31 - 1)
 * without overflowing 31 bits:
 *      (2^31 - 1) = 127773 * (7^5) + 2836
 * From "Random number generators: good ones are hard to find",
 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
 * October 1988, p. 1195.
 */
	long hi, lo, x;

	/* Can't be initialized with 0, so use another value. */
	if (*ctx == 0)
		*ctx = 123459876;
	hi = *ctx / 127773;
	lo = *ctx % 127773;
	x = 16807 * lo - 2836 * hi;
	if (x < 0)
		x += 0x7fffffff;
	return ((*ctx = x) % ((u_long)RAND_MAX + 1));
#endif  /* !USE_WEAK_SEEDING */
}

int
rand()
{
	return (do_rand(&next));
}

Q: c库内部出现的错误或者异常,如何让上层获取到?

A: 可以采用全局变量保存的方式或者保存在各个线程的TLS区域。采用全局变量在多线程的时候会出现问题,TLS可以很好地解决这个问题。另外,也可能在各个可能出现异常函数加上一个参数专门保存异常信息,当然这样会导致函数接口不简洁。下面是苹果对errno的实现:

extern int errno;
int *__error(void) {
    pthread_t self = pthread_self();
    /* If we're not a detached pthread, just return the global errno */
    if ((self == (pthread_t)0) || (self->sig != _PTHREAD_SIG)) {
        return &errno;
    }
    return &self->err_no;
}

int cthread_errno(void) {
        return *__error();
}

可以看出,每个线程均会有单独的错误信息,它是支持多线程的。下面附注pthread_t结构:
typedef struct _pthread
{
	long	       sig;	      /* Unique signature for this structure */
	struct __darwin_pthread_handler_rec *__cleanup_stack;
	pthread_lock_t lock;	      /* Used for internal mutex on structure */
	uint32_t	detached:8,
			inherit:8,
			policy:8,
			freeStackOnExit:1,
			newstyle:1,
			kernalloc:1,
			schedset:1,
			wqthread:1,
			wqkillset:1,
			pad:2;
	size_t	       guardsize;	/* size in bytes to guard stack overflow */
#if  !defined(__LP64__)
	int	       pad0;		/* for backwards compatibility */
#endif
	struct sched_param param;
	uint32_t	cancel_error;
#if defined(__LP64__)
	uint32_t	cancel_pad;	/* pad value for alignment */
#endif
	struct _pthread *joiner;
#if !defined(__LP64__)
	int		pad1;		/* for backwards compatibility */
#endif
	void           *exit_value;
	semaphore_t    death;		/* pthread_join() uses this to wait for death's call */
	mach_port_t    kernel_thread; /* kernel thread this thread is bound to */
	void	       *(*fun)(void*);/* Thread start routine */
        void	       *arg;	      /* Argment for thread start routine */
	int	       cancel_state;  /* Whether thread can be cancelled */
	int	       err_no;		/* thread-local errno */
	void	       *tsd[_EXTERNAL_POSIX_THREAD_KEYS_MAX + _INTERNAL_POSIX_THREAD_KEYS_MAX];  /* Thread specific data */
        void           *stackaddr;     /* Base of the stack (is aligned on vm_page_size boundary */
        size_t         stacksize;      /* Size of the stack (is a multiple of vm_page_size and >= PTHREAD_STACK_MIN) */
	mach_port_t    reply_port;     /* Cached MiG reply port */
#if defined(__LP64__)
        int		pad2;		/* for natural alignment */
#endif
	void           *cthread_self;  /* cthread_self() if somebody calls cthread_set_self() */
	/* protected by list lock */
	uint32_t 	childrun:1,
			parentcheck:1,
			childexit:1,
			pad3:29;
#if defined(__LP64__)
	int		pad4;		/* for natural alignment */
#endif
	TAILQ_ENTRY(_pthread) plist;
	void *	freeaddr;
	size_t	freesize;
	mach_port_t	joiner_notify;
	char	pthread_name[MAXTHREADNAMESIZE];		/* including nulll the name */
        int	max_tsd_key;
	void *	cur_workq;
	void * cur_workitem;
	uint64_t thread_id;
} *pthread_t;

Q: atexit函数该如何实现?

A: 需要一个可以保存数个注册的函数指针的结构,当应用程序结束前可以从中取出函数,按特定的顺序执行。

struct atexit {
	struct atexit *next;			/* next in list */
	int ind;				/* next index in this table */
	struct atexit_fn {
		int fn_type;			/* ATEXIT_? from above */
		union {
			void (*std_func)(void);
			void (*cxa_func)(void *);
#ifdef __BLOCKS__
			void (^block)(void);
#endif /* __BLOCKS__ */
		} fn_ptr;			/* function pointer */
		void *fn_arg;			/* argument for CXA callback */
		void *fn_dso;			/* shared module handle */
	} fns[ATEXIT_SIZE];			/* the table itself */
};

当然,atexit是以进程为单位的,可以使用static变量保存此结构:
static struct atexit __atexit0;

Q: exit函数在何时调用atexit注册的函数呢?

A: 当然在调用系统退出应用程序前执行它。

/*
 * Exit, flushing stdio buffers if necessary.
 */
void
exit(status)
	int status;
{
	__cxa_finalize(NULL);
	if (__cleanup)
		(*__cleanup)();
	__exit(status);
}

而__cxa_finalize函数就执行了atexit曾经注册的函数:
void
__cxa_finalize(void *dso)
{
	struct atexit *p;
	struct atexit_fn fn;
	int n;

	_MUTEX_LOCK(&atexit_mutex);
	for (p = __atexit; p; p = p->next) {
		for (n = p->ind; --n >= 0;) {
			if (p->fns[n].fn_type == ATEXIT_FN_EMPTY)
				continue; /* already been called */
			if (dso != NULL && dso != p->fns[n].fn_dso)
				continue; /* wrong DSO */
			fn = p->fns[n];
			/*
			  Mark entry to indicate that this particular handler
			  has already been called.
			*/
			p->fns[n].fn_type = ATEXIT_FN_EMPTY;
		        _MUTEX_UNLOCK(&atexit_mutex);
		
			/* Call the function of correct type. */
			if (fn.fn_type == ATEXIT_FN_CXA)
				fn.fn_ptr.cxa_func(fn.fn_arg);
			else if (fn.fn_type == ATEXIT_FN_STD)
				fn.fn_ptr.std_func();
			_MUTEX_LOCK(&atexit_mutex);
		}
	}
	_MUTEX_UNLOCK(&atexit_mutex);
}

Q: system函数实现执行某个可执行文件,如何实现?

A: 采用fork方式创建一个子进程,如果返回父进程执行,将等待子进程完成;如果进入子进程,使用execl加载子进程镜像来执行。如下代码:

int
__system(command)
	const char *command;
{
	pid_t pid, savedpid;
	int pstat;
	struct sigaction ign, intact, quitact;
	sigset_t newsigblock, oldsigblock;

	if (!command)		/* just checking... */
		return(1);

	/*
	 * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
	 * existing signal dispositions.
	 */
	ign.sa_handler = SIG_IGN;
	(void)sigemptyset(&ign.sa_mask);
	ign.sa_flags = 0;
	(void)_sigaction(SIGINT, &ign, &intact);
	(void)_sigaction(SIGQUIT, &ign, &quitact);
	(void)sigemptyset(&newsigblock);
	(void)sigaddset(&newsigblock, SIGCHLD);
	(void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
	switch(pid = fork()) {
	case -1:			/* error */
		break;
	case 0:				/* child */
		/*
		 * Restore original signal dispositions and exec the command.
		 */
		(void)_sigaction(SIGINT, &intact, NULL);
		(void)_sigaction(SIGQUIT,  &quitact, NULL);
		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
		execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL);
		_exit(127);
	default:			/* parent */
		savedpid = pid;
		do {
			pid = _wait4(savedpid, &pstat, 0, (struct rusage *)0);
		} while (pid == -1 && errno == EINTR);
		break;
	}
	(void)_sigaction(SIGINT, &intact, NULL);
	(void)_sigaction(SIGQUIT,  &quitact, NULL);
	(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
	return(pid == -1 ? -1 : pstat);
}

xichen

2012-6-2 15:52:11 


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值