12個滑稽的C語言面試問答——《12個有趣的C語言問答》評析(4)


前文鏈接:http://www.cnblogs.com/pmer/p/3324063.html

 

8,Making changes in Code segment
Q:以下代碼運行時一定會崩潰,你能說出原因嗎? 

#include<stdio.h>

int main(void)
{
    char *ptr = "Linux";
    *ptr = 'T';

    printf("\n [%s] \n", ptr);

    return 0;
}

A:這是因為,通過 *ptr = 'T',此行代碼嘗試更改只讀內存存儲的字符串'Linux'。此操作當然行不通所以才會造成崩潰。

Answer: This is because, through *ptr = ‘T’, the code is trying to change the first byte of the string ‘Linux’ kept in the code (or the read-only) segment in the memory. This operation is invalid and hence causes a seg-fault or a crash.

 

評:

  很難說這段代碼一定崩潰(crash)或發生段錯誤(seg-fault)。
  在C標准中,修改String literal(The program attempts to modify a string literal)是一種UB(undefined behavior),即未定義行為。用通俗的話來講,就是C語言沒規定這樣寫有意義。這種情況下,什么事情都可能發生,無論發生什么,都是代碼的錯誤,是程序員的責任。

  事實上,修改String literal可能導致各種結果,有些情況下也可能執行時並不產生崩潰或段錯誤。

  K&R認為修改String literal應該規定為unspecified behavior(不同於undefined behavior,unspecified behavior可能有多種結果,但代碼本身不存在語義錯誤,而undefined behavior是沒有語義),但標准委員會並沒有采納K&R的建議。

  對照一下原文,不難發現譯文漏掉了in the code segment。

  但原文說string literal存於代碼段或只讀段,也是不正確的。這種說法沒有依據。C語言並沒有規定實現應該把string literal存放在什么地方,這是由實現自己安排的事情。

 

9,Process that changes its own name Q:你能否寫一個程序在它運行時修改它的名稱? (Can you write a program that changes its own name when run?)
A:以下代碼可以完成 :
Answer: Following piece of code tries to do the required : 

#include<stdio.h>

int main(int argc, char *argv[])
{
    int i = 0;
    char buff[100];

    memset(buff,0,sizeof(buff));

    strncpy(buff, argv[0], sizeof(buff));
    memset(argv[0],0,strlen(buff));

    strncpy(argv[0], "NewName", 7);

    // Simulate a wait. Check the process
    // name at this point.
    for(;i<0xffffffff;i++);

    return 0;
}

 

評:

  這個問答比較雷人。

  首先標題是要改變Process的名字,問題是Process有名字嗎?

  但在Question中又說要在運行時改Program的名字。如果說Program有名字,那么就只可能是對應可執行文件的名字。可執行文件的名字和Process的名字是無法划等號的。

  Process通常是通過PCB(Process Control Block)管理,其標識的方法通常是id號,當然,PCB中也有所謂外部標識符。如果把PCB的外部標識符理解為Process的名字,雖然極為牽強,但舍此似乎也無法再有任何其他解釋了。

  查了一下,某些語言中似乎有Process Name這個概念,也有相關的函數。但是C語言中沒有這個概念。

  再看代碼,無非是修改了一下argv[0]所指向的字符串而已。盡管argv[0]指向的字符串確實是程序的名稱,但這種改變沒有實際意義——下次啟動程序還得用原來的名字,因為外存中可執行文件的名字並沒有改變。

  代碼中的注釋部分又提到了Process Name,就這樣一會Process Name,一會又是Program Name,題目的設計者自己明顯概念不清。

  注釋說在這里模擬等待,搞不清究竟要在這里等什么。怎么看都覺得是在裝模作樣。
  再來看代碼風格。 

 int i = 0;
  //……
 for(;i<0xffffffff;i++);

  把for語句分為兩部分來寫,風格乖張。

  把循環體部分的空語句寫在行尾,也是一種惡習。對比一下下面的寫法就知道了: 

for ( i = 0 ; i < 0xffffffff ; i ++ )
   ;

  這條for語句本身的語義在一定條件下也成問題。假如int類型的最大值為0x7fffffff,當i值達到int類型最大值之后,i再自加就溢出了。而溢出,是一種未定義行為(UB)。

  也就是說,這條語句說的很可能並不是讓i值從0逐次加1一直變化到0xffffffff-1。代碼作者心里想的究竟是什么,也許他自己都不知道。順便說一下,int類型的最大值為0x7fffffff時,0xffffffff不是int類型,而是unsigned int類型。

    char buff[100];

    memset(buff,0,sizeof(buff));

    strncpy(buff, argv[0], sizeof(buff));
    memset(argv[0],0,strlen(buff));

       strncpy(argv[0], "NewName", 7);

  這段代碼,依然是很蠢。首先莫名其妙地把buff清0: 

  memset(buff,0,sizeof(buff));

  實際上去掉這句沒有任何問題。因為后面的 

  strncpy(buff, argv[0], sizeof(buff));

  還要再次填充\0。舉個例子,下面代碼段 

   char s[4];
   strncpy( s , "1" , 4);
   printf("%d %d %d %d\n",s[0],s[1],s[2],s[3]);

的輸出,一定是 

  49 0 0 0

  這就說明,原來代碼中memset()填充的0毫無意義,因為緊接着還要再填充一次。我不是說memset()沒用,但到現在為止我看到的大多數memset()都是稀里糊塗地在做無用功,是在濫用memset()。再次說明了很多使用memset()的人根本不知道自己的代碼究竟是在做什么。所謂對“大型商業程序,這是良好代碼風格的習慣”雲雲,純粹是胡扯。

  后面一句

memset(argv[0],0,strlen(buff));

不但沒用,邏輯上也有點荒唐。應該

memset(argv[0],0,strlen(argv[0]));

才合理。盡管我們知道,這里strlen(argv[0])和strlen(buff)的值是一樣的。 

strncpy(argv[0], "NewName", 7);

  這一句屬於不懂裝懂、煞有介事。作者使用strncpy()希望不至於拷貝越界,但這里的7是"NewName"的長度,而不是argv[0]所指向字符串的長度。如果strlen(argv[0])小於7,顯然還會發生越界。 

  綜上所述,這段代碼不但不可能改變Program Name,連正確地修改命令行參數都談不上,更不要說改變Process Name了。  

  所以,這個問答的“問”,是一個嘩眾取寵的偽問題。“答”則是生拉硬扯、胡編濫造的解答。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM