一、預處理
1、什么是預編譯?何時需要預編譯?
(1)預編譯又稱預處理,是做些代碼文本的替換工作,即程序執行前的一些預處理工作。主要處理#開頭的指令,如拷貝#include包含的文件代碼、替換#define定義的宏、條件編譯#if等。
(2)何時需要預編譯:
a.總是使用不經常改動的大型代碼體;
b.程序由多個模塊組成,所有模塊都使用一組標准的包含文件和相同的編譯選項。在這種情況下,可以將所有包含文件預編譯為一個預編譯頭。
2、寫一個“標准”宏,這個宏輸入兩個參數並返回較小的一個
#define MIN(x, y) ((x)<(y)?(x):(y)) //注意結尾沒有;C語言面試知識
3、#與##的作用?
#是把宏參數轉化為字符串的運算符,##是把兩個宏參數連接的運算符。
例如:
#define STR(arg) #arg //則宏STR(hello)展開時為”hello”
#define NAME(y) name_y //則宏NAME(1)展開時仍為name_y
#define NAME(y) name_##y //則宏NAME(1)展開為name_1
#define DECLARE(name, type) typename##_##type##_type,
//則宏DECLARE(val, int)展開為int val_int_type
4、如何避免頭文件被重復包含?
例如,為避免頭文件my_head.h被重復包含,可在其中使用條件編譯:
#ifndef _MY_HEAD_H
#define _MY_HEAD_H /*空宏*/
/*其他語句*/
#endif
5、#include<file.h> 與 #include "file.h"的區別?
前者是從Standard Library的路徑尋找和引用file.h,而后者是從當前工作路徑搜尋並引用file.h。
二、關鍵字
1、register:
(1)register關鍵字的作用:
請求CPU盡可能讓變量的值保存在CPU內部的寄存器中,減去CPU從內存中抓取數據的時間,提高程序運行效率。
(2)register作用的實現原理:
擴展:CPU組成,計算機系統組成,數據處理流程 。
(3)什么時候使用register?
一般,我們將頻繁被訪問的變量,用register修飾。
(因為CPU內存資源是有限的,是稀缺的,不可能將所有變量都聲明為register變量)
(4)使用register關鍵字應注意什么?
a.只有局部變量才可以被聲明用register修飾;
(register不能修飾全局變量和函數的原因:全局變量可能被多個進程訪問,而用register修飾的變量,只能被當前進程訪問)
b.不能用取地址獲取用register修飾的變量的地址;
(原因:變量保存在寄存器中,而取地址獲取的地址的是內存的地址)
c.用register修飾的變量一定要是CPU所接受的數據類型。
2、static:
(1)static關鍵字的作用:
static既可以修飾變量,也可以修飾函數,修飾變量時,既可以修飾局部,也可修飾全局。
static修飾靜態局部變量,延長變量的生命周期,直至程序結束,這個變量才釋放;
static修飾全局變量,使其只可在本文件可訪問,其他文件不可見;
(static修飾的變量都保存在數據段靜態數據區中,未初始化時,系統將默認初始化為0)
static修飾函數,使其只可在本文件可調用,其他文件不可調用;
(2)什么時候使用static修飾變量?
當希望一個變量直至程序結束才釋放時,用static修飾靜態局部變量;
當希望一個全局變量只可在本文件可訪問,其他文件不可見時,用Static修飾全局變量;
當希望一個函數只可在本文件可調用,其他文件不可調用時,用Static修飾函數;
3、extern:
(1)extern關鍵字的作用:
extern用來外部聲明一個全局變量,這個全局變量在另一個文件中被定義。
(2)使用extern關鍵字應注意什么?
標明數據類型,例:extern int count;
(3)什么時候使用extern修飾變量?
在a.c文件中想使用b.c文件中的全局變量時,用extern 外部聲明。
(4)用於extern "C"
當文件為CPP文件時,通過extern “C”告訴C++編譯器,extern “C”{}里包含的函數都用C的方式來編譯:
a.可以是單一語句;
extern "C" double sqrt(double);
b.可以是復合語句, 相當於復合語句中的聲明都加了extern "C";
extern "C"
{
double sqrt(double);
int min(int, int);
}
c.可以包含頭文件,相當於頭文件中的聲明都加了extern "C";
extern "C"
{
#include <cmath>
}
d.不可以將extern "C" 添加在函數內部;
e.如果函數有多個聲明,可以都加extern "C", 也可以只出現在第一次聲明中,后面的聲明會接受第一個鏈接指示符的規則;
f.除extern "C", 還有extern "FORTRAN" 等。
4、const:
(1)const關鍵字的作用:
const修飾變量,是這個變量變成只讀變量,變量對應的空間的值是可變的,但不能用變量名來修改空間中的值。
(2)使用const關鍵字應注意什么?
使用const關鍵字修飾的變量,一定要對變量進行初始化。
int *const p = # p++ ✘
const int *p = # (*p)++ ✘
int const *p = # (*p)++ ✘
(離誰進,誰就不可以改變)
(3)什么時候使用const修飾變量?
a.聲明常變量,使得指定的變量不能被修改;
const int a = 5;/*a的值一直為5,不能被改變*/
const int b; b = 10;/*b的值被賦值為10后,不能被改變*/
const int *ptr; /*ptr為指向整型常量的指針,ptr的值可以修改,但不能修改其所指向的值*/
int *const ptr;/*ptr為指向整型的常量指針,ptr的值不能修改,但可以修改其所指向的值*/
const int *const ptr;/*ptr為指向整型常量的常量指針,ptr及其指向的值都不能修改*/
b.修飾函數形參,使得形參在函數內不能被修改,表示輸入參數;
如int fun(const int a);或int fun(const char *str);
c.修飾函數返回值,使得函數的返回值不能被修改。
const char *getstr(void);使用:const *str= getstr();
const int getint(void); 使用:const int a =getint();
(4)const與#define 相比,有何優點?
(1)const作用:定義常量、修飾函數參數、修飾函數返回值。
被const修飾的東西都受到強制保護,可以預防意外的變動,能提高程序的健壯性。
(2)const 常量有數據類型,而宏常量沒有數據類型。編譯器可以對前者進行類型安全檢查。而對后者只進行字符替換,沒有類型安全檢查,並且在字符替換可能會產生意料不到的錯誤。
(3)有些集成化的調試工具可以對const 常量進行調試,但是不能對宏常量進行調試。
5、typedef:
(1)typedef關鍵字的作用:
聲明一個已經存在的數據類型的同義字,給數據類型定義一個新名字,提高了移植性;簡化復雜的類型聲明,提高編碼效率;解釋數據類型的作用。
6、voliate:
(1)voliate關鍵字的作用:
volatile指定的變量可能被系統、硬件、進程/線程改變,強制編譯器每次從內存中取得該變量的值,而不是從被優化后的寄存器中讀取。
簡單來說,就是自己所定義的變量在程序運行過程中一直會變,如果希望這個值被正確處理,就需要每次從內存中去讀這個值,這樣就不會有錯誤了。
(2)什么情況下用volatile關鍵字?
a.中斷服務程序中修改的供其他程序檢測的變量需要加volatile;
b.多任務環境下各任務間共享的標志應該加volatile;
c.存儲器映射的硬件寄存器通常也要加volatile說明,因此每次對它的讀寫都可能有不同意義。
(3)一個參數既可以是const還可以是volatile嗎?
可以,例如只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
(4)一個指針可以是volatile 嗎?
可以,盡管這並不常見。例如當一個中服務子程序修該一個指向buffer的指針時。
7、inline:
(1)inline關鍵字的作用:
內聯inline是給編譯器的優化提示,如果一個函數被編譯成inline的話,那么就會把函數里面的代碼直接插入到調用這個函數的地方,而不是用調用函數的形式。
(2)使用inline關鍵字應注意什么?
內聯是以代碼膨脹(復制)為代價,僅僅省去了函數調用的開銷,從而提高函數的執行效率。如果執行函數體內代碼的時間,相比於函數調用的開銷較大,那么效率的收獲會很少。另一方面,每一處內聯函數的調用都要復制代碼,將使程序的總代碼量增大,消耗更多的內存空間。
8、sizeof:
(1)sizeof關鍵字的作用:
sizeof是在編譯階段處理,且不能被編譯為機器碼。sizeof的結果等於對象或類型所占的內存字節數。sizeof的返回值類型為size_t。
(2)sizeof的取值:
變量:int a; sizeof(a)為4;
指針:int *p; sizeof(p)為4;
數組:int b[10]; sizeof(b)為數組的大小,4*10;int c[0]; sizeof(c)等於0;
結構體:struct (int a; char ch;)s1; sizeof(s1)為8 ,因為結構體字節需要對齊。
注意:不能對結構體中的位域成員使用sizeof
sizeof(void)等於1
sizeof(void *)等於4
三、變量
1、全局變量和局部變量在內存中的區別?
全局變量儲存在靜態數據區,局部變量在堆棧中。
2、局部變量能否和全局變量重名?
能,局部會屏蔽全局。要用全局變量,需要使用"::"。
局部變量可以與全局變量同名,在函數內引用這個變量時,會用到同名的局部變量,而不會用到全局變量。對於有些編譯器而言,在同一個函數內可以定義多個同名的局部變量,比如在兩個循環體內都定義一個同名的局部變量,而那個局部變量的作用域就在那個循環體內
3、如何引用一個已經定義過的全局變量?
可以用引用頭文件的方式,也可以用extern關鍵字,如果用引用頭文件方式來引用某個在頭文件中聲明的全局變量,假定你將那個變量寫錯了,那么在編譯期間會報錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,那么在編譯期間不會報錯,而在連接期間報錯。
4、全局變量能否定義在被多個.C文件包含的頭文件中?
可以,在不同的C文件中以static形式來聲明同名全局變量。
可以在不同的C文件中聲明同名的全局變量,前提是其中只能有一個C文件中對此變量賦初值,此時連接不會出錯。
三、結構體
1、結構體的賦值?
C語言中對結構體變量的賦值或者在初始化或者在定義后按字段賦值。
方式1:初始化
struct tag
{
char a;
int b;
}x = {‘A’, 1};/*初始化*/
或
struct tag
{
char a;
int b;
};
struct tag x = {‘A’,1};/*在定義變量時初始化*/
方式2:定義變量后按字段賦值
struct tag
{
char a;
int b;
};
struct tag x;/*定義變量*/
x.a =‘A’;/*按字段賦值*/
x.b = 1; /*按字段賦值*/
此時使用初始化的方式來賦值時,如x = {‘A’,1};則出錯。
方式3:結構變量間的賦值
struct tag
{
chara;
int b;
};
struct tag x,y;
x.a=’A’;
x.b=1;
y = x;/*結構變量間直接賦值*/
2、結構體變量如何比較?
雖然結構體變量之間可以通過=直接賦值,但不能通過比較符(==)來比較,因為比較符只作用於基本數據類型。這個時候,只能通過int memcmp(const void *s1, const void *s2, size_t n);來進行內存上的比較。
3、結構體位域
(1)位域的定義:
位域是一個或多個位的字段,不同長度的字段(如聲明為unsigned int類型)存儲於一個或多個其所聲明類型的變量中(如整型變量中)。
(2)位域的類型:
可以是char、short、int,多數使用int,使用時最好帶上signed或unsigned。
(3)位域的特點:
字段可以不命名,如unsigned int :1;可用來填充;unsigned int :0; 0寬度用來強制在下一個整型(因此處是unsigned int類型)邊界上對齊。
(4)位域的使用:
struct st1
{
unsigned char a:7;/*字段a占用了一個字節的7個bit*/
unsigned char b:2;/*字段b占用了2個bit*/
unsigned char c:7;/*字段c占用了7個bit*/
}s1;
sizeof(s1)等於3。因為一個位域字段必須存儲在其位域類型的一個單元所占空間中,不能橫跨兩個該位域類型的單元。也就是說,當某個位域字段正處於兩個該位域類型的單元中間時,只使用第二個單元,第一個單元剩余的bit位置補0。
struct st2
{
unsigned int a:31;
unsigned int b:2;/*前一個整型變量只剩下1個bit,容不下2個bit,所以只能存放在下一個整型變量*/
unsigned int c:31;
}s2;
於是可知sizeof(s2)等於3*sizeof(int)即12。
(5)位域的好處:
a.有些信息在存儲時,並不需要占用一個完整的字節,而只需占幾個或一個二進制位。例如在存放一個開關量時,只有0和1 兩種狀態,用一位二進位即可。這樣節省存儲空間,而且處理簡便。這樣就可以把幾個不同的對象用一個字節的二進制位域來表示。
b.可以很方便的利用位域把一個變量給按位分解。比如只需要4個大小在0到3的隨機數,就可以只rand()一次,然后每個位域取2個二進制位即可,省時省空間。
(6)位域的缺點:
不同系統對位域的處理可能有不同的結果,如位段成員在內存中是從左向右分配的還是從右向左分配的,所以位域的使用不利於程序的可移植性。
4、結構體成員數組大小為0
結構體數組成員的大小為0是GNU C的一個特性。好處是可以在結構體中分配不定長的大小。如:
typedef struct st
{
int a;
int b;
char c[0];
}st_t;
sizeof(st_t)等於8,即char c[0]的大小為0。
5、結構體與聯合體的區別?
(1)結構和聯合都是由多個不同的數據類型成員組成, 但在任何同一時刻, 聯合中只存放了一個被選中的成員(所有成員共用一塊地址空間), 而結構的所有成員都存在(不同成員的存放地址不同)。
(2)對於聯合的不同成員賦值, 將會對其它成員重寫, 原來成員的值就不存在了, 而對於結構的不同成員賦值是互不影響的。
四、數據類型和函數
1、bool,int,float,指針類型的變量a 與0如何比較?
(1)bool : if ( !a ) or if(a)
(2)int : if ( a == 0)
(3)float : const EXPRESSION EXP = 0.000001
if ( a < EXP && a >-EXP)
(4)pointer : if ( a != NULL) or if(a == NULL)
2、函數參數入棧順序
C語言函數參數入棧順序是從右向左的,這是由編譯器決定的,更具體的說是函數調用約定決定了參數的入棧順序。C語言采用是函數調用約定是__cdecl的,所以對於函數的聲明,完整的形式是:int __cdecl func(int a, int b);
3、什么函數不能聲明為虛函數?
Constructor
五、數組與指針
1、“引用”與指針的區別?
(1)引用必須被初始化,指針不必。
(2)引用初始化以后不能被改變,指針可以改變所指的對象。
(3)不存在指向空值的引用,但是存在指向空值的指針。
(4)指針通過某個指針變量指向一個對象后,對它所指向的變量間接操作。程序中使用指針,程序的可讀性差;而引用本身就是目標變量的別名,對引用的操作就是對目標變量的操作。
(5)流操作符<<和>>、賦值操作符=的返回值、拷貝構造函數的參數、賦值操作符=的參數、其它情況都推薦使用引用。
2、數組與指針的區別?
數組要么在靜態存儲區被創建(如全局數組),要么在棧上被創建。指針可以隨時指向任意類型的內存塊。
(1)修改內容上的差別
char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 編譯器不能發現該錯誤,運行時錯誤
(2)用運算符sizeof 可以計算出數組的容量(字節數)。sizeof(p),p 為指針得到的是一個指針變量的字節數,而不是p 所指的內存容量。C++/C 語言沒有辦法知道指針所指的內存容量,除非在申請內存時記住它。
//計算數組和指針的內存容量
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字節
cout<< sizeof(p) << endl; // 4 字節
注意當數組作為函數的參數進行傳遞時,該數組自動退化為同類型的指針。
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 字節而不是100 字節
}
六、內存分配回收
1、malloc/free與new/delete的區別?
(1)malloc與free是C/C++語言的標准庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。
(2)對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,以及一個能完成清理與釋放內存工作的運算符delete。注意new/delete不是庫函數。
(3)不要企圖用malloc/free來完成動態對象的內存管理,應該用new/delete。由於內部數據類型的“對象”沒有構造與析構的過程,對它們而言malloc/free和new/delete是等價的。
(4)既然new/delete的功能完全覆蓋了malloc/free,為什么C++不把malloc/free淘汰出局呢?這是因為C++程序經常要調用C函數,而C程序只能用malloc/free管理動態內存。如果用free釋放“new創建的動態對象”,那么該對象因無法執行析構函數而可能導致程序出錯。如果用delete釋放“malloc申請的動態內存”,結果也會導致程序出錯,但是該程序的可讀性很差。所以new/delete必須配對使用,malloc/free也一樣。
2、malloc(0)返回值是什么?
如果請求的長度為0,則標准C語言函數malloc返回一個null指針或不能用於訪問對象的非null指針,該指針能被free安全使用。
3、程序的內存分配是怎樣的?
一個由C/C++編譯的程序占用的內存分為以下幾個部分:
(1)棧區(stack)—由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
(2)堆區(heap)—一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表,呵呵。
(3)全局區(靜態區)(static)—全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束后由系統釋放。
(4)文字常量區—常量字符串就是放在這里的。程序結束后由系統釋放。
(5)程序代碼區—存放函數體的二進制代碼
例子程序:
//main.cpp
int a=0; //全局初始化區
char *p1; //全局未初始化區
main()
{
intb;棧
char s[]="abc"; //棧
char *p2; //棧
char *p3="123456"; //123456\0在常量區,p3在棧上。
static int c=0; //全局(靜態)初始化區
p1 = (char*)malloc(10);
p2 = (char*)malloc(20); //分配得來得10和20字節的區域就在堆區。
strcpy(p1,"123456"); //123456\0放在常量區,編譯器可能會將它與p3所向"123456"優化成一個地方。
}
4、堆(heap)和棧(stack)的區別?
(1)申請方式
stack:由系統自動分配。
例如,聲明在函數中一個局部變量int b;系統自動在棧中為b開辟空間。
heap:需要程序員自己申請,並指明大小,在C中用malloc函數,
如p1=(char*)malloc(10);
但是注意p1本身是在棧中的。
(2)申請后系統的響應
棧:只要棧的剩余空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。
堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。
(3)申請大小的限制
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
(4)申請效率的比較:
棧:由系統自動分配,速度較快。但程序員是無法控制的。
堆:是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。
另外,在WINDOWS下,最好的方式是用Virtual Alloc分配內存,他不是在堆,也不是在棧,而是直接在進程的地址空間中保留一塊內存,雖然用起來最不方便。但是速度快,也最靈活。
(5)堆和棧中的存儲內容
棧:在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可執行語句)的地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變量。注意靜態變量是不入棧的。
當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容由程序員安排。
(6)存取效率的比較
char s1[]="aaaaaaaaaaaaaaa";
char *s2="bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運行時刻賦值的;
而bbbbbbbbbbb是在編譯時就確定的;
但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。
比如:
#include
voidmain()
{
char a=1;
char c[]="1234567890";
char *p="1234567890";
a = c[1];
a = p[1];
return;
}
對應的匯編代碼
10:a=c[1];
004010678A4DF1movcl,byteptr[ebp-0Fh]
0040106A884DFCmovbyteptr[ebp-4],cl
11:a=p[1];
0040106D8B55ECmovedx,dwordptr[ebp-14h]
004010708A4201moval,byteptr[edx+1]
004010738845FCmovbyteptr[ebp-4],al
第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字符,顯然慢了。
(7)堆棧溢出的原因?
沒有回收垃圾資源;層次太深的遞歸調用。
5、為什么需要內存對齊?
為了提高程序的性能,數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的內存,處理器需要作兩次內存訪問;然而,對齊的內存訪問僅需要一次訪問。
其他
1、不能做switch()的參數類型?
switch的參數不能為實型。
2、語句for( ;1 ;)有什么問題?
和while(1)相同,無限循環。
3、do……while和while……do有什么區別?
前一個循環一遍再判斷,后一個判斷以后再循環
4、三種基本的數據模型?
按照數據結構類型的不同,將數據模型划分為層次模型、網狀模型和關系模型。
5、判斷一段程序是由C 編譯程序還是由C++編譯程序編譯的?
#ifdef __cplusplus
cout<<"c++";
#else
cout<<"c";
#endif
6、無限循環的幾種寫法?
(1)while(1){}
(2)for(;;){}
(3)Loop: ... goto Loop;
7、ASSERT()的作用?
ASSERT()是一個調試程序時經常使用的宏,在程序運行時它計算括號內的表達式,如果表達式為FALSE (0), 程序將報告錯誤,並終止執行。如果表達式不為0,則繼續執行后面的語句。這個宏通常原來判斷程序中是否出現了明顯非法的數據,如果出現了終止程序以免導致嚴重后果,同時也便於查找錯誤。例如,變量n在程序中不應該為0,如果為0可能導致錯誤,可以這樣寫程序:
......
ASSERT( n != 0);
k = 10/ n;
.....
ASSERT只有在Debug版本中才有效,如果編譯為Release版本則被忽略。
assert()的功能類似,它是ANSI C標准中規定的函數,它與ASSERT的一個重要區別是可以用在Release版本中。
8、system("pause");的作用?
系統的暫停程序,按任意鍵繼續,屏幕會打印,"按任意鍵繼續。。。。。"省去了使用getchar();
9、C++的類和C里面的struct有什么區別?
C++中的類具有成員保護功能,並且具有繼承,多態這類OO特點,而C里的struct沒有。C里面的struct沒有成員函數,不能繼承,派生等等。
10、動態連接庫的兩種方式?
(1)載入時動態鏈接(load-time dynamic linking),模塊非常明確調用某個導出函數,使得他們就像本地函數一樣。這需要鏈接時鏈接那些函數所在DLL的導入庫,導入庫向系統提供了載入DLL時所需的信息及DLL函數定位。
(2)運行時動態鏈接(run-time dynamic linking),運行時可以通過LoadLibrary或LoadLibraryEx函數載入DLL。DLL載入后,模塊可以通過調用GetProcAddress獲取DLL函數的出口地址,然后就可以通過返回的函數指針調用DLL函數了。如此即可避免導入庫文件了。
11、程序什么時候應該使用多線程?
(1)耗時的操作使用線程,提高應用程序響應
(2)並行操作時使用線程,如C/S架構的服務器端並發線程響應用戶的請求。
(3)多CPU系統中,使用線程提高CPU利用率
(4)改善程序結構。一個既長又復雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程序會利於理解和修改。
其他情況都使用單線程。
12、多進程與多線程的區別?
(1)進程:子進程是父進程的復制品。子進程獲得父進程數據空間、堆和棧的復制品。
(2)線程:相對與進程而言,線程是一個更加接近與執行體的概念,它可以與同進程的其他線程共享數據,但擁有自己的棧空間,擁有獨立的執行序列。
(3)兩者都可以提高程序的並發度,提高程序運行效率和響應時間。
(4)線程和進程在使用上各有優缺點:線程執行開銷小,但不利於資源管理和保護;而進程相反。同時,線程適合於在SMP機器上運行,而進程則可以跨機器遷移。
13、用變量a給出下面的定義
a) 一個整型數(An integer)
b) 一個指向整型數的指針(A pointer to an integer)
c) 一個指向指針的的指針,它指向的指針是指向一個整型數(A pointer to a pointer to an integer)
d) 一個有10個整型數的數組(An array of 10 integers)
e) 一個有10個指針的數組,該指針是指向一個整型數的(An array of 10 pointers to integers)
f) 一個指向有10個整型數數組的指針(A pointer to an array of 10 integers)
g) 一個指向函數的指針,該函數有一個整型參數並返回一個整型數(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數( An array of ten pointers to functions that take an integer
argument and return an integer )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
-----------END----------

(中庭地白樹棲鴉,冷露無聲濕桂花。今夜月明人盡望,不知秋思落誰家。)
