[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