goto语句在C/C++语言中可谓是“臭名昭著”,乃至有的书(或公司的编程规范)提出禁用goto语句的说法。其结果就是,造成有的程序员一看到goto语句在某程序中被使用,就本能地认为这个程序写得很“垃圾”。此外,也使得有些程序员因为使用了goto语句而觉得自己很不专业。其实,凡事都不能太偏激,goto语句运用得好能大大地简化程序,以及提高程序的可读性和可维护性。在开始示例其好处之前,先用一些统计数据来说明goto语句并没有因为“臭名昭著”而被抛弃,这些统计数据可能并不是百分之百的精确,但很具有说服力。对于操作系统,Linux-2.6.21内核使用了20,333个goto语句,VxWorks-6.2则使用了9142个,最后941个goto语句被运用到了rtems-4.9.2中;另外,glibc-2.9库使用了1750个goto语句。所有这些统计数据都表明,goto语言并没有想象的那样可怕而招到禁用,其关键在于 —— 恰当地运用它。
图1是一个没有采用goto语句编写的函数,其中存在多处出错处理的代码,比如113~115行、120~122行和126~129行。采用这种分布式的出错处理,很容易出现遗漏释放前面已经分配的资源,从而造成资源泄漏问题。如果采用goto语句,则能取得更好的效果。
00097: int queue_init (queue_t ** _pp_queue, int _size)
00098: {
00099: pthread_mutexattr_t attr;
00100: queue_t *queue;
00101:
00102: queue = (queue_t *) malloc(sizeof(queue_t));
00103: if (0 == queue) {
00104: return -1;
00105: }
00106: *_pp_queue = queue;
00107:
00108: memset (queue, 0, sizeof (*queue));
00109: queue->size_ = _size;
00110:
00111: pthread_mutexattr_init (&attr);
00112: if (0 != pthread_mutex_init(&queue->mutex_, &attr)) {
00113: pthread_mutexattr_destroy (&attr);
00114: free (queue);
00115: return -1;
00116: }
00117:
00118: queue->messages_ = (void **) malloc (queue->size_ * sizeof (void *));
00119: if (0 == queue->messages_) {
00120: pthread_mutexattr_destroy (&attr);
00121: free (queue);
00122: return -1;
00123: }
00124:
00125: if (0 != sem_init(&queue->sem_put_, 0, queue->size_)) {
00126: free (queue->messages_);
00127: pthread_mutexattr_destroy (&attr);
00128: free (queue);
00129: return -1;
00130: }
00131:
00132: pthread_mutexattr_destroy (&attr);
00133:
00134: return 0;
00135: }
图2是采用goto语句所编写的另一个版本,与不采用goto语句的版本相比,程序更加的简单,且在出错处理的地方大都使用goto语句跳转到程序的末尾进行处理。goto语句除了可以用在这里所示例的出错处理中,还可以用在其它的程序逻辑中以简化程序并提高阅读性。
00053: int queue_init (queue_t ** _pp_queue, int _size)
00054: {
00055: pthread_mutexattr_t attr;
00056: queue_t *queue;
00057:
00058: queue = (queue_t *) malloc(sizeof(queue_t));
00059: if (0 == queue) {
00060: return -1;
00061: }
00062: *_pp_queue = queue;
00063:
00064: memset (queue, 0, sizeof (*queue));
00065: queue->size_ = _size;
00066:
00067: pthread_mutexattr_init (&attr);
00068: if (0 != pthread_mutex_init(&queue->mutex_, &attr)) {
00069: goto error;
00070: }
00071:
00072: queue->messages_ = (void **) malloc (queue->size_ * sizeof (void *));
00073: if (0 == queue->messages_) {
00074: goto error;
00075: }
00076:
00077: if (0 != sem_init(&queue->sem_put_, 0, queue->size_)) {
00078: goto error1;
00079: }
00080:
00081: pthread_mutexattr_destroy (&attr);
00082:
00083: return 0;
00084:
00085: error1:
00086: free (queue->messages_);
00087: error:
00088: pthread_mutexattr_destroy (&attr);
00089: free (queue);
00090: return -1;
00091: }
使用goto语句时需要注意以下原则:
1) 不要过份地使用。比如图2中的60行就没有采用goto语句跳到程序的最后面,之所以这里不使用goto是为了阅读方便。因为程序此时还没有分配资源,所以直接返回显得更加的直接了当。还有就是,在这个函数中如果存在使用goto语句都意味着出错了且需要释放资源。如果将60行的语句也改为goto就破坏了这个函数中使用goto语句的一致性。
2) 不要让goto语句形成一个环。使用goto语句应形成一条线,从一点跳到另一点。当然,如果goto语句的使用没有破坏可读性,那可以适当的考虑打破这一原则。