網路城邦
上一篇 回創作列表 下一篇  字體:
一位讀者來信來電問 C 程式的問題
2009/07/11 13:03:51瀏覽1621|回應0|推薦2
2009 年 7 月 10 日晚上十一點,工作室的電話響了,筆者正納悶是誰這麼晚了還打電話,接了電話後才知道是一位讀者來電問一道 C 程式的問題 (e-mail 的寄發時間是當天的晚上六點)。他的問題如下:


你好
我跟你買過書,我買那一本C語言的修練與實踐,我有一個程式的問題想請教你一下。
這一個我所附上的範例,我有各問題。
很困惑?

是這樣的我宣告一個buf  4k 但試我卻塞給他64k 的資料,我根據你的書上第四章所講的
我有點疑惑。

我那個buf 宣告成不同的型態和放置的位置所的到的結果都不一樣。


我分別宣告 buf 為

char buf [4096]
static char buf [4096]
以上兩個我都宣告在 main 


我另一個宣告是放在 gloable放在main 上面。


我所知知道的事
宣告在main 裡面的變數
char buf[4096 ] 會規劃在 stack 那邊,我只要一run 就馬上 segmentation fault 
我宣告成static char buf[4096] 會規劃在data area  但是run 起來不會出錯

我宣告在gloable 時buf 會規劃在bss 那邊 run 起來也不會出錯


我明明宣告一個4k 的資料但是卻塞進去64k 資料,但是當我宣告buf 在不同的位置時確有不一樣的答案
請問一下,這要如何解釋呢?

謝謝你

這位讀者所附的程式如下:

#include <stdio.lh>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <mem.h>
typedef int uint16_t;
typedef unsigned char uint8_t;
typedef unsigned long  uint32_t;
unsigned char data[64 *1024];
int memcpy4(char *dst, unsigned char *src, int size);
char testbuf[2] = {0x01, 0x05};
char buf[4096]= {0};
int main(){
    long i = 0, test = 0;
 
    //static char buf[4096]= {0};
    //char buf[4096]= {0};
    //char testbuf[2] = {0x01, 0x05};
 
    for(test = 0; test < testbuf[1]; test++){
        printf("test is %ld\n",test);
        printf("testbuf[0] is %x\n",testbuf[0]);
        printf("testbuf[1] is %x\n",testbuf[1]);   
    }   
 
    for(i = 0; i < (1024 * 64); i++){
        data[i] = i;
        //printf(" data[%ld] is %ld\n",i, (long)data[i]);
    }    
    memcpy4(buf, data, 65536);
    printf(" Finish\n");
 
    #if 0
    for(test = 0; test < testbuf[1]; test++){
        printf("2222 test is %ld\n",test);
        printf("2222 testbuf[0] is %x\n",testbuf[0]);
        printf("2222 testbuf[1] is %x\n",testbuf[1]);   
    }
    #endif   
    return 0;
}   

int memcpy4(char *dst, unsigned char *src, int size)
{
    long *ls = (long *)src;
    uint32_t data;
    for(; size >= 4; size-=4, ls++, dst+=4) {
        //printf("3333333333 size is %d\n",size);
        data = *ls;
        memcpy(dst, &data, sizeof(long));
    }
    if (size) {
        data = *ls;
        printf("4444444444444444444444444444\n");
        memcpy(dst, &data, size);
    }
    return 0;
}


筆者的回答如下:

4K buf 在前,64K data 在後,將 64K data 內容拷貝至 4K buf 時又蓋到自己(64K data array)。至於為什麼不會發生 segmentation fault, 因為這些位址都已經分配給這支程式,是這支程式合法取得的記憶體資源,只是這支程式的邏輯在做拷貝時又不小心地蓋到自己,即 Source。

筆者略微增修幾行程式 (以紅色顯示),即可證明這支程式在「將 64K data 內容拷貝至 4K buf 時又蓋到自己」。


 

#include
#include
#include
#include
#include
//#include
typedef int uint16_t;
typedef unsigned char uint8_t;
typedef unsigned long  uint32_t;
unsigned char data[64 *1024];
int memcpy4(char *dst, unsigned char *src, int size);
char testbuf[2] = {0x01, 0x05};
char buf[4096]= {0};
int main(){
    long i = 0, test = 0;
   
    //static char buf[4096]= {0};
    //char buf[4096]= {0};
    //char testbuf[2] = {0x01, 0x05};
   
    for(test = 0; test < testbuf[1]; test++){
        printf("test is %ld\n",test);
        printf("testbuf[0] is %x\n",testbuf[0]);
        printf("testbuf[1] is %x\n",testbuf[1]);   
    }   
  
    for(i = 0; i < (1024 * 64); i++){
        data[i] = i;
        //printf(" data[%ld] is %ld\n",i, (long)data[i]);
    }
    printf("data[0] = %d, data[1] = %d, data[2] = %d\n",
           data[0], data[1], data[2]);

    printf("&buf = %p, &data = %p\n", buf, data);
   
    memcpy4(buf, data, 65536);
    printf(" Finish\n");
   
    printf("data[0] = %d, data[1] = %d, data[2] = %d\n",
           data[0], data[1], data[2]);

    #if 0
    for(test = 0; test < testbuf[1]; test++){
        printf("2222 test is %ld\n",test);
        printf("2222 testbuf[0] is %x\n",testbuf[0]);
        printf("2222 testbuf[1] is %x\n",testbuf[1]);   
    }
    #endif   
    return 0;
}   

int memcpy4(char *dst, unsigned char *src, int size)
{
    long *ls = (long *)src;
    uint32_t data;
    for(; size >= 4; size-=4, ls++, dst+=4) {
        //printf("3333333333 size is %d\n",size);
        //data = *ls;
        data = 0xffffffff;
        memcpy(dst, &data, sizeof(long));
    }
    if (size) {
        data = *ls;
        printf("4444444444444444444444444444\n");
        memcpy(dst, &data, size);
    }
    return 0;
}


執行結果如下:

test is 0
testbuf[0] is 1
testbuf[1] is 5
test is 1
testbuf[0] is 1
testbuf[1] is 5
test is 2
testbuf[0] is 1
testbuf[1] is 5
test is 3
testbuf[0] is 1
testbuf[1] is 5
test is 4
testbuf[0] is 1
testbuf[1] is 5
data[0] = 0, data[1] = 1, data[2] = 2
&buf = 20d20, &data = 21d28
 Finish
data[0] = 255, data[1] = 255, data[2] = 255


這支程式有一些值得討論與改進的地方:

1. data 被宣告成 unsigned char,而 for loop 的邏輯是
    for (i = 0; i < 1024 * 64; i++)
      data[i] = i;

    事實上,data[i] 的值只可能介於 [0, 255],筆者有點不瞭解此處的意圖為何。


2. 有一些不必要的 type cast 與 assignment,例如 memcpy4 中的

    
long *ls = (long *)src;  與
    
data = *ls;

     其實原程式可以直接寫成 memcpy((void*)dst, (void*)src, sizeof(long));

3. 原程式使用了太多的 magic number (即寫死的常數值 ... constants, literals)


最後,筆者想說的是


晚上十點過後還打電話是不太禮貌的行為,要稍微留意一下!
( 創作其他 )
推薦文章 列印 加入我的文摘
上一篇 回創作列表 下一篇

引用
引用網址:https://classic-blog.udn.com/article/trackback.jsp?uid=chungchia&aid=3123810