1.获得随机数的方法:
rand()函数用来生成随机数,但严格意义上来说只是生成了伪随机数。在使用默认种子不变的情况下,如果在程序内循环,那么下一次生成随机数时会调用上一次rand()的结果作为种子。但是使用默认种子不变的情况下,每次执行程序得到的随机数都是相同的。
此时可使用srand()来指定种子数,通常用当前时间作为种子:srand(time(0))。具体情况如下:
#include <cstdlib>
#include <ctime>
srand(time(0));
while(1)
{
result = rand() % 100;
}
注意:千万不能把srand()写入循环当中,否则得出的随机数与当前时间有关,同一秒内的随机数是相同的。

2.位域(位段)
位域是一种数据结构,所谓位域是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的为数;每个域有一个域名,允许在程序中按域名进行操作。如此一来,就能把几个不同对象用一个字节的二进制位域来表示。
struct 位域结构名
{ 类型说明符 位域名:位域长度; };
(1)一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域;当然也可以有意使某位域从下一单元开始。
struct bs
{
  unsigned a:4;
  unsigned :0;    /*空域*/
  unsigned b:4;   /*从下一单元开始存放*/
  unsigned c:4;
};
(2)位域的长度不能大于一个字节的长度,即不能超过8位。
(3)位域可以无位域名,这时它只用来填充或者调整位置,例如:
struct k
{
  int a:1;
  int :2;     /*该2位不能使用*/
  int b:3;
  int c:2;
};
位域的使用方法和结构相同,没有什么区别。
注意:对于位域的对齐方式,和结构类似。比如int a : 4,首先会给int类型分配4个字节,然后将高4位分给a,如果a前后变量也是int,可以合用这4个字节(除非已经超出4个字节的范围)。最终整个位段结构也要根据最大长度的类型来对齐。

3.6种位运算符
&  按位与
|   按位或
^  按位异或
~  取反            //不会改变原值
<< 左移           //不会改变原值
>> 右移          //不会改变原值

4.数值分为整型和实型,整型就是不带小数位的数,例如char、int等,实型则是指带小数位的数,也称浮点数,例如double、float等。

5.左值和右值
凡是可以被赋值的表达式,就叫左值,左值总是直接的或间接的变量(返回引用类型的函数也可以作为左值)。
凡是可以赋值给左值的,就叫右值,右值可以是变量、常量、函数调用等。
所有的左值都是右值,反之不见得。
注意:并不是左值就是可以赋值的值。可以被赋值的左值称为modifiable l-values,不可赋值的左值称为nonmodifiable l-values,包括有:
const int i=3;       //i是左值,但是不可能被赋值
int a[10];            //a不可能被赋值
struct Node a;   //一个struct或者union中包含const,不可赋值
++a=20;        //a是一个变量,可被程序寻址,可以改变它的值
a++=20;        //a++是一个引用了临时对象的表达式,所以是右值
a+5=20;        //a+5也是引用了临时对象的表达式,不能寻址该对象,是右值

6.sizeof求长度
32位系统中,对于char a[10]这样的数组来说,sizeof(a)的值为10;对于char *p=“12345”这样的指针来说,sizeof(p)的值为4,要求p指向的字符串长度,需要用strlen函数。
对于char *p=malloc(100)这样的指针来说,sizeof(p)的值为4,要求分配内存的大小,需要用到_msize函数,size_t  _msize(void *)在#include <malloc.h>中
对于函数总void Function(char a[100]),相当于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();
}
运行结果
   2293628 4199056 abc
   2293624 2293624 abc
   2293620 4199056 abc
分析:
char *c1 = "abc"中的字符串是常量字符串,存储在常量区中;char c2[] = "abc"的字符串存储在栈中;char *c3 = ( char* )malloc(3)存储在堆中;c3 = "abc"使得c3指向新的常量字符串,而不是对动态申请的内存进行更新;同时,c1,c2,c3这三个变量都是局部变量,只不过存储的是指向的内存地址,占4个字节的长度。
&c2和c2指向同一个地址,只不过&c2指向的是char[3]数组类型,c2指向的是char类型;c1和c3指向同一个地址,因为c3指向新的字符串,而且经过编译器优化后,与c1指向同一个字符串。
&c1、&c2、&c3的值分别为2293620、2293624、2293628,这是因为指针c1和c2占了4个字节的长度,而c2指向的数组恰好也占了4个字节的长度。

9.结构体对齐问题
结构体中成员对齐的条件是:
(1)每个成员按照自己的方式对齐,对齐规则是每个成员按照类型的大小和对齐参数(默认是8字节)中较小的一个来对齐;
(2)整体结构的默认对齐方式是按照最长的成员和对齐参数中较小的那个来对齐。
(3)总的来说,对齐方式无非是1、2、4、8、16……等。
设置对齐的预编译命令是:
#pragma pack(1)
其中pack()中的数字表示对齐参数(字节数),不写表示默认为8字节。
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.
静态定义的变量对象称为有名对象;动态定义的变量对象称为无名对象。
动态定义的对象初始化:需要使用初始化式显示初始化,且动态定义的类型数组不能初始化。
动态建立类数组时,必须要有缺省的构造函数,因为数组不可添加初始化式。

11.memcpy / memmove / strstr / strcpy
函数原型:void *memmove(void *dest, const void *source, size_t count)
返回值说明:返回指向dest的void *指针
参数说明:dest,source分别为目标串和源串的首地址。count为要移动的字符的个数
函数说明:memmove用于从source拷贝count个字符到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。

函数原型:void *memcpy(void *dest, const void *source, size_t count);
返回值说明:返回指向dest的void *指针
函数说明:memcpy功能和memmove相同,但是memcpy中dest和source中的区域不能重叠,否则会出现未知结果。

原型:char *strstr(char *haystack, char *needle);
用法:#include <string.h>
功能:从字符串haystack中寻找needle第一次出现的位置(不比较结束符NULL)。
说明:返回指向第一次出现needle位置的指针,如果没找到则返回NULL。

函数原型: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 的区别:
函数memcpy()从source指向的区域向dest指向的区域复制count个字符,如果两数组重叠,不定义该函数的行为。而memmove()如果两函数重叠,赋值仍正确进行。memcpy函数假设要复制的内存区域不存在重叠,如果你能确保你进行复制操作的的内存区域没有任何重叠,可以直接用memcpy;如果你不能保证是否有重叠,为了确保复制的正确性,你必须用memmove。memcpy的效率会比memmove高一些,如果还不明白的话可以看一些两者的实现:

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)),从后往前执行,先做++ptr,指向8,然后输出8和8。
注:一般禁止在语句中这样使用++运算符,因为会依赖不同的编译器而有所不同。例如,TC中printf语句从右向左执行,VC6.0中同样从右向左执行,但是在遇见后增运算符a++或者a--时是在整个printf语句后才执行。
int i=8;
printf("%d\n%d\n%d\n%d\n",++i,--i,i++,i--);
这个函数的输出结果在VC里是8 7 8 8,在TC里是8 7 7 8。
int i=3, j;
j = (i++)*(i++);
这个表达式输出j是9,i最后是5,因为两个i++是在整个运算式之后才进行的。
int i=3, j;
j = (++i)*(++i);
这个表达式输出j是25,i最后是5,因为++i相当于i加1后将i放入式子中,所以j=i*i,且i为5。
int i=3, j;
printf(“%d %d”, ++i, ++i);
这个表达式输出5, 4,为什么两个值不一样呢?因为printf是函数,将++i传给printf后,相当于打印的是形参,而两个++i传入的形参不同。
注:特别要注意宏、运算与函数使用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语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理;C++语言有很严格的类型安全检查,不允许上述情况(指函数不加类型声明)发生。可是编译器并不一定这么认定,譬如在Visual C++6.0中上述add函数的编译无错也无警告且运行正确。
#include "stdio.h"
int fun()
{
return 1;
}

main()
{
printf("%d",fun(2));
getchar();
}
在C语言中,可以给无参数的函数传送任意类型的参数,但是在C++编译器中编译同样的代码则会出错。所以,无论在C还是C++中,若函数不接受任何参数,一定要指明参数为void。
按照ANSI(American National Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的:
void * pvoid;
pvoid++;       //ANSI:错误
pvoid += 1;    //ANSI:错误
ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。
例如:
int *pint;
pint++;        //ANSI:正确
pint++的结果是使其增大sizeof(int)。
但是大名鼎鼎的GNU(GNU's Not Unix的缩写)则不这么认定,它指定void *的算法操作与char *一致。
因此下列语句在GNU编译器中皆正确:
pvoid++;      //GNU:正确
pvoid += 1;   //GNU:正确
pvoid++的执行结果是其增大了1。
在实际的程序设计中,为迎合ANSI标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码:
void * pvoid;
char* tmp = (char*)pvoid;
tmp++;    //ANSI:正确;GNU:正确
tmp += 1; //ANSI:正确;GNU:正确
GNU和ANSI还有一些区别,总体而言,GNU较ANSI更“开放”,提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合ANSI标准。