C语言中不同数据类型在内存中的存储格式研究

  学了这么多年C语言、C++、VC、MFC,但却从来没有认真研究过各种数据类型在内存中是如何存储的。感觉自己一直在弄的都是皮毛,没有触及真正核心的东西。直到昨天,重新翻看谭浩强老师经典的《C程序设计(第三版)》,在“第十四章 常见错误和程序调试”中有一个例子是这样的:

1     int a=3;
2     float b=4.5;
3     printf("%f %d",a,b);

  输出的结果是这样的:

  为什么会是这样的结果呢?让我们看一看a和b在内存中的存储方式吧?

  int 和 long 一样,按 2 的补码、低位字节在前的形式存储于 4 个字节中;

  float 按 IEEE 754 单精度数的形式存储于 4 个字节中;

  double 按 IEEE 754 双精度数的形式存储于 8 个字节中。

  a是int型的,在内存中占4个字节,在内存中的存储方式:

  地址:0x0012ff7c  0x0012ff7d  0x0012ff7e  0x0012ff7f

  数值:  03      00       00       00

  b是float型的,在内存中占4个字节,在内存中的存储方式:

  地址:0x0012ff70  0x0012ff71  0x0012ff72  0x0012ff73

  数值:  00      00       90       40

  看了printf的源码:

1 int printf( char * format , ...)
2 {
3     va_list ap;
4     int n;
5     va_start (ap,format);
6     n=vprintf(format ,ap);
7     va_end(ap);
8     return n;
9 }

  又看了vprintf的源码:

  1 /***
  2 *vprintf.c - printf from a var args pointer
  3 *
  4 *       Copyright (c) Microsoft Corporation. All rights reserved.
  5 *
  6 *Purpose:
  7 *       defines vprintf() - print formatted data from an argument list pointer
  8 *
  9 *******************************************************************************/
 10 
 11 #include <cruntime.h>
 12 #include <stdio.h>
 13 #include <dbgint.h>
 14 #include <stdarg.h>
 15 #include <internal.h>
 16 #include <file2.h>
 17 #include <mtdll.h>
 18 #include <stddef.h>
 19 
 20 /***
 21 *int vprintf(format, ap) - print formatted data from an argument list pointer
 22 *
 23 *Purpose:
 24 *       Prints formatted data items to stdout.  Uses a pointer to a
 25 *       variable length list of arguments instead of an argument list.
 26 *
 27 *Entry:
 28 *       char *format - format string, describes data format to write
 29 *       va_list ap - pointer to variable length arg list
 30 *
 31 *Exit:
 32 *       returns number of characters written
 33 *
 34 *Exceptions:
 35 *
 36 *******************************************************************************/
 37 
 38 int __cdecl vprintf_helper (
 39         OUTPUTFN outfn,
 40         const char *format,
 41         _locale_t plocinfo,
 42         va_list ap
 43         )
 44 /*
 45  * stdout 'V'ariable, 'PRINT', 'F'ormatted
 46  */
 47 {
 48         REG1 FILE *stream = stdout;
 49         REG2 int buffing;
 50         REG3 int retval;
 51 
 52         _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
 53 
 54 
 55         _lock_str(stream);
 56         __try {
 57 
 58         buffing = _stbuf(stream);
 59         retval = outfn(stream, format, plocinfo, ap );
 60         _ftbuf(buffing, stream);
 61 
 62         }
 63         __finally {
 64             _unlock_str(stream);
 65         }
 66 
 67         return(retval);
 68 }
 69 
 70 int __cdecl _vprintf_l (
 71         const char *format,
 72         _locale_t plocinfo,
 73         va_list ap
 74         )
 75 {
 76     return vprintf_helper(_output_l,format, plocinfo, ap);
 77 }
 78 
 79 int __cdecl _vprintf_s_l (
 80         const char *format,
 81         _locale_t plocinfo,
 82         va_list ap
 83         )
 84 {
 85     return vprintf_helper(_output_s_l,format, plocinfo, ap);
 86 }
 87 
 88 int __cdecl _vprintf_p_l (
 89         const char *format,
 90         _locale_t plocinfo,
 91         va_list ap
 92         )
 93 {
 94     return vprintf_helper(_output_p_l,format, plocinfo, ap);
 95 }
 96 
 97 int __cdecl vprintf (
 98         const char *format,
 99         va_list ap
100         )
101 {
102     return vprintf_helper(_output_l,format, NULL, ap);
103 }
104 
105 int __cdecl vprintf_s (
106         const char *format,
107         va_list ap
108         )
109 {
110     return vprintf_helper(_output_s_l,format, NULL, ap);
111 }
112 
113 int __cdecl _vprintf_p (
114         const char *format,
115         va_list ap
116         )
117 {
118     return vprintf_helper(_output_p_l,format, NULL, ap);
119 }

  发现里面使用了可变参数,即函数根本不知道传进来的参数类型,所以可能出现匹配错误的问题。4.5如果按照double类型存储格式是:

  0x4012000000000000,按照%d格式输出就是1074921472,与我们前面得到的结果正好相同。

  下面,再做一个实验:

1     int a=3;
2     int *p=&a;
3     double b=4.5;
4     double *q=&b;
5     printf("%f %d",a,b);

  运行结果如下:

  与前面得到的结果完全相同。

  看来问题的原因在于可变参数类别表,传入的参数类型与编译器的解释类型不一致引起的,所以以后编程时一定要慎用可变参数列表。在网上看到一篇文章也提到了使用可变参数列表应该注意的问题:

  (1)可变参数的类型和个数完全由程序代码控制,它并不能智能地识别不同参数的个数和类型;

 

  (2)如果我们不需要一一详解每个参数,只需要将可变列表拷贝至某个缓冲,可用vsprintf函数;

 

  (3)因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码;

posted on 2012-11-20 20:48  onedime  阅读(1880)  评论(0编辑  收藏  举报