一、关键字
int m; 定义创建了对象,并为该对象分配了内存
extern int m; 声明内存,告诉编译器,这个名字已经被预订了
数据从内存拿出来后,先放在寄存器中,然后CPU再从寄存器中读取数据,进行处理。
寄存器是一块块小的存储空间,读取速度比内存快得多。(更贵)
1.1 static
1) static 修饰变量,作用域仅限于变量被定义的文件中,从定义处开始,到文件结束
其他文件 即使用extern也不能使用该变量
Eg:static int j;
void fun1(void)
{
staticint i=0;
i++;
printf("i=%d\n",i);
}
void fun2(void)
{
j=0;
j++;
printf("j=%d\n",j);
}
int main()
{
intk=0;
for(k=0;k<10;k++)
{
fun1();
fun2();
}
return0;
}
运行完后,i=10; j=1;
全局变量,静态变量,如果不赋值,编译器给他赋上0.
如果定义了值,就在编译的时候就确定下来了
http://blog.sina.com.cn/s/blog_615ec1630102uwxk.html
2)static 修饰函数 不指存储方式
而是函数的作用域仅仅在本文件(???内部函数)
1.2 数据类型
32位系统,
char 1 byte(字节)
short 2 bytes
int 4 bytes(unsigned or signed)
long 4 bytes
float 4 bytes
double 2 bytes
1.3 sizeof
int i=0;
(A)sizeof(int) (B)sizeof(i) (C)sizeof int (D)sizeof i
A,B,D 正确,为4 C错误
Sizeof 计算变量所占的内存空间时,括号可以省略
计算类型时,必须加括号
Eg:
int*p=NULL;
sizeof(p); //4
sizeof(*p); //4
inta[100];
sizeof(a); //400 a数组占空间
sizeof(a[100]); //4 a数组中第100个内存地址
sizeof(&a); //4
sizeof(&a[0]); //4
intb[100];
void fun(int b[100]) //传递是数组第一个元素的地址 等同于void fun(int b[])
{
sizeof(b);
}
1.4 signed、unsigned 数据范围
编辑器默认是signed 类型
unsigned char 0~(2^8-1);
signed char: -2^7 ~ 2^7-1;
0 1 2 … 127 -128 -127 -126
Eg:
1、char c=128;
Printf(“c=%d”,c);//-128
2、 int i;
for(i=9;i>=0;i--)
printf("%d ",i); //输出9 8 7 6 5 4 3 2 1 0
3、上面程序i的定义改为
Signed int I; 变成了死循环,无符号值均大于0
在用i作为循环次数时,一定要用signed int 类型
1.5 零值比较--BOOL,int,float,指针变量与零值比较的if语句
BOOL flag 与“零值”比较
正确 if( flag )
if ( !flag )
错误 if(flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)
TRUE 的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE 定义为1,而Visual Basic 则将TRUE 定义为-1。所以我们不可以将布尔变量直接与TRUE、FALSE 或者1、0 进行比较。
float x 与“零值”比较
正确 const float EPSINON= 0.00001;
if ((x >= - EPSINON) && (x <=EPSINON)
错误 if (x == 0.0)
整型变量与零值比较
正确 if (value == 0)
if(value != 0)
错误 if (value) // 会让人误解 value是布尔变量
指针变量与零值比较
int *p=NULL;
正确 if(NULL==p)
If(NULL!=p);
1.6 switch ,case
switch(variable) //variable只能是 整形、字符型常量,或者常量表达式
{
case value1: // program code
break;
case value2: // program code
break;
default: break;
}
Case使用顺序: 按字母或数字顺序
正常放前面,异常放后面
按执行频率排序
简化case语句操作
1.7 break、continue
break 终止本层循环,该层循环体内的语句不执行
continue:终止本次循环,该次循环体内 后面不执行
1.8 void
void * 空类型,任何类型的变量可以对它赋值,反之no
eg:
1、 float *p1;
Int *p2;
P1=p2; //错误,类型不匹配
P1=(float *)P2;
2、 void *p1;
Int *p2;
P1=p2; //ok
p2= p1;//no 空类型无法赋值
不加返回值类型限定的函数,就会被编辑器作为返回整形值。与void不同
1.9 return
终止一个函数,并且返回其后面跟的值。
没有返回值也可以,此时return返回的是一个不确定的值
1.10 const 只读变量
1) const 修饰的只读变量必须在定义同时初始化
声明时,需要与原变量保持一致,不能重新定义值
不可以用在case后面 (case expression notconstant)
eg: const int Max=100; //right
extern const int j=10; //error
2) const 在程序运行过程中只有一份备份(因为它是全局的只读变量,放在静态去)
编译时候,确定值
修饰变量有特定类型
define 宏在内存中有若干个备份
预编译阶段进行替换
没有类型
eg:
define M 3 //宏常量
const int N=5; //此时未将N放入内存中
int i=N; //为N分配内存,以后不再分配
int I=M; //预编译期间进行宏替换,分配内存
int j=N //未分配内存
int j=M; //宏替换,分配内存
3) 修饰指针(将类型名忽略)
const int *p ; //p可变,p指向的对象不可变
int const *p; //p可变,p指向的对象不可变
int * const p; //p不可变,p指向的对象可变
const int * const p; //p和p指向的对象都不可变
1.11 volatile
定义:修饰的变量可以被编译器未知的因素更改,如操作系统,硬件或者其他线程等。
编译器不进行代码优化,对特殊地址的稳定访问。
Eg: int j=10;
Inti=j; //(1)
Intk=j; //(2)
(1)语句之后i值没有丢掉,在(2)语句中继续用,而不从内存中取i的值
若改为 volatileint j=10; 每次使用时,汇编代码会从j的地址取值
1.12 struct关键字
可以将相关联的数据打包成一个整体
在C++中struct成员默认是public,class默认是private
1) 空结构体的大小
struct student
{
}stu;
Sizeof(stu)=1;
2) 柔性数组
结构体中包含一个大小可变的数组
Eg: typedefstruct st_type
{
int i;
int a[];
}type_a;
sizeof(type_a)=4; //不包括柔性数组内存
type_a *p=( type_a)malloc(sizeof(type_a)+100*sizeof(int) );
此时sizeof(type_a)=4; //柔性数组 算不得结构体的 正式成员
3) union 联合体
union所有数据成员具有相同的起始地址,同一时间只能用一个数据成员
如何用程序确认当前系统的储存模式(大小端)
Int checksystem()
{
union check
{
int I;
char ch;
}c;
c.i=1;
return (c.ch==1);
}
返回为1,小端模式。(数据低字节放在低地址,高字节放在高地址)
返回为0,大端模式。(高字节放在低地址)
注意内存的增长是从低到高,大端先放高字节,小端反之。
1.13 enum关键字
枚举常量,一般大写
如果有一个赋值,后面不赋值变量从赋值变量后 一次加1
如果都不复制,从0开始递增
Eg: enum Color
{
GREEN=1, //1
RED, //2
BLUE, //3
GREEN_RED=10,
GREEN_BLUE //11
}ColorVal;
sizeof(ColorVal)=4; //????? Enum 默认整形
1.14 typedef
1) 给一个已经存在的数据类型取一个别名,而非定义一个新的数据类型
Eg: typedef struct student
{
//
}Stu_st, *Stu_pst;
(a) struct student stu1 和 Stu_st stu1; 相同
(b) struct student * stu2和Stu_st * stu2和Stu_pst stu2 相同
(c) const Stu_pst stu3;
(d) Stu_pst const stu4;
Const修饰stu3,stu4,这两个不能变
将Stu_pst作为一种新的类型,const修饰时,将数据类型去掉
2) Typedef与define区别
(e)#define INT32 int
Unsigned INT32 i=10;
//right 预编译的时候 已经替换
(F)typedef int INT32
Unsigned int32 j=10;
//typedef定义了一种类型,不支持类型扩展。(unsigned与int结合???)
(G)#definePCHAR char*
PCHAR p3,p4; //p4不是指针,仅仅是字符变量
(H) typedef char * pchar;
pchar p1,p2; // p1, p2均是字符指针
二、符号
2.1 注释符号
(a) int/*….*/i; //编译器会将注释的内容 用空格代替,变成int i ;
(b) char * s=”abcd //hijkl\\m”;//其中//是字符串的内容,\\表示‘\’
(c) //Is it a \
Valid comment? // \ 连接符,后面不能有空格,下一行开头也不能有空格
(d) in/*…*/t i ; //error
(e) /*this is /* illegal*/ */ //注释不能嵌套
(f)y=x/*p; //error /*被认为是注释,直到出现*/,
Y=x/ *p; or y=x/(*p); //right / *有空格就不是注释
2.2 ‘’,“”
‘a’字符常量,1字节
“a”字符串常量,2个字节(\0结束)
2.3 运输符
1)&& 、|| 逻辑运算符
inti=0;
int j=0;
for( (++i>0) || (++j>0) )
{
Printf(“%d %d”,i,j); //i=1,j=0;
}
2) << 、>>
0x01<<2+3; //算术运算级的优先级 高于 逻辑优先级,结果为32
3)++、--
Eg:int x;
int i=3;
x=(++i,i++,i+10);
printf("%d %d",i,x); //x,遇到逗号,任务本计算单位已经结束。
3) for循环的顺序 1,2,程序;3,2,程序
for(1;2;3)
{
程序
}
三、预处理
3.1 文件包含
1)include<stdio.h>尖括号,预处理到系统路径中获取文件(C编译系统提供的并存放在指定子目录下的头文件)
2)include “filename” 双引号,在当前目录中查找该文件。
3.2 内存对齐(如下图)
3.3 #
# 可以将语言符号转换为字符串
3.4 ## 粘合剂
#define XNAME(n) x##n
XNAME(8);
展开为:x8
四、指针、数组
4.1 指针
1)指针的定义
正确: int *p=NULL;
//等价于int *p; p=NULL; 是把p的值设为0x00000000(NULL被宏定义为0)
错误: int *p;
*p=NULL; //p指向内存中保存的是int类型数据,p保存的可能是一个非法地址。
2)将数值存储在指定内存处
往内存地址0x12ff7c上存入一个整型数0x100:
方法一、int *p=(int *)0x12ff7c; //将0x12ff7c强制转换为int型指针,赋给指针变量p
*p=0x100;
方法二、*((int *)0x12ff7c)=0x100;
???0x12ff7c而不是其他地址,可以通过定义变量int i,查找i的地址
3)指针访问
Eg: char *p=“abcdef”;
指针形式访问:*(p+4)
数组形式访问:p[4] //取出p里存储的地址,再加上4个偏移元素个数,不是字节数
4.2 数组
4.2.1 定义
内存上连续存储
Eg:int a[5];
sizeof(a) 值为sizeof(int) *5,32位系统下为20
sizeof(a[0]) 值为sizeof(int)=5;
sizeof(a[5]) 值为32系统下为4,sizeof关键字是在编译的时候运行,不是运行,不去真正访问a[5]。
4.2.2数组访问
Eg: char a[] =”123456”;
*(a+4),a[4]均可以访问。
4.2.3 a 与&a 区别
地址的强制转换
指针变量 +- 整数:指针变量的地址加减元素个数,而不是字节数
Eg:
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
假设p值为0x100 000,求下面表达式的值
p+0x1=??? //p+0x1*(sizeof(p))=p+20=0x100014(转化为16进制数)
(unsigned long)p+0x1=??? //p+(unsigned long)0x1= 0x100 001;
(unsigned int *)p+0x1=??? //p+(unsigned int *)0x01=p+4=0x100 004;
Eg:
1、
voidmain()
{
inta[5]={1,2,3,4,5};
int*ptr1=(int *)(&a+1);
int *ptr2=(int *)(a+1); //输出5,2
//int *ptr2=(int *)((int)a+1); //????输出5,2000 000
printf("%x,%x",ptr1[-1],*ptr2);
}
a 数组首元素的首地址,a+1是数组下一元素
&a数组的首地址,&a+1是下一数组的首地址
2、
void main()
{
char a[5]={'A','B','C','D'};
char (*p3)[5]=&a;
char (*p4)[5]=a; // warning C4048: different arraysubscripts : 'char (*)[10]' and 'char (*)[5]'
char (*p3)[3]=&a;
char (*p4)[3]=a;//warning
char (*p3)[10]=&a;
char (*p4)[10]=a;//warning
printf("%x,%x",p3+1,p4+1);
}
a 的地址为0x0012ff78,p3,p4 也指向该地址。
4.3 指针数组、数组指针
定义
指针数组:首先是个数组,其次数组中的元素都是指针
Eg: int*p1[10]; // [] 优先级比* 高
数组指针:首先是个指针,它指向数组。
Eg: int (*p1)[10]; //()优先级比[] 高
4.4 多维数组和指针
二维数组的定义
1) inta[3][2]={ {0,1},{2.3},{4.5} }; //中的一维数组也是{ }
若定义为这样呢?
int a[3][2]={ (0.1),(2,3),(4,5) }; //小括号从左往右依次计算,取最右侧的值
此时 a[0]=1;
2)第一个参数可省,第二个不可省(否则不知道一行有几个,无法确定)
Eg: a[][4] ok; a[4][] error
&p[4][2]-&a[4][2]值
Eg:int a[5][5];
Int (*p)[4]
P=a;
问&p[4][2]-&a[4][2]值为多少?
P是指向一个包含4个元素的数组的指针,即p+1表示指针向后移动了一个“包含4个int类型元素的数组”。
4.5 无法向函数传递数组
Eg:
void fun1(char a[]) //ok
{
char c=a[3];
printf("%c",c);
}
void fun2(char a[10]) //实际传递数组的大小 与函数形参数组大小 没有关系
{
char c=a[3];
printf("%c",c);
}
void fun3(char *p) //ok
{
char c=p[3];
printf("%c",c);
}
int main()
{
char b[100]="abcdef";
fun1(b);
fun2(b);
fun3(b);
return 0;
}
当一维数组作为函数的参数,编译器总是把它解析成 指向该数组首元素首地址 的指针。
所有非数组形式的数据实参 均以传值形式(对实参做备份,传递给被调用函数,不能修改实参的值)。
main 函数内的变量 不是全局变量,而是局部变量,只不过它生命周期和全局变量(定义在函数外面)一样。
4.6 函数指针
Char * (* fun1)(char *p1,char *p2); //fun1不是函数名,而是指向函数 的指针
五、内存管理
5.1 如何避免野指针
指针
定义时,最好初始化为NULL。
eg:char * p=NULL;(NULL是地址为0x0处,核心系统位置,此时指针内容不允许修改,
报错)
5.2 栈、堆和静态区
堆 定义:malloc系列函数 或new操作符分配内存,由free或delete结束,释放内存。
在没有释放前一直存在,直到程序结束。
特点:使用灵活,空间较大,但是容易出错(野指针)。
Eg:
free(p); //指针p保存的地址没有改变,被释放的内存中值也没变,但是p无法使用该内存了
p=NULL; //p 指向地址为0
栈 定义:保存局部变量。栈上内容只在函数范围内存在,函数运行结束,自动销毁。
特点:效率高,但空间大小有限。
静态区保存全局变量和static变量(包括static全局和局部变量)
由编译器在编译的时候分配空间,在整个程序的都存在。
5.3 常见的内存错误及策略
5.3.1 指针指向合法内存
struct student
{
char *name;
int score;
}stu,*pstu;
void main()
{
//指针赋值,大小要合适
char *p1="abcdefg";
char *p2=(char *)malloc(sizeof(char)*(strlen(p1)+1));
strcpy(p2,p1);
printf("%s ",p2);*/
pstu=(struct student *)malloc(sizeof(struct student));//结构体指针分配内存空间
//strcpy(pstu->name,"Jimy");//error 结构体变量 没有分配空间。编译无误,运行错误
pstu->name=(char *)malloc(10*sizeof(char));
strcpy(pstu->name,"Jimy");//right
pstu->score=99;
printf("%s %d",pstu->name,pstu->score);
free(pstu);
return 0;
}
六、函数
1)fun() 不要省略
若函数没有返回值,应声明为void类型
若无返回值,函数默认返回为int 类型
2) return 不可返回函数内部变量
char *Func(void)
{
char str[30];
...
return str;
}