可变参数列表<stdarg.h>

来源:互联网 发布:百川二建考试软件 编辑:程序博客网 时间:2024/05/22 00:17

调试的错误就是编程给你最好的东西,因为在每个错误上面都标志着前进的一步。
—-By CSDN

在练习写一个简单版scanf函数,测试时出现意外错误,检查了一下,可能是对头文件里一些宏的引用出错。查了一下源代码,取了部分关键的几个宏定义:

1)va_list 宏定义

#ifndef   _VA_LIST#define _VA_LISTtypedef char* va_list;#endif

2)va_argsiz(t)获取类型占用的空间长度,最小占用长度为int的整数倍

 #define __va_argsiz(t)   (((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))

3)va_start,使ap指向第一个无名参数

#ifdef    __GNUC__#define va_start(ap, pN)     ((ap) = ((va_list) __builtin_next_arg(pN)))#else#define va_start(ap, pN)    \  ((ap) = ((va_list) (&pN) + __va_argsiz(pN))) #endif

4)清空va_list列表

#define va_end(ap)     ( ap = (va_list)0 )

5)返回与当前无名参数具有相同类型和数值的值,并使ap指向下一个无名参数

#define va_arg(ap, t)                   (((ap) = (ap) + __va_argsiz(t)), *((t*) (void*) ((ap) - __va_argsiz(t))))

——————————————————————————————————————–
我写的读取标准输入流中的字符程序,就需要修改参数变量的值。
那么问题来了,如果参数是一个地址,如何修改地址中的值呢?其实是一样的,比如传递一个(int*)的参数进去,*((int**)(void*) ((ap) - __va_argsiz(int*))))。
这里int**是将指向void类型的指针转为指向int*的指针,再解引用,就变成了(int*)的参数。
——————————————————————————————————————–
下面是简单版的scanf函数

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<stdarg.h>#define MAXWORD 50void minscanf(char *fmt,...){  va_list ap;   char *p, *sval;   int i, *ival;   double *fval;   va_start(ap, fmt);   for(p = fmt; *p; p++)   {       char c[MAXWORD];      while(*p==' ' || *p=='\t' || *p=='\n')               p++;      if(*p == '%'){         while( (*c =  getchar() ) == ' ' )                   ;            i = 0;            while((c[++i] = getchar())  != ' ' && c[i]!='\n' )                     ;             c[i] = '\n';          switch(*++p)           {  case 'd':                  ival = va_arg(ap,int *);                  *ival = atoi(c);    \\              ap++;                      break;              case 'f':                   fval = va_arg(ap,double *) ;                   *fval = atof(c);     \\              ap++;                   break;              case 's':                   sval = va_arg(ap,char *);                   strcpy(sval,c);     \\            ap++;                   break;              default:                   continue;           }      }      printf("\n");   }   va_end(ap);}

调试过程中,操作系统给出了“SIGSEGV”信号,查了一下资料。SIG是信号名的通用前缀。SEGV是segmentationviolation(段违例)的缩写。SIGSEGV是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。通常是访问了一个不可用的内存地址,或着往只读内存地址写数据。
检查了很久,才发现是ap++;这句话出错了,应该删去。
仔细想想,这种毛病在高中时代做题时就有了,惯性动作带来的后果。为使ap指向下一个位置,就直接ap++了。呵呵~

此外,写这个程序的过程中还犯了一个老毛病,变量的位置放置错误,导致不恰当的作用域,从而在循环的过程中上一次循环变量的值影响这一次。

0 0
原创粉丝点击