天天看點

家庭作業

家庭作業一(Chapter 2)

P80 2.56

試用不同的示例值來運作show_bytes的代碼。

為了能友善地同屏顯示多個不同的示例值結果,對原有的代碼做了一定的添加修改,使得主函數main中有do-while循環。

源代碼如下:

#include <stdio.h>
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, int len)
{
    int i;
    for(i = 0;i<len;i++)
    {
        printf("%.2x",start[i]);
    }
    printf("\n");
}
void show_int(int x)
{
    show_bytes((byte_pointer) &x, sizeof(int));
}
void show_float(float x)
{
    show_bytes((byte_pointer) &x, sizeof(float));
}
void show_pointer(void *x)
{
    show_bytes((byte_pointer) &x, sizeof(void *));
}
void test_show_bytes(int val)
{
    int ival = val;
    float fval = (float)ival;
    int *pval = &ival;
    printf("show_int = ");
    show_int(ival);
    printf("show_float = ");
    show_float(fval);
    printf("show_pointer = ");
    show_pointer(pval);
}
void main()
{
    int val;
    char flag;
    do
    {
        printf("please enter an int:\n");
        scanf("%d",&val);
        test_show_bytes(val);
        printf("Do you want to continue?(YY or NN)\n");
        fflush(stdin);
        scanf("%c\n",&flag);
        //printf("flag =%c\n",flag);
        fflush(stdin);
    }while(flag == 'y' || flag == 'Y');
}
           

運作結果(以12345,23,45為例) 

家庭作業

P80 2.57

編寫程式showshort,showlong,show_double,它們分别列印類型為short int,long int,double的C語言對象的位元組表示。請嘗試在幾種機器上運作。

與上題中的show_int函數結構類似,都是将原來的資料類型強轉為位元組型然後以二進制形式輸出。

C語言代碼如下:

#include <stdio.h>
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, int len)
{
    int i;
    for(i = 0;i<len;i++)
    {
        printf("%.2x",start[i]);
    }
    printf("\n");
}
void show_short(short int x)
{
    show_bytes((byte_pointer) &x, sizeof(short int));
}
void show_long(long int x)
{
    show_bytes((byte_pointer) &x, sizeof(long int));
}
void show_double(double x)
{
    show_bytes((byte_pointer) &x, sizeof(double));
}
void test_show_bytes(short int sval)
{
    //short int sval = val;
    long int  lval = (long int)sval;
    float fval = (float)sval;
    double dval = (double)fval;
    printf("show_short = ");
    show_short(sval);
    printf("show_long = ");
    show_long(lval);
    printf("show_double = ");
    show_double(dval);
}
void main()
{
    short int sval;
    char flag;
    do
    {
        printf("please enter an short int:\n");
        scanf("%d",&sval);
        test_show_bytes(sval);
        printf("Do you want to continue?(YY or NN)\n");
        fflush(stdin);
        scanf("%c\n",&flag);
        //printf("flag =%c\n",flag);
        fflush(stdin);
    }while(flag == 'y' || flag == 'Y');
}
           

結果如下:

家庭作業

P204 3.58

下面的代碼是在一個開關語句中進行分支選擇的例子。實作各個動作的彙編代碼如圖中的彙編代碼所示。填寫C代碼中缺失的那部分,注意那些會落入其他情況中的情況。

C代碼:

typedef enum{MODE_A,MODE_B,MODE_C,MODE_D,MODE_E} mode_t;
int switch3(int *p1,int *p2,mode_t action)
{
    int result =0;
    switch(action)
    {
        case MODE_A:

        case MODE_B:

        case MODE_C:

        case MODE_D:

        case MODE_E:

        default:

    }
    return result;
}
           

彙編代碼:

.L17:
movl $17,%edx
jmp .L19
.L13:
movl 8(%ebp),%eax
movl (%eax),%edx
movl 12(%ebp),%ecx
movl (%ecx),%eax
movl %eax,(%ecx)
jmp .L19
.L14
movl 12(%ebp),%edx
movl (%edx),%eax
movl %eax, %edx
movl 8(%ebp),%ecx
addl (%ecx),%edx
movl 12(%ebp),%eax
movl %edx,(%eax)
jmp .L19
.L15
movl 12(%ebp),%edx
movl $15,(%edx)
movl 8(%ebp),%ecx
movl (%ecx),%edx
movl .L19
.L16
movl 8(%ebp),%edx
movl (%edx),%eax
movl 12(%ebp),%ecx
movl %eax,(%ecx)
movl $17,%edx
.L19
movl %edx,%eax
           

分析:

.L17部分:直接是result =17;

.L13部分:把P1的值傳給%edx;把p1(指針)傳給了%ecx之後,又将p2的值傳給了%ecx所指向位置——相當于讓指針P1指向原P2所指向的值

.L14部分:根據前面的基礎,movl %edx,(%eax) 一句是讓P2指向了(P1指向的值加上P2指向的值)

.L15部分:直接讓P2指向了15;然後讓P2指向了P1指向的值

.L16部分:讓P2指向了P1指向的值,傳回值設定為17

.L19部分:直接傳回了result的初始值

【注意:關于可能落入其他情況中的情況;我注意到.L16部分是沒有jmp .L19的語句的;然而在給出的C代碼中,.L16對應的MODED之後是.L17對應的MODEE。仔細觀察這兩部分彙編代碼,發現都有将result指派為17的語句。是以得出,MODED落入MODEE】

typedef enum{MODE_A,MODE_B,MODE_C,MODE_D,MODE_E} mode_t;
int switch3(int *p1,int *p2,mode_t action)
{
    int result =0;
    switch(action)
    {
        case MODE_A:
            result = *p1;
            *p1 = *p2;
            break;
        case MODE_B:
            result = *p1+*p2;
            *p2 = result;
            break;
        case MODE_C:
            result = *p1;
            *p2 =15;
            break;
        case MODE_D:
            *p2 = *p1;
        case MODE_E:
            result = 17;
            break;
        default:
            result = -1;
            break;
    }
    return result;
}           

家庭作業二(Chapter 3)

P206 3.60

考慮下面的源代碼,這裡R,S,T都是用#define聲明的常數
int A[R][S][T];
int store_ele(int i,int j,int k,int *dest)
{
    *dest=A[i][j][k];
    return sizeof(A);
}
           
編譯這個程式,GCC産生下面的彙編代碼:(i at %ebp+8,j at %ebp+12,k at %ebp+16,dest at %ebp+20)
movl 8(%ebp),%ecx
movl 12(%ebp),%eax
leal (%eax,%eax,8),%eax
movl %ecx,%edx
sall $6,%edx
subl %ecx,%edx
addl %edx,%eax
addl 16(%ebp),%eax
movl A(,%eax,4),%edx
movl 20(%ebp),%eax
movl %edx,(%eax)
movl $2772,%eax
           

A.将等式(3-1)從二維擴充到三維,提供數組元素A[i][j][k]位置的公式

B.根據彙編代碼,确定R,S,T的值

對上面的彙編代碼逐行翻譯,可以得到以下内容:

1:i
2:j
3:9j
4:i
5:64i
6:63i
7:63i+9j
8:63i+9j+k
9:A+4(63i+9j+k)
10:dest
11:将dest指向%edx的内容
12:2772即A[R][S][T]的大小
           

A.不考慮每個元素的大小,A[i][j]起始地位置是iST+jT;而A[i][j][k]起始的位置就是iST+jT+K。考慮A數組有起始位置并且數組元素也有大小(4),是以真正的位址是A(,iST+j*T+K,4)

B.由第9行的A+4(63i+9j+k)=A(,iST+jT+K,4)可以得到:T=9;ST=63;

再有最後的4RS*T=2772可以得出:

R=11;S=7;T=9

家庭作業三(第八章)

8.10

在這一章裡,我們介紹了一些具有不尋常的調用和傳回行為的函數:setjmp,longjmp,execve,fork。找到下列行為中和每個函數相比對的一種:  A.調用一次,傳回兩次  B.調用一次,從不傳回  C.調用一次,傳回1次或者多次

【解答】  對列舉出來的函數逐個分析:

  • setjmp函數——
    • 代碼:
      int setjmp(imp_buf env);傳回0
      int sigsetjmp(sigjmp_buf env,int savesigs);
                 
    • 解釋:setjmp函數被調用一次而傳回兩次,依次是在儲存目前環境的時候(傳回0),另一次是被每一個相應的longjmp調用(傳回錯誤類型)
  • longjmp函數——與上面的setjmp相配合使用,無傳回值
    • void longjmp(jmp_buf env,int retval);
      void siglongjmp(sigjmp_buf env,int retval);
                 
  • execve函數——
    • int execve(const char *filename,const char *argv[],const char *envp[]);//加載并運作可執行目标檔案,如果成功則無傳回值,如果不成功則傳回-1
                 
  • fork函數——
    • pid_t fork(void);//子程序傳回0;父程序傳回子程序的PID;如果出錯,則為-1
                 
    • 解釋:fork函數被建立之後,将傳回兩次:一次傳回到父程序中,一次傳回到子程序中

是以,

A. 調用一次,傳回兩次: fork  B. 調用一次,從不傳回: execve, longjmp  C. 調用一次,傳回一次或者多次: setjmp

8.16

下面這個程式的輸出是什麼?
#include "csapp.h"
int counter = 1;
int main()
{
    if(fork() == 0)
    {
        counter--;
        exit(0);
    }
    else
    {
        Wait(NULL);
        printf("counter = %d\n",++counter);
    }
    exit(0);
}
           

【解答】 首先,主函數建立了子程序,并将其傳回值加入判斷條件;進而使得子程序執行if語句中的指令;而父程序執行else裡面的指令;子程序會使得在自己從父程序中拷貝來的資料裡的counter從原有的值減一變為0;父程序執行wait之後,若子程序還未結束則被挂起,直到子程序結束,然後列印出來其資料中儲存的counter加上1的結果:2

是以答案為:counter = 2