| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 579 人关注过本帖, 1 人收藏
标题:老江湖碰到的新问题
取消只看楼主 加入收藏
normalcn
Rank: 1
等 级:新手上路
帖 子:1
专家分:0
注 册:2010-12-27
结帖率:0
收藏(1)
已结贴  问题点数:20 回复次数:0 
老江湖碰到的新问题
// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
/*
// ================== fscanf() ==================
// test.txt : 1 2 3 4 5 6 7 8 9 0 1 2
int main( int argc , char* argv[ ] )
{
    FILE *f ;

    char buf[ 11] ;

    int  i ;

    if( ( f = fopen( "test.txt" , "r" ) ) == NULL )
    {
        printf( "Can't Open The File" ) ;
        return  0 ;
    }

    for( i = 0 ; i < 10 ; i++ )
    {
        //fscanf( f , "%x" , &buf[ i ] ) ; // A
        fscanf( f , "%x" , &buf[ 9-i ] ) ; // B
    }
    fclose( f ) ;
   
    printf( "\nEnd  : " ) ;
    for( i = 0 ; i < 10 ; i++ )
    {
        printf( "%02x " , buf[ i ] ) ;
    }
    printf( "\n" ) ;

    return 0;
}*/
/*
这段代码完成的工作就是将文件中的十个数字,从9-0反序放到数组buf中,但是,在实际执行过程中,有下面两个问题

1. 会产生溢出错误....,A,B均有
2. 在B方式下,输出结果全0

这个问题实在是有点让人莫名其妙的
给大家一个分析这个问题的办法,在//B方案下面添加如下代码:
        printf( "\nNO.%d : " , i ) ;
        for( int  j = 0 ; j < 10 ; j++ )
        {
            printf( "%02x " , buf[ j ] ) ;
        }
分析:
  1. fcanf("%x")做为一个(四字节)整型X读入,然后强制&0xff( char),然后,按照intel数据顺序,低位在前,高位在后,写四个字节,在A,B方式下,问题1均是由于对buf[9]的赋值产生的边界溢出造成的,buf的缓冲区长度最少应为13.......OMG
    2 .在B方式下,由于一次对四个字节赋值,从数组尾向前的运动方式,使的数组从后向前逐个赋0!!

    解决办法
    1  int buf[10 ];
      2 char a;
    fscanf(f,"%x",&a);
    buf[9-i]=a&0xff;

写这个例子只是想告诉大家,其实很多时候,代码本身是没错的,错在系统,大家要学代码设计,一定要记住,任何代码都是在一定的环境下实现的,要想写好代码,一定要对代码的应用环境有充分的认识。
*/

/*
// ================== 文件操作 ==================
unsigned char pool[ 1 << 20 ] ;
unsigned int  buf[ 1 << 20 ] ;

int main( int argc , char* argv[ ] )
{   
    FILE  *f ;   

    int   i , j , k ;

    if( ( f = fopen( "test.txt" , "r" ) ) == NULL )
    {        
        printf( "Can't Open The File" ) ;
        return  0 ;
    }
    i=fread(pool,1,1<<20,f); //A
    fread(buf,1,1,f); //B

    i = 0 ;
    while( !feof( f ) )
    {
        fscanf( f , "%c" , &pool[ i++ ] ) ;
    }
    fclose( f ) ; // C
   
    return 0 ;

} */
/*
A  此时要注意的是,由于打开方式为"r"而不是"rb",则返回的值i不为读入数据的真实长度.
B  此代码本意为读入一个字节,存入buf[0]中,但是,由于buf为整型缓冲区,而实际上,fread会读入四个字符来补满buf[0],由此产生两个问题:1,读入数据不准确;2,由于文件指针移动了四而不是一,则下一个数据实际读入的是文件的第五个字节.
C 此时要注意的是,最后i的值会比实际读入数据多一个,正确的是,在最后补一条指令:i--;
*/

/*
// ================== 边界 ==================
int main( int argc , char* argv[ ] )
{
    char           m_ca  ;
    unsigned char  m_ucb ;
    unsigned int   i , j ;

    char           m_cBuf[ 257 ] ;
   
    for( m_ca = 0 ; m_ca < 256 ; m_ca++ )
    {
        m_cBuf[ m_ca ] = m_ca ; // A
    }
   
    for( m_ucb = 0 ; m_ucb < 256 ; m_ucb++ )
    {
        m_cBuf[ m_ucb ] =  m_ucb ; // B
    }
   
    i = 0 ;
    i-- ;

    for( j = 0 ; j < i ; j++ ) // C
    {

    }
}*/
/*
A  有符号的字符型值的范围是 -127-128 ,这段代码会让你很爽的把内存改的乱七八糟,还是个死循环。。。
B 数组初始化只有一种结果:死循环  无符号字符型的表达范围为 0- 255,永远不可能达到 256
C 上述循环会运行2的32次方减一次 !!!
   printf( "\n %u \n" , i ) ;
*/

/*
// ================== 编译顺序 ==================
int main( int argc , char* argv[ ] )
{
    int i , j ;
   
    i = 1 ;
    j = 2 ;
    printf( "\ni = 1 , j = 2 , i += ( 4 + j ) = %d , i += 4 + j = %d \n" , (i+=(4+j)) , (i+=4+j) ) ;

    return 0 ;
}*/
/*
    本例测试 += 操作时,右面的括号有没有用
    (实际上,+=的右边只算一个操作数,也就是右边表达式的值。两个+=的计算内容是一样的,都是 i += ( 4 + j ) )
    由于本条语句的编译顺序为从右向左,结果为:
    Debug  :
    i = 1 , j = 2 , i += ( 4 + j ) = 13 , i += 4 + j = 7
    Release :
    i = 1 , j = 2 , i += ( 4 + j ) = 13 , i += 4 + j = 13
    都不是原代码的预期结果!!!!
    看来采用这种高手编程技术可能会产生算法的二义性.
    编写代码的基本要求是安全可靠,既然可能产生问题,我的建议是 : 不采用这种技术
*/

/*
// ================== 计算精度 ==================
int main( int argc , char* argv[ ] )
{   
    // float(浮点型)的精度非常低,以下程序的最后结果,一般不会是 1
    float m_fa , m_fb ;
   
    m_fa = 1 / 12 ;
    m_fb = m_fa * 12 ;
   
    printf( "\n\n Test Float : %12.10f \n" , m_fb);
   
    // 此时将数据类型改为double(双精度),会有比较满意的结果
    #define  MAX_DOUBLE_NUMBER (12345678901234567890.0 )
   
    double m_dfa , m_dfb ;
   
    m_dfa = 1 / MAX_DOUBLE_NUMBER ;
    m_dfb = m_dfa * MAX_DOUBLE_NUMBER ;
   
    printf( "\n\n Test Double : %12.10f \n" , m_dfb);
   
    // 结果依然是1.
    // 要注意的是,在本例中,double能做的除法长度为10的20次方幂.再长就要采用大数算法了。
    return 0 ;

}
*/

/*
// ================== 边界对齐 ==================

int main( int argc , char* argv[ ] )
{
//#pragma pack ( 1 )
   struct Type_A
   {
       int   a ;
       char  b ;
       short c ;
   } m_stTesta ;

   struct Type_B
   {
       char  b ;
       int   a ;
       short c ;
   } m_stTestb ;
//#pragma pack (  )

   printf("\n Struct A : %d   Struct B : %d \n" , sizeof( m_stTesta ) , sizeof( m_stTestb ) ) ;

   return 0 ;
}
*/
/* 编译器为了提高代码效率,或者保证代码正确运行,会对数据的起点进行相应的处理
   例如:
     有些CPU的数据起点必须为偶数,若为奇数则出错;
     有些CPU只能从偶数起点读数据,若一个整型起点为偶数,则可一次读入,若起点为奇数,则要两次才能读入

   一般情况下,数据起点为2的n次幂,表现即为地址码的某几位低地址为0

   VC编译器在默认设定下,边界对齐采用8字节
   但是,这只是对于大于八字节的长度类型而言,采用八字节边界
   在数据类型低于8字节时,编译器会采用最小符合条件的边界来设定数据起点
   这也就是在上述情况下  struct Type_A 的长度为 8 ,struct Type_B的长度为12
   
   如果想让这两个结构体只有实际定义长度,我们必须通知编译器,改变边界对齐方式

   方法一:
   预编译命令 : #pragma pack( [ n ] ) 与 #pragma pack( )
   #pragma pack( [ n ] ) 通知编译系统,以下代码采用 n字节长的边界 ,n = 1 , 2 , 4 , 8 , 16
   #pragma pack(       ) 通知编译系统,结束上面的边界对齐方式,以下采用默认方式

   方法二:
   VC IDE 中设定编译命令:
   [Project]|[Settings],[c/c++]选项卡[Category]的[Code Generation]选项
   [Struct Member Alignment]中修改,默认是8字节。
   
   我个人不推荐使用IDE的修改,IDE的修改,意味着全部编译代码均采用这种新的边界对齐方式,会对某些代码产生影响
   (预编译命令#pragma 的内容比较多,可以参考MSDN中#pragma的详细说明,eg:  #pragma comment( lib , "ws2_32.lib" ) )  
*/

/*
// ================== 指针与逗号语句 ==================

#define HOST_c2l( c , l ) ( l  = ( ( ( unsigned long ) ( *( ( c )++ ) ) ) << 24 ) , \
                            l |= ( ( ( unsigned long ) ( *( ( c )++ ) ) ) << 16 ) , \
                            l |= ( ( ( unsigned long ) ( *( ( c )++ ) ) ) <<  8 ) , \
                            l |= ( ( ( unsigned long ) ( *( ( c )++ ) ) )       ) , \
                            l )
   
int main( int argc , char* argv[ ] )
{
    unsigned char m_caData[ 256 ] ;

    unsigned char *m_cpPoint ;
    unsigned int  *m_ipPoint ;
   
    int           i , l ;

    m_cpPoint = m_caData ;
    m_ipPoint = ( unsigned int * ) m_caData ;

    printf( "\n SIZEOF_POINT " ) ;
    printf( "\n CHAR_SIZEOF :  %08x - INT_SIZEOF : %08x " , sizeof( m_cpPoint ) , sizeof( m_ipPoint ) ) ;

    for( i = 0 ; i < 256 ; i++ )
    {
        m_caData[ i ] = ( i + 1 ) % 0xff ;
    }
   
    printf( "\n\n POINT START " ) ;
    printf( "\n CHAR %p : %08x - %08x " , m_cpPoint , m_cpPoint , *m_cpPoint ) ;
    printf( "\n INT  %p : %08x - %08x " , m_ipPoint , m_ipPoint , *m_ipPoint ) ;

    printf( "\n\n POINT + 4 " );
    m_cpPoint += 4 ;
    m_ipPoint += 4 ;
    printf( "\n CHAR %p : %08x - %08x %08x  ", m_cpPoint , m_cpPoint , *m_cpPoint , *m_cpPoint + 6 ) ;   
    printf( "\n INT  %p : %08x - %08x %08x  ", m_ipPoint , m_ipPoint , *m_ipPoint , *m_ipPoint + 6 ) ;

    printf( "\n\n ,  COMMAND AND CHAR_POINT MOVEMENT : " ) ;
    i = HOST_c2l( m_cpPoint , l ) ;
   
    printf( "\n CHAR %p : %08x " , m_cpPoint , *m_cpPoint ) ;   
    printf( "\n\n I : %08x L: %08x \n\n" , i , l ) ;
   
    return 0 ;
}*/
/*
1. printf( "%p" , XXXX )  输出为XXXX的地址
2. 指针本身只是一个地址入口(四字节) , 任意指针 sizeof( p ) 只有一个结果 : 4 .
3. *p 为指针内容,p 为指针地址
4. 整型指针指向字符型缓冲区时,采用intel数据排列顺序,以四字节为单位进行反序
5. ( p + 1 ) 表示根据指针定义的单元长度加1,若定义为字符指针,则只加一字节,若为整型,则加四字节
6. 逗号语句的计算顺序为自左向右,逗号语句的值为逗号语句的最后一个分量的值.
7. 指针C在每个逗号后就指向下一个字符,运算后,C的指针值变为 C + 4 .
8. l的值每个都在变化,最后的l,即四个字符的并,做为这个表达式的值,可以直接做为赋值使用.
*/

/*
// ================== 指针应用 ==================
#include "malloc.h"

#pragma pack ( 1 )

typedef struct MY_IP_HEAD { unsigned char Ver_Len ;
                            unsigned int  Source_IP ;
                            unsigned int  Dest_IP ; } *IPHEAD , TEST_LEN;

#pragma pack (  )

int main( int argc , char* argv[ ] )
{
    char          *Buf ;
    unsigned char *Point ;

    TEST_LEN      test ;
    IPHEAD        ip_point ;

    int            i , j ;

    Buf = ( char * )malloc( 1 << 20 ) ;

    for( i = 0 ; i < 1000 ; i++ )
    {
        Buf[ i ] = i + 1 ;
    }

    Point = (unsigned char * ) Buf ;

    for( i = 0 ; i < 10 ; i++ )
    {
        ip_point = ( IPHEAD ) Point ;

        printf( "\n STEP : %2d" , i ) ;
        printf( "\n Buf : " ) ;
        for( j = 0 ; j < 10 ; j++ )
        {
            printf( "%02x " , Point[ j ] ) ;
        }

        printf( "\n IP  : " ) ;
        printf( "VER : %02x  S : %08x  D : %08x " ,
                ip_point->Ver_Len , ip_point->Source_IP , ip_point->Dest_IP ) ;
        Point += 60 ;
    }

    free( Buf ) ;
    Buf = NULL ;

    return 0 ;
}*/
/*
   指针是地址,指针也只是地址,这点一定要牢记
   在这里,我们采用一个简单的例子,一个无符号的字符串指针,一个IP包的结构体指针
   第一个指针赋值之后,会强制将有符号字符变为无符号字符,没有任何额外的开销(告别&0xff了)
   第二个指针赋值之后,当我们引用指针时,会自动将字符数据变成整型了( 告别了(l=(a<<24)+(b<<16)+(c<<8)+d )这种有时间开销的语句了)
   美中不足的是,又是intel数据排列方式的问题,四个字节做了一个反序
   与此类似的就是,结构体的位段,采用的是从右向左的编排顺序
*/

/*
// ================== 死循环 ==================
//#include "windows.h"

int main( int argc , char* argv[ ] )
{
    while(1)
    {
        // ::Sleep(1); //停顿1/1000秒
    }

    return 0 ;
}
*/
/*
 很多时候,我们工作中会用到死循环
 在这种情况下,CPU的占用率会非常高,表现就是明显感觉机器运行很慢
 双核情况下,占有率一般都超过50%,估计在单核情况下,就是100%(没单核CPU测试)

  解决方案:
  添加头文件包含: #include "windows.h"
  在循环体内加入下列代码:  ::Sleep(1); //停顿1/1000秒
  这种处理之后,一般双核的CPU,资源占用率不会超过15%
*/

/*
// ================== 文件系统 ==================

#define   FILE_NUMEBER ( 2000 )

int main( int argc , char* argv[ ] )
{
    FILE *f ;
    char name[ 256 ] ;

    int  i ;

    for( i = 0 ; i < FILE_NUMEBER ; i++ )
    {
        sprintf( name , "%04d.txt" , i ) ;

        if( ( f = fopen( name , "w" ) ) == NULL )
        {
            printf("\n Can't Creat File %s " , name ) ;

            return 0 ;
        }
        fprintf( f, " %04d" , i ) ;

        fclose( f ) ;
    }

    return 1 ;

}
*/
/*
   在win系统中,我们进入文件夹后,win文件系统会对这个文件夹下的文件进行一次遍历以构造一个数据表
   显然,文件数目越多,时间越长(要不停地开内存来放数据,重构数据表)
   运行上面的代码后,打开那个文件夹,你会发现,系统很慢,如果不慢,FILE_NUMEBER 后面的数字改成20000
   一般建议一个文件夹下面最多放1000个文件
   我最近碰到一兄弟,一文件夹下面放2W个文件!!
   机器的情况是:系统不稳定,老死机(不死才怪,一开这个文件夹,win不崩溃都算是奇迹了)
*/
/*
// ================== 文件处理效率 ==================

#include "time.h"

// test.txt 一个长度不小于31M字节的数据文件

// 定义缓冲区长度 31 M
char buf[ 31 << 20 ] ;

// 测试开关,设置则为测试块读,不设置则为字节读
#define TEST_BLOCKREAD

#ifdef TEST_BLOCKREAD
  #define  RUM_TIMES ( 200 )
#else
  #define  RUM_TIMES ( 2 )
#endif

int main( int argc , char* argv[ ] )
{
    FILE * f;

    clock_t tima,timb ;

    int i , j ;

    if( ( f = fopen( "test.txt" , "rb" ) ) == NULL)
    {
        printf( "\n Can't Open The File " ) ;
        return 0 ;
    }

    tima = clock( ) ; // clock() : 以千分之一秒为计数,返回当前cpu时间
    for( j = 0 ; j < RUM_TIMES ; j ++)
    {
#ifdef  TEST_BLOCKREAD
        fread( buf , 1 , 31 << 20 , f ) ;
#else
        for( i = 0 ; i < ( 31 << 20 ) ; i++ )
        {
            fscanf( f , "%c" , &buf[ i ] ) ;            
        }
#endif

        fseek( f , SEEK_SET , 0l ) ;
    }
    timb = clock( ) ;

    fclose( f ) ;
   
#ifdef TEST_BLOCKREAD
    printf( "BLOCK_RUN 200 Times : %d  \n" , timb - tima ) ;
#else
    printf( "BYTE_RUN  200 Times : %d  \n" , ( timb - tima) * 100 ) ;
#endif

    return 0 ;
}
*/
/*
  我的测试结果:
  BLOCK_RUN 200 Times : 7812
  BYTE_RUN  200 Times : 670300
  这段代码可以清楚的告诉我们,在进行文件操作时,字节方式与块方式到底有多大的效率差别
  一般情况下,两者处理同样的数据,块读写要比字节读写快100倍
  如果数据读写在核心代码段外,这个差别还不大,只是一个时间的叠加
  如果数据读写在核心代码段内,这时候,算法效率是时间的乘积(也就是说,仅仅是文件操作方式的改变,代码的运行效率就要差100倍)
  不要小看这100倍,很多时候,数据处理都是一种极限问题.
  如果说,做一次数据处理用一天的话,没人会有反对意见,但是,要是做两年呢,我看就没人会同意
  
  建议在做大数据量处理时,先尽可能多的把数据一次读入,然后自己编写解析代码
*/
搜索更多相关主题的帖子: return 
2010-12-27 22:36
快速回复:老江湖碰到的新问题
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.019281 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved