1.獲得隨機(jī)數(shù)的方法:
rand()函數(shù)用來生成隨機(jī)數(shù),但嚴(yán)格意義上來說只是生成了偽隨機(jī)數(shù)。在使用默認(rèn)種子不變的情況下,如果在程序內(nèi)循環(huán),那么下一次生成隨機(jī)數(shù)時會調(diào)用上一次rand()的結(jié)果作為種子。但是使用默認(rèn)種子不變的情況下,每次執(zhí)行程序得到的隨機(jī)數(shù)都是相同的。
此時可使用srand()來指定種子數(shù),通常用當(dāng)前時間作為種子:srand(time(0))。具體情況如下:
#include <cstdlib>
#include <ctime>
srand(time(0));
while(1)
{
result = rand() % 100;
}
注意:千萬不能把srand()寫入循環(huán)當(dāng)中,否則得出的隨機(jī)數(shù)與當(dāng)前時間有關(guān),同一秒內(nèi)的隨機(jī)數(shù)是相同的。
2.位域(位段)
位域是一種數(shù)據(jù)結(jié)構(gòu),所謂位域是把一個字節(jié)中的二進(jìn)位劃分為幾個不同的區(qū)域,并說明每個區(qū)域的為數(shù);每個域有一個域名,允許在程序中按域名進(jìn)行操作。如此一來,就能把幾個不同對象用一個字節(jié)的二進(jìn)制位域來表示。
struct 位域結(jié)構(gòu)名
{ 類型說明符 位域名:位域長度; };
(1)一個位域必須存儲在同一個字節(jié)中,不能跨兩個字節(jié)。如一個字節(jié)所剩空間不夠存放另一位域時,應(yīng)從下一單元起存放該位域;當(dāng)然也可以有意使某位域從下一單元開始。
struct bs
{
unsigned a:4;
unsigned :0; /*空域*/
unsigned b:4; /*從下一單元開始存放*/
unsigned c:4;
};
(2)位域的長度不能大于一個字節(jié)的長度,即不能超過8位。
(3)位域可以無位域名,這時它只用來填充或者調(diào)整位置,例如:
struct k
{
int a:1;
int :2; /*該2位不能使用*/
int b:3;
int c:2;
};
位域的使用方法和結(jié)構(gòu)相同,沒有什么區(qū)別。
注意:對于位域的對齊方式,和結(jié)構(gòu)類似。比如int a : 4,首先會給int類型分配4個字節(jié),然后將高4位分給a,如果a前后變量也是int,可以合用這4個字節(jié)(除非已經(jīng)超出4個字節(jié)的范圍)。最終整個位段結(jié)構(gòu)也要根據(jù)最大長度的類型來對齊。
3.6種位運算符
& 按位與
| 按位或
^ 按位異或
~ 取反 //不會改變原值
<< 左移 //不會改變原值
>> 右移 //不會改變原值
4.數(shù)值分為整型和實型,整型就是不帶小數(shù)位的數(shù),例如char、int等,實型則是指帶小數(shù)位的數(shù),也稱浮點數(shù),例如double、float等。
5.左值和右值
凡是可以被賦值的表達(dá)式,就叫左值,左值總是直接的或間接的變量(返回引用類型的函數(shù)也可以作為左值)。
凡是可以賦值給左值的,就叫右值,右值可以是變量、常量、函數(shù)調(diào)用等。
所有的左值都是右值,反之不見得。
注意:并不是左值就是可以賦值的值??梢员毁x值的左值稱為modifiable l-values,不可賦值的左值稱為nonmodifiable l-values,包括有:
const int i=3; //i是左值,但是不可能被賦值
int a[10]; //a不可能被賦值
struct Node a; //一個struct或者union中包含const,不可賦值
++a=20; //a是一個變量,可被程序?qū)ぶ罚梢愿淖兯闹?br>a++=20; //a++是一個引用了臨時對象的表達(dá)式,所以是右值
a+5=20; //a+5也是引用了臨時對象的表達(dá)式,不能尋址該對象,是右值
6.sizeof求長度
32位系統(tǒng)中,對于char a[10]這樣的數(shù)組來說,sizeof(a)的值為10;對于char *p=“12345”這樣的指針來說,sizeof(p)的值為4,要求p指向的字符串長度,需要用strlen函數(shù)。
對于char *p=malloc(100)這樣的指針來說,sizeof(p)的值為4,要求分配內(nèi)存的大小,需要用到_msize函數(shù),size_t _msize(void *)在#include <malloc.h>中。
對于函數(shù)總void Function(char a[100]),相當(dāng)于void Function(char *a),所以sizeof(a)的值為4。
7.局部變量能和全局變量重名,則局部變量屏蔽全局變量。需要使用全局變量時使用"::"符號。“::”表示全局有效域。
8.
main()
{
char *c1 = "abc";
char c2[] = "abc";
char *c3 = ( char* )malloc(3);
c3 = "abc";
printf("%d %d %s\n",&c1,c1,c1);
printf("%d %d %s\n",&c2,c2,c2);
printf("%d %d %s\n",&c3,c3,c3);
getchar();
}
運行結(jié)果
2293628 4199056 abc
2293624 2293624 abc
2293620 4199056 abc
分析:
char *c1 = "abc"中的字符串是常量字符串,存儲在常量區(qū)中;char c2[] = "abc"的字符串存儲在棧中;char *c3 = ( char* )malloc(3)存儲在堆中;c3 = "abc"使得c3指向新的常量字符串,而不是對動態(tài)申請的內(nèi)存進(jìn)行更新;同時,c1,c2,c3這三個變量都是局部變量,只不過存儲的是指向的內(nèi)存地址,占4個字節(jié)的長度。
&c2和c2指向同一個地址,只不過&c2指向的是char[3]數(shù)組類型,c2指向的是char類型;c1和c3指向同一個地址,因為c3指向新的字符串,而且經(jīng)過編譯器優(yōu)化后,與c1指向同一個字符串。
&c1、&c2、&c3的值分別為2293620、2293624、2293628,這是因為指針c1和c2占了4個字節(jié)的長度,而c2指向的數(shù)組恰好也占了4個字節(jié)的長度。
9.結(jié)構(gòu)體對齊問題
結(jié)構(gòu)體中成員對齊的條件是:
(1)每個成員按照自己的方式對齊,對齊規(guī)則是每個成員按照類型的大小和對齊參數(shù)(默認(rèn)是8字節(jié))中較小的一個來對齊;
(2)整體結(jié)構(gòu)的默認(rèn)對齊方式是按照最長的成員和對齊參數(shù)中較小的那個來對齊。
(3)總的來說,對齊方式無非是1、2、4、8、16……等。
設(shè)置對齊的預(yù)編譯命令是:
#pragma pack(1)
其中pack()中的數(shù)字表示對齊參數(shù)(字節(jié)數(shù)),不寫表示默認(rèn)為8字節(jié)。
struct{
short a1;
short a2;
short a3;
}A;
//sizeof(A) = 6
struct{
long a1;
short a2;
}B;
//sizeof(B) = 8
#pragma pack(8)
struct S1{
char a;
long b;
};
struct S2 {
char c;
struct S1 d;
long long e;
};
//sizeof(S2) = 24
#pragma pack(1)
struct{
short a1;
short a2;
short a3;
}A;
//sizeof(A) = 6
struct{
long a1;
short a2;
}B;
#pragma pack(pop)
//sizeof(B) = 6
10.
靜態(tài)定義的變量對象稱為有名對象;動態(tài)定義的變量對象稱為無名對象。
動態(tài)定義的對象初始化:需要使用初始化式顯示初始化,且動態(tài)定義的類型數(shù)組不能初始化。
動態(tài)建立類數(shù)組時,必須要有缺省的構(gòu)造函數(shù),因為數(shù)組不可添加初始化式。
11.memcpy / memmove / strstr / strcpy
函數(shù)原型:void *memmove(void *dest, const void *source, size_t count)
返回值說明:返回指向dest的void *指針
參數(shù)說明:dest,source分別為目標(biāo)串和源串的首地址。count為要移動的字符的個數(shù)
函數(shù)說明:memmove用于從source拷貝count個字符到dest,如果目標(biāo)區(qū)域和源區(qū)域有重疊的話,memmove能夠保證源串在被覆蓋之前將重疊區(qū)域的字節(jié)拷貝到目標(biāo)區(qū)域中。
函數(shù)原型:void *memcpy(void *dest, const void *source, size_t count);
返回值說明:返回指向dest的void *指針
函數(shù)說明:memcpy功能和memmove相同,但是memcpy中dest和source中的區(qū)域不能重疊,否則會出現(xiàn)未知結(jié)果。
原型:char *strstr(char *haystack, char *needle);
用法:#include <string.h>
功能:從字符串haystack中尋找needle第一次出現(xiàn)的位置(不比較結(jié)束符NULL)。
說明:返回指向第一次出現(xiàn)needle位置的指針,如果沒找到則返回NULL。
函數(shù)原型:char * strcpy(char * strDest,const char * strSrc);
char * strcpy(char * strDest,const char * strSrc)
{
if ((strDest==NULL)||(strSrc==NULL))
throw "Invalid argument(s)";
char * strDestCopy=strDest;
while ((*strDest++=*strSrc++)!='\0')
;
return strDestCopy;
}
memcpy和memmove 的區(qū)別:
函數(shù)memcpy()從source指向的區(qū)域向dest指向的區(qū)域復(fù)制count個字符,如果兩數(shù)組重疊,不定義該函數(shù)的行為。而memmove()如果兩函數(shù)重疊,賦值仍正確進(jìn)行。memcpy函數(shù)假設(shè)要復(fù)制的內(nèi)存區(qū)域不存在重疊,如果你能確保你進(jìn)行復(fù)制操作的的內(nèi)存區(qū)域沒有任何重疊,可以直接用memcpy;如果你不能保證是否有重疊,為了確保復(fù)制的正確性,你必須用memmove。memcpy的效率會比memmove高一些,如果還不明白的話可以看一些兩者的實現(xiàn):
void *memmove(void *dest, const void *source, size_t count)
{
assert((NULL != dest) && (NULL != source));
char *tmp, *s;
if (dest <= src)
{
tmp = (char *) dest;
s = (char *) src;
while (count--)
*tmp++ = *s++;
}
Else
{
tmp = (char *) dest + count;
s = (char *) src + count;
while (count--)
*--tmp = *--s;
}
return dest;
}
12.++i與i++
int arr[] = {6,7,8,9,10};
int *ptr = arr;
*(ptr++) +=123;
Print(“%d %d”, *ptr, *(++ptr));
一定要注意求值順序,*(ptr++) +=123中先做加法6+123,然后ptr++,此時指針指向7;對于Print(“%d %d”, *ptr, *(++ptr)),從后往前執(zhí)行,先做++ptr,指向8,然后輸出8和8。
注:一般禁止在語句中這樣使用++運算符,因為會依賴不同的編譯器而有所不同。例如,TC中printf語句從右向左執(zhí)行,VC6.0中同樣從右向左執(zhí)行,但是在遇見后增運算符a++或者a--時是在整個printf語句后才執(zhí)行。
int i=8;
printf("%d\n%d\n%d\n%d\n",++i,--i,i++,i--);
這個函數(shù)的輸出結(jié)果在VC里是8 7 8 8,在TC里是8 7 7 8。
int i=3, j;
j = (i++)*(i++);
這個表達(dá)式輸出j是9,i最后是5,因為兩個i++是在整個運算式之后才進(jìn)行的。
int i=3, j;
j = (++i)*(++i);
這個表達(dá)式輸出j是25,i最后是5,因為++i相當(dāng)于i加1后將i放入式子中,所以j=i*i,且i為5。
int i=3, j;
printf(“%d %d”, ++i, ++i);
這個表達(dá)式輸出5, 4,為什么兩個值不一樣呢?因為printf是函數(shù),將++i傳給printf后,相當(dāng)于打印的是形參,而兩個++i傳入的形參不同。
注:特別要注意宏、運算與函數(shù)使用i++和++i的情況,容易讓人困惑……
13.
add ( int a, int b )
{
return a + b;
}
int main(int argc, char* argv[])
{
printf ( "2 + 3 = %d", add ( 2, 3) );
return 0;
}
在C語言中,凡不加返回值類型限定的函數(shù),就會被編譯器作為返回整型值處理;C++語言有很嚴(yán)格的類型安全檢查,不允許上述情況(指函數(shù)不加類型聲明)發(fā)生??墒蔷幾g器并不一定這么認(rèn)定,譬如在Visual C++6.0中上述add函數(shù)的編譯無錯也無警告且運行正確。
#include "stdio.h"
int fun()
{
return 1;
}
main()
{
printf("%d",fun(2));
getchar();
}
在C語言中,可以給無參數(shù)的函數(shù)傳送任意類型的參數(shù),但是在C++編譯器中編譯同樣的代碼則會出錯。所以,無論在C還是C++中,若函數(shù)不接受任何參數(shù),一定要指明參數(shù)為void。
按照ANSI(American National Standards Institute)標(biāo)準(zhǔn),不能對void指針進(jìn)行算法操作,即下列操作都是不合法的:
void * pvoid;
pvoid++; //ANSI:錯誤
pvoid += 1; //ANSI:錯誤
ANSI標(biāo)準(zhǔn)之所以這樣認(rèn)定,是因為它堅持:進(jìn)行算法操作的指針必須是確定知道其指向數(shù)據(jù)類型大小的。
例如:
int *pint;
pint++; //ANSI:正確
pint++的結(jié)果是使其增大sizeof(int)。
但是大名鼎鼎的GNU(GNU's Not Unix的縮寫)則不這么認(rèn)定,它指定void *的算法操作與char *一致。
因此下列語句在GNU編譯器中皆正確:
pvoid++; //GNU:正確
pvoid += 1; //GNU:正確
pvoid++的執(zhí)行結(jié)果是其增大了1。
在實際的程序設(shè)計中,為迎合ANSI標(biāo)準(zhǔn),并提高程序的可移植性,我們可以這樣編寫實現(xiàn)同樣功能的代碼:
void * pvoid;
char* tmp = (char*)pvoid;
tmp++; //ANSI:正確;GNU:正確
tmp += 1; //ANSI:正確;GNU:正確
GNU和ANSI還有一些區(qū)別,總體而言,GNU較ANSI更“開放”,提供了對更多語法的支持。但是我們在真實設(shè)計時,還是應(yīng)該盡可能地迎合ANSI標(biāo)準(zhǔn)。