C語言——老江湖不容忽視的新問題你有遇到嗎?
C語言代碼碰到的問題,都是些小問題,但是,越是小問題,越是不好解決,希望對大家有所幫助.
有句話說的好,你有一個思想,我有一個思想,大家交流一下,就是兩個思想,共同進步吧.
// test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
// 編寫代碼的幾類問題
// 1 fscanf
// 2 文件操作
// 3 變數邊界
// 4 編譯順序
// 5 計算精度
// 6 邊界對齊
// 7 指針與逗號語句
// 8 指針應用
// 9 死循環
// 10 文件系統
// 11 改變演算法流程
// 12 改變演算法模型
想要一起學習C 的可以加裙二四八八九四四三零,有很多大神一起學習交流,有資源,然後可以訂閱一下
/*
// ================== 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
{
//fscanf( f , "%x" , &buf[ i ] ) ; // A
fscanf( f , "%x" , &buf[ 9-i ] ) ; // B
}
fclose( f ) ;
printf( "
End : " ) ;
for( i = 0 ; i
{
printf( "%02x " , buf[ i ] ) ;
}
printf( "
" ) ;
return 0;
}*/
/*
這段代碼完成的工作就是將文件中的十個數字,從9-0反序放到數組buf中,但是,在實際執行過程中,有下面兩個問題
1. 會產生溢出錯誤....,A,B均有
2. 在B方式下,輸出結果全0
這個問題實在是有點讓人莫名其妙的
給大家一個分析這個問題的辦法,在//B方案下面添加如下代碼:
printf( "
NO.%d : " , i ) ;
for( int j = 0 ; 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
unsigned int buf[ 1
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
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 的可以加裙二四八八九四四三零,有很多大神一起學習交流,有資源,然後可以訂閱一下
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
{
m_cBuf[ m_ca ] = m_ca ; // A
}
for( m_ucb = 0 ; m_ucb
{
m_cBuf[ m_ucb ] = m_ucb ; // B
}
i = 0 ;
i-- ;
for( j = 0 ; j
{
}
}*/
/*
A 有符號的字元型值的範圍是 -127-128 ,這段代碼會讓你很爽的把內存改的亂七八糟,還是個死循環。。。
B 數組初始化只有一種結果:死循環 無符號字元型的表達範圍為 0- 255,永遠不可能達到 256
C 上述循環會運行2的32次方減一次 !!!
printf( "
%u
" , i ) ;
*/
/*
// ================== 編譯順序 ==================
int main( int argc , char* argv[ ] )
{
int i , j ;
i = 1 ;
j = 2 ;
printf( "
i = 1 , j = 2 , i += ( 4 + j ) = %d , i += 4 + j = %d
" , (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( "
Test Float : %12.10f
" , m_fb);
// 此時將數據類型改為double(雙精度),會有比較滿意的結果
double m_dfa , m_dfb ;
m_dfa = 1 / MAX_DOUBLE_NUMBER ;
m_dfb = m_dfa * MAX_DOUBLE_NUMBER ;
printf( "
Test Double : %12.10f
" , 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("
Struct A : %d Struct B : %d
" , sizeof( m_stTesta ) , sizeof( m_stTestb ) ) ;
return 0 ;
}
*/
/* 依照正常的方式來計算,兩個結構體的長度都應該是7,但實際結果是 8 和12
產生這個問題的原因就是編譯器的邊界對齊問題。
編譯器為了提高代碼效率,或者保證代碼正確運行,會對數據的起點進行相應的處理
例如:
有些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 )++ ) ) )
l |= ( ( ( unsigned long ) ( *( ( c )++ ) ) )
l |= ( ( ( unsigned long ) ( *( ( c )++ ) ) )
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( "
SIZEOF_POINT " ) ;
printf( "
CHAR_SIZEOF : %08x - INT_SIZEOF : %08x " , sizeof( m_cpPoint ) , sizeof( m_ipPoint ) ) ;
for( i = 0 ; i
{
m_caData[ i ] = ( i + 1 ) % 0xff ;
}
printf( "
POINT START " ) ;
printf( "
CHAR %p : %08x - %08x " , m_cpPoint , m_cpPoint , *m_cpPoint ) ;
printf( "
INT %p : %08x - %08x " , m_ipPoint , m_ipPoint , *m_ipPoint ) ;
printf( "
POINT + 4 " );
m_cpPoint += 4 ;
m_ipPoint += 4 ;
printf( "
CHAR %p : %08x - %08x %08x ", m_cpPoint , m_cpPoint , *m_cpPoint , *m_cpPoint + 6 ) ;
printf( "
INT %p : %08x - %08x %08x ", m_ipPoint , m_ipPoint , *m_ipPoint , *m_ipPoint + 6 ) ;
printf( "
, COMMAND AND CHAR_POINT MOVEMENT : " ) ;
i = HOST_c2l( m_cpPoint , l ) ;
printf( "
CHAR %p : %08x " , m_cpPoint , *m_cpPoint ) ;
printf( "
I : %08x L: %08x
" , 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
for( i = 0 ; i
{
Buf[ i ] = i + 1 ;
}
Point = (unsigned char * ) Buf ;
for( i = 0 ; i
{
ip_point = ( IPHEAD ) Point ;
printf( "
STEP : %2d" , i ) ;
printf( "
Buf : " ) ;
for( j = 0 ; j
{
printf( "%02x " , Point[ j ] ) ;
}
printf( "
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
美中不足的是,又是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
{
sprintf( name , "%04d.txt" , i ) ;
if( ( f = fopen( name , "w" ) ) == NULL )
{
printf("
Can"t Creat File %s " , name ) ;
return 0 ;
}
fprintf( f, " %04d" , i ) ;
fclose( f ) ;
}
return 1 ;
}
*/
/*
在win系統中,我們進入文件夾後,win文件系統會對這個文件夾下的文件進行一次遍歷以構造一個數據表
顯然,文件數目越多,時間越長(要不停地開內存來放數據,重構數據表)
運行上面的代碼後,打開那個文件夾,你會發現,系統很慢,如果不慢,FILE_NUMEBER 後面的數字改成20000 想要一起學習C 的可以加裙二四八八九四四三零,有很多大神一起學習交流,有資源,然後可以訂閱一下
一般建議一個文件夾下面最多放1000個文件
我最近碰到一兄弟,一文件夾下面放2W個文件!!
機器的情況是:系統不穩定,老死機(不死才怪,一開這個文件夾,win不崩潰都算是奇蹟了)
*/
/*
// ================== 改變演算法流程 ==================
unsigned int Test( unsigned int uCur )
{
return ( ( uCur
}
int main( int argc , char* argv[ ] )
{
Test( 3 ) ;
return 0 ;
}
*/
/*
你能馬上告訴我,Test( 3 ) 的返回值是幾嗎? 我是做不到的
對這個演算法的第一種改進,就是將這個表達式分解成正規的if else 模型
但是,如果只是想到改進,那你就大錯特錯了,其實有更好的辦法來實現這個問題
// 分支法
unsigned int Test( unsigned int uCur )
{
ASSERT( uCur >= 0 && uCur
if( uCur == 1 )
{
return 0 ;
}
if( uCur == 4 )
{
return 2 ;
}
return ( uCur + 1 ) ;
}
// 列表法
unsigned int Test( unsigned int uCur )
{
static const unsigned int uReturn[ ] = { 1, 0 , 3 , 4 , 2 } ;
ASSERT( uCur >= 0 && uCur
return ( uReturn[ uCur ] ) ;
}
*/
// ================== 改變演算法模型 ==================
/*
某程序員為一個ftp伺服器寫一段代碼,不允許用戶上傳可執行代碼
一般可執行代碼的擴展名為: bat,com,exe,於是,生成下面的匹配表
char Refuse_File[ 3 ][ ] = { "bat" , "com" , "exe" } ;
但是,過了幾天,他發現,其實MS_WORD家族的文件類型也是可以執行的,於是,在上面的列表中,添加如下:
char Refuse_File[ 5 ][ ] = { "bat" , "com" , "exe" ,"doc","ppt" } ;
再過了幾天,他又發現,網頁文件也是可執行的,於是,繼續添加列表
char Refuse_File[ 8 ][ ] = { "bat" , "com" , "exe" ,"doc","ppt","htm","html","asp" ,"php" } ;
又過了幾天,發現其實資料庫文件,也有機會執行,繼續這個動作....
到此為止了,我們可以肯定的說,這個列表總會在不停地增漲,無論是從代碼實現的安全功能還是執行效率來說,
這個模型都不理想.
現在,讓我們回到起點,這段代碼到底要做什麼?
不允許用戶上傳可執行代碼
解決這個問題有幾種方法?
1,不允許用戶上傳可執行代碼
2,允許上傳不可執行代碼!!!!
如果我們採用第二套方案,將否定策略改成肯定策略,這個問題就很輕鬆了
想要一起學習C 的可以加裙二四八八九四四三零,有很多大神一起學習交流,有資源,然後可以訂閱一下
顯然這個列表會很小,安全性和效率都得到了很好的解決
這個故事只是想告訴我們,一般情況下,任何問題都有二義性,不要只知其一不知其二
做不出好演算法,不是你不努力,而是你考慮不全面
這段時間工作太忙,都不知道哪兒來這麼多事....
把我前段時間整理出來的問題貼一合集,這是我這十年左右時間碰到的代碼設計問題,都是些小問題,但是,越是小問題,越是不好解決,希望對大家有所幫助.
有句話說的好,你有一個思想,我有一個思想,大家交流一下,就是兩個思想,共同進步吧.
// test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
// 編寫代碼的幾類問題
// 1 fscanf
// 2 文件操作
// 3 變數邊界
// 4 編譯順序
// 5 計算精度
// 6 邊界對齊
// 7 指針與逗號語句
// 8 指針應用
// 9 死循環
// 10 文件系統
// 11 改變演算法流程
// 12 改變演算法模型
/*
// ================== 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
{
//fscanf( f , "%x" , &buf[ i ] ) ; // A
fscanf( f , "%x" , &buf[ 9-i ] ) ; // B
}
fclose( f ) ;
printf( "
End : " ) ;
for( i = 0 ; i
{
printf( "%02x " , buf[ i ] ) ;
}
printf( "
" ) ;
return 0;
}*/
/*
這段代碼完成的工作就是將文件中的十個數字,從9-0反序放到數組buf中,但是,在實際執行過程中,有下面兩個問題
1. 會產生溢出錯誤....,A,B均有
2. 在B方式下,輸出結果全0
這個問題實在是有點讓人莫名其妙的
給大家一個分析這個問題的辦法,在//B方案下面添加如下代碼:
printf( "
NO.%d : " , i ) ;
for( int j = 0 ; 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;
寫這個例子只是想告訴大家,其實很多時候,代碼本身是沒錯的,錯在系統,大家要學代碼設計,一定要記住,任何代碼都是在一定的環境下實現的,要想寫好代碼,一定要對代碼的應用環境有充分的認識。
想要一起學習C 的可以加裙二四八八九四四三零,有很多大神一起學習交流,有資源,然後可以訂閱一下
*/
/*
// ================== 文件操作 ==================
unsigned char pool[ 1
unsigned int buf[ 1
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
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
{
m_cBuf[ m_ca ] = m_ca ; // A
}
for( m_ucb = 0 ; m_ucb
{
m_cBuf[ m_ucb ] = m_ucb ; // B
}
i = 0 ;
i-- ;
for( j = 0 ; j
{
}
}*/
/*
A 有符號的字元型值的範圍是 -127-128 ,這段代碼會讓你很爽的把內存改的亂七八糟,還是個死循環。。。
B 數組初始化只有一種結果:死循環 無符號字元型的表達範圍為 0- 255,永遠不可能達到 256
C 上述循環會運行2的32次方減一次 !!!
printf( "
%u
" , i ) ;
*/
/*
// ================== 編譯順序 ==================
int main( int argc , char* argv[ ] )
{
int i , j ;
i = 1 ;
j = 2 ;
printf( "
i = 1 , j = 2 , i += ( 4 + j ) = %d , i += 4 + j = %d
" , (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( "
Test Float : %12.10f
" , m_fb);
// 此時將數據類型改為double(雙精度),會有比較滿意的結果
double m_dfa , m_dfb ;
m_dfa = 1 / MAX_DOUBLE_NUMBER ;
m_dfb = m_dfa * MAX_DOUBLE_NUMBER ;
printf( "
Test Double : %12.10f
" , m_dfb);
// 結果依然是1.
// 要注意的是,在本例中,double能做的除法長度為10的20次方冪.再長就要採用大數演算法了。
return 0 ;
}
*/
/*
想要一起學習C 的可以加裙二四八八九四四三零,有很多大神一起學習交流,有資源,然後可以訂閱一下
※C語言程序員與程序員老婆的故事
※c語言編程之C語言學習技巧
※C語言編程過程中的一些建議
※想要學習C語言C加加?C語言C加加入門書籍推薦
※《C語言陪我們快半個世紀,有人說它老了,你認為呢》
TAG:C加加 |
※產後抑鬱?這是不容忽視的問題
※大姨媽不準時?這幾個問題不能忽視!
※頭髮泄露你的健康問題?為什麼不早點讓我看到?
※花卉換盆最容易忽略的問題,你也還不知道?
※瑜伽沒有問題,你才有問題
※翡翠開光的講究真不少,看看你都注意到這些問題了嗎
※」我的青春戀愛物語果然有問題「這部動漫有人看懂了嗎?
※看懂這些,認識篆字不是問題 ?
※有問題就解決問題吧,搞不死你的只會讓你看起來更帥氣啊!
※孕前最容易被雙方忽視的幾個問題,你知道嗎?
※做功問題搞不懂?解題思路點開看!
※某火影迷問的幾個問題,不知道有沒有大神能解答?
※老子道德經勸言:做人不可死板,遇到問題要懂得這樣去看
※杜哥講史:琦善,老江湖遇到了新問題
※你挽回愛情中有這些常見問題嗎?
※我說的話孩子怎麼就聽不進去呢?也許你說話的方式有問題!
※養君子蘭最煩惱的4大問題,看看你有沒遇到?
※初婚會遇到很多你想像不到的問題
※當心,女人癢乳頭癢不是什麼好事,這類問題真的不容忽視!