可变参数列表<stdarg.h>

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

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

1)va_list 宏定义

#ifndef   _VA_LIST
#define _VA_LIST
typedef 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 50

void 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值