• 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)因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码;

  • 相关阅读:
    珠海洪锐在线监测agent_linux系统
    python中的不定长参数
    记一次刻苦铭心得安装zabbix经历
    狼书第三章Jinja2模板总结
    关于消息闪现的问题
    了解HTTP状态码
    关于用Flask建立一个简单的web应用
    将模块安装到Site-packages
    在Centos6中安装python3.6
    unity 生成缩略图 , 图片缩放
  • 原文地址:https://www.cnblogs.com/onedime/p/2779707.html
Copyright © 2020-2023  润新知