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