12個有趣的C語言面試題

摘要:12個C語言面試題,涉及指針、進程、運算、結構體、函數、內存,看看你能做出幾個!

1.gets()函數

問:請找出下面代碼里的問題:

  1. #include<stdio.h>?
  2. int?main(void)?
  3. {?
  4. ????char?buff[10];?
  5. ????memset(buff,0,sizeof(buff));?
  6. ?
  7. ????gets(buff);?
  8. ?
  9. ????printf("\n?The?buffer?entered?is?[%s]\n",buff);?
  10. ?
  11. ????return?0;?
  12. }?

答:上面代碼里的問題在于函數gets()的使用,這個函數從stdin接收一個字符串而不檢查它所復制的緩存的容積,這可能會導致緩存溢出。這里推薦使用標準函數fgets()代替。

2.strcpy()函數

問:下面是一個簡單的密碼保護功能,你能在不知道密碼的情況下將其破解嗎?

  1. #include<stdio.h>?
  2. ?
  3. int?main(int?argc,?char?*argv[])?
  4. {?
  5. ????int?flag?=?0;?
  6. ????char?passwd[10];?
  7. ?
  8. ????memset(passwd,0,sizeof(passwd));?
  9. ?
  10. ????strcpy(passwd,?argv[1]);?
  11. ?
  12. ????if(0?==?strcmp("LinuxGeek",?passwd))?
  13. ????{?
  14. ????????flag?=?1;?
  15. ????}?
  16. ?
  17. ????if(flag)?
  18. ????{?
  19. ????????printf("\n?Password?cracked?\n");?
  20. ????}?
  21. ????else?
  22. ????{?
  23. ????????printf("\n?Incorrect?passwd?\n");?
  24. ?
  25. ????}?
  26. ????return?0;?
  27. }?

答:破解上述加密的關鍵在于利用攻破strcpy()函數的漏洞。所以用戶在向“passwd”緩存輸入隨機密碼的時候并沒有提前檢查“passwd”的容量是否足夠。所以,如果用戶輸入一個足夠造成緩存溢出并且重寫“flag”變量默認值所存在位置的內存的長“密碼”,即使這個密碼無法通過驗證,flag驗證位也變成了非零,也就可以獲得被保護的數據了。例如:

  1. $?./psswd?aaaaaaaaaaaaa?
  2. ?
  3. Password?cracked?

雖然上面的密碼并不正確,但我們仍然可以通過緩存溢出繞開密碼安全保護。

要避免這樣的問題,建議使用 strncpy()函數。

作者注:最近的編譯器會在內部檢測棧溢出的可能,所以這樣往棧里存儲變量很難出現棧溢出。在我的gcc里默認就是這樣,所以我不得不使用編譯命令‘-fno-stack-protector’來實現上述方案。

3.main()的返回類型

問:下面的代碼能 編譯通過嗎?如果能,它有什么潛在的問題嗎?

  1. #include<stdio.h>?
  2. ?
  3. void?main(void)?
  4. {?
  5. ????char?*ptr?=?(char*)malloc(10);?
  6. ?
  7. ????if(NULL?==?ptr)?
  8. ????{?
  9. ????????printf("\n?Malloc?failed?\n");?
  10. ????????return;?
  11. ????}?
  12. ????else?
  13. ????{?
  14. ????????//?Do?some?processing?
  15. ????????free(ptr);?
  16. ????}?
  17. ?
  18. ????return;?
  19. }?

答:因為main()方法的返回類型,這段代碼的錯誤在大多數編譯器里會被當作警告。main()的返回類型應該是“int”而不是“void”。因為“int”返回類型會讓程序返回狀態值。這點非常重要,特別當程序是作為依賴于程序成功運行的腳本的一部分運行時。

4.內存泄露

問:下面的代碼會導致內存泄漏嗎?

  1. #include<stdio.h>?
  2. ?
  3. void?main(void)?
  4. {?
  5. ????char?*ptr?=?(char*)malloc(10);?
  6. ?
  7. ????if(NULL?==?ptr)?
  8. ????{?
  9. ????????printf("\n?Malloc?failed?\n");?
  10. ????????return;?
  11. ????}?
  12. ????else?
  13. ????{?
  14. ????????//?Do?some?processing?
  15. ????}?
  16. ?
  17. ????return;?
  18. }?

答:盡管上面的代碼并沒有釋放分配給“ptr”的內存,但并不會在程序退出后導致內存泄漏。在程序結束后,所有這個程序分配的內存都會自動被處理掉。但如果上面的代碼處于一個“while循環”中,那將會導致嚴重的內存泄漏問題!

提示:如果你想知道更多關于內存泄漏的知識和內存泄漏檢測工具,可以來看看我們在Valgrind上的文章。

5.free()函數

問:下面的程序會在用戶輸入'freeze'的時候出問題,而'zebra'則不會,為什么?

  1. #include<stdio.h>?
  2. ?
  3. int?main(int?argc,?char?*argv[])?
  4. {?
  5. ????char?*ptr?=?(char*)malloc(10);?
  6. ?
  7. ????if(NULL?==?ptr)?
  8. ????{?
  9. ????????printf("\n?Malloc?failed?\n");?
  10. ????????return?-1;?
  11. ????}?
  12. ????else?if(argc?==?1)?
  13. ????{?
  14. ????????printf("\n?Usage??\n");?
  15. ????}?
  16. ????else?
  17. ????{?
  18. ????????memset(ptr,?0,?10);?
  19. ?
  20. ????????strncpy(ptr,?argv[1],?9);?
  21. ?
  22. ????????while(*ptr?!=?'z')?
  23. ????????{?
  24. ????????????if(*ptr?==?'')?
  25. ????????????????break;?
  26. ????????????else?
  27. ????????????????ptr++;?
  28. ????????}?
  29. ?
  30. ????????if(*ptr?==?'z')?
  31. ????????{?
  32. ????????????printf("\n?String?contains?'z'\n");?
  33. ????????????//?Do?some?more?processing?
  34. ????????}?
  35. ?
  36. ???????free(ptr);?
  37. ????}?
  38. ?
  39. ????return?0;?
  40. }?

答:這里的問題在于,代碼會(通過增加“ptr”)修改while循環里“ptr”存儲的地址。當輸入“zebra”時,while循環會在執行前被終止,因此傳給free()的變量就是傳給malloc()的地址。但在“freeze”時,“ptr”存儲的地址會在while循環里被修改,因此導致傳給free()的地址出錯,也就導致了seg-fault或者崩潰。

6.使用_exit退出

問:在下面的代碼中,atexit()并沒有被調用,為什么?

  1. #include<stdio.h>?
  2. ?
  3. void?func(void)?
  4. {?
  5. ????printf("\n?Cleanup?function?called?\n");?
  6. ????return;?
  7. }?
  8. ?
  9. int?main(void)?
  10. {?
  11. ????int?i?=?0;?
  12. ?
  13. ????atexit(func);?
  14. ?
  15. ????for(;i<0xffffff;i++);?
  16. ?
  17. ????_exit(0);?
  18. }?

這是因為_exit()函數的使用,該函數并沒有調用atexit()等函數清理。如果使用atexit()就應當使用exit()或者“return”與之相配合。



7.void*和C結構體

問:你能設計一個能接受任何類型的參數并返回interger(整數)結果的函數嗎?

答:如下:

  1. int?func(void?*ptr)?

如果這個函數的參數超過一個,那么這個函數應該由一個結構體來調用,這個結構體可以由需要傳遞參數來填充。

8.*和++操作

問:下面的操作會輸出什么?為什么?

  1. #include<stdio.h>?
  2. ?
  3. int?main(void)?
  4. {?
  5. ????char?*ptr?=?"Linux";?
  6. ????printf("\n?[%c]?\n",*ptr++);?
  7. ????printf("\n?[%c]?\n",*ptr);?
  8. ?
  9. ????return?0;?
  10. }?

答:輸出結果應該是這樣:

  1. [L]??
  2. ?
  3. [i]?

因為“++”和“*”的優先權一樣,所以“*ptr++”相當于“*(ptr++)”。即應該先執行ptr++,然后才是*ptr,所以操作結果是“L”。第二個結果是“i”。

9.問:修改代碼片段(或者只讀代碼)

問:下面的代碼段有錯,你能指出來嗎?

  1. #include<stdio.h>?
  2. ?
  3. int?main(void)?
  4. {?
  5. ????char?*ptr?=?"Linux";?
  6. ????*ptr?=?'T';?
  7. ?
  8. ????printf("\n?[%s]?\n",?ptr);?
  9. ?
  10. ????return?0;?
  11. }?

答:這是因為,通過*ptr = ‘T’,會改變內存中代碼段(只讀代碼)“Linux”的第一個字母。這個操作是無效的,因此會造成seg-fault或者崩潰。

10.會改變自己名字的進程

問:你能寫出一個在運行時改變自己進程名的程序嗎?

答:參見下面這段代碼:

  1. #include<stdio.h>?
  2. ?
  3. int?main(int?argc,?char?*argv[])?
  4. {?
  5. ????int?i?=?0;?
  6. ????char?buff[100];?
  7. ?
  8. ????memset(buff,0,sizeof(buff));?
  9. ?
  10. ????strncpy(buff,?argv[0],?sizeof(buff));?
  11. ????memset(argv[0],0,strlen(buff));?
  12. ?
  13. ????strncpy(argv[0],?"NewName",?7);?
  14. ?
  15. ????//?Simulate?a?wait.?Check?the?process?
  16. ????//?name?at?this?point.?
  17. ????for(;i<0xffffffff;i++);?
  18. ?
  19. ????return?0;?
  20. }?

11.返回本地變量的地址

問:下面代碼有問題嗎?如果有,該怎么修改?

  1. #include<stdio.h>?
  2. ?
  3. int*?inc(int?val)?
  4. {?
  5. ??int?a?=?val;?
  6. ??a++;?
  7. ??return?&a;?
  8. }?
  9. ?
  10. int?main(void)?
  11. {?
  12. ????int?a?=?10;?
  13. ????int?*val?=?inc(a);?
  14. ????printf("\n?Incremented?value?is?equal?to?[%d]?\n",?*val);?
  15. ?
  16. ????return?0;?
  17. }?

答:盡管上面的程序有時候能夠正常運行,但是在“inc()”中存在嚴重的漏洞。這個函數返回本地變量的地址。因為本地變量的生命周期就是“inc()”的生命周期,所以在inc結束后,使用本地變量會發生不好的結果。這可以通過將main()中變量“a”的地址來避免,這樣以后還可以修改這個地址存儲的值。

12.處理printf()的參數

問:下面代碼會輸出什么?

  1. #include<stdio.h>?
  2. ?
  3. int?main(void)?
  4. {?
  5. ????int?a?=?10,?b?=?20,?c?=?30;?
  6. ????printf("\n?%d..%d..%d?\n",?a+b+c,?(b?=?b*2),?(c?=?c*2));?
  7. ?
  8. ????return?0;?
  9. }?

答:輸出結果是:

  1. 110..40..60?

這是因為C語言里函數的參數默認是從右往左處理的,輸出時是從左往右。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450832.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450832.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450832.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

超全Typora快速入門

文章目錄一.Typora快速入門1.代碼塊2.標題3.字體4.引用5.水平分割線6.圖片插入7.超鏈接8.列表9.表格10.任務列表11.數學表達式12.生成目錄13.表情符號14.定義腳注15.文件導出16.主題修改17.修改主題背景圖片18.設置背景透明度19.Typora 插入圖片調整大小20.字體和顏色21.頁內跳…

聊聊畢業設計系列 --- 系統實現

效果展示 github moment-server github地址 moment github地址 moment-manage github地址 articles 聊聊畢業設計系列 --- 項目介紹 聊聊畢業設計系列 --- 系統實現 前言 在上一篇文章中&#xff0c;主要是對項目做了介紹&#xff0c;并且對系統分析和系統設計做了大概的介紹。…

求職小記(持續更新)

自16年春正式工作以來也有兩年半了&#xff0c;也許是對現在leader的不滿。也許是想要折騰一下&#xff0c;也許還有也許&#xff0c;決定換一份工作&#xff0c;結束兩年零四個月的第一家it工作。從8月份的離職到十月底的offer經歷了很多&#xff0c;外面天慢慢的涼了&#xf…

js 實現用window.print()打印頁面中的部分內容,局部打印

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 如下方法&#xff1a; function printProof(){var printData document.getElementById("forPrint").innerHTML; // 只打印 f…

搞懂靜態變量static

文章目錄1.什么是static&#xff1f;2.static關鍵字的作用是什么&#xff1f;3.靜態變量和非靜態變量的區別&#xff1f;4.static可以修飾局部變量嗎&#xff1f;5.可以通過this訪問靜態變量嗎&#xff1f;6.靜態方法能否調用非靜態方法&#xff1f;7.靜態變量、普通變量、靜態…

神經網絡優化(二) - 滑動平均

1 滑動平均概述 滑動平均&#xff08;也稱為 影子值 &#xff09;&#xff1a;記錄了每一個參數一段時間內過往值的平均&#xff0c;增加了模型的泛化性。 滑動平均通常針對所有參數進行優化&#xff1a;W 和 b&#xff0c; 簡單地理解&#xff0c;滑動平均像是給參數加了一個影…

Docker完全自學手冊

阿里云大學免費課程&#xff1a;Docker完全自學手冊課程介紹&#xff1a;Docker 是 PaaS 提供商 dotCloud 開源的一個基于 LXC 的高級容器引擎&#xff0c;源代碼托管在 Github 上, 基于go語言并遵從Apache2.0協議開源。Docker 是一個開源的應用容器引擎&#xff0c;讓開發者可…

Spring 之注解事務 @Transactional

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 先讓我們看代碼吧&#xff01; 以下代碼為在“Spring3事務管理——基于tx/aop命名空間的配置”基礎上修改。首先修改applicationContext…

超級程序員神話

摘要&#xff1a;大部分的程序員在思想里都會某種程度的承認&#xff0c;承認自己只是一個普通的程序員&#xff0c;但這世界上確實有一些超級程序員&#xff0c;在一個為企業開發應用的程序員和一個為谷歌寫搜索算法的程序員之間&#xff0c;或和一個開發用來控制讀寫頭從磁盤…

HashMap30連問,徹底搞懂HashMap

文章目錄一、背景知識1、什么是Map&#xff1f;2、什么是Hash&#xff1f;3、什么是哈希表&#xff1f;4、什么是HashMap?5、如何使用HashMap&#xff1f;6、HashMap有哪些核心參數&#xff1f;7、HashMap與HashTable的對比&#xff1f;8、HashMap和HashSet的區別&#xff1f;…

博弈論的算法總結

開頭先啰嗦一句&#xff1a;想學好博弈&#xff0c;必然要花費很多的時間&#xff0c;深入學習&#xff0c;不要存在一知半解&#xff0c;應該是一看到題目&#xff0c;就想到博弈的類型。 以及&#xff0c;想不斷重復不斷重復&#xff0c;做大量各大oj網站的題目&#xff0c;最…

Slog55_lua面向對象之lua類

Slog55_lua面向對象之lua類 ArthurSlog SLog-55 Year1 GuangzhouChina Aug 30th 2018 微信掃描二維碼&#xff0c;關注我的公眾號GitHub 掘金主頁 簡書主頁 segmentfault 現實中的事情不是根據人的喜好而定的 比如長在你嘴里的智齒 大部分情況下 你會因為自己&#xff0…

Spring中的@scope注解

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Scope 簡單點說就是用來指定bean的作用域作用域 &#xff08;官方解釋&#xff1a;scope用來聲明IOC容器中的對象應該處的限定場景或者…

編程語言大比拼——誰的效率高

摘要&#xff1a;C、C、Java這幾個屹立不倒的開發語言&#xff0c;如果以功能點作為單位的話&#xff0c;誰的效率最高呢&#xff1f;如果在項目初期就能確定功能點數量&#xff0c;那么就可以很好的預測項目完成時間。這一點是不是對你很有幫助呢&#xff1f; 一份6000個項目的…

Hadoop之Flume詳解

1、日志采集框架Flume   1.1 Flume介紹     Flume是一個分布式、可靠、和高可用的海量日志采集、聚合和傳輸的系統。     Flume可以采集文件&#xff0c;socket數據包等各種形式源數據&#xff0c;又可以將采集到的數據輸出到HDFS、hbase、hive、     kafka等眾多…

搞懂Java的反射機制

搞懂Java的反射機制 1.什么是反射&#xff1f; java的反射機制是指可以在運行狀態下獲取類和對象的所有屬性和方法。 2.反射的作用&#xff1f; 1、在運行時獲取一個類/對象的成員變量和方法 2、在運行時創建一個類的對象 3、在運行時判斷一個對象是否屬于一個類 3.反射有哪些…

表單oninput和onchange事件區別

oninput事件是元素value發生變化是立刻觸發&#xff0c;而onchange是元素發生變化并且失去焦點時才會觸發。 轉載于:https://www.cnblogs.com/ykli/p/9565601.html

Struts2中<s:iterator>基本用法及示例

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Struts2中<s:iterator>基本用法及示例 Iterator用于遍歷集合&#xff08;java.util.Collection&#xff09;或枚舉值&#xff08;j…

如何使用postman做接口測試

1、get請求傳參 只要是get請求都可以在瀏覽器中直接發&#xff1a; 在訪問地址后面拼 ?keyvalue&keyvalue 例如&#xff1a;在瀏覽器中直接輸入訪問地址&#xff0c;后面直接拼需要傳給服務器的參數http://api.nnzhp.cn/api/user/stu_info?stu_name小黑2、post請求&…

【狂神說】分析前后端分離開源項目?

文章目錄1.如何分析開源項目項目簡介項目源碼2.觀察開源項目3.開源項目下載4.跑起來是第一步5.前后端分離項目固定套路6.如何找到一個開源項目1.如何分析開源項目 學習的方式&#xff1a; 不知道這個代碼怎么來的這個代碼跑不起來這個項目對我們有什么幫助&#xff0c;不會模…