关键字
- 32个,值得一提的是sizeof也是关键字,还有没见过的比如volatile,见得比较少的auto,goto
- auto 缺省都是auto
- register 不能用&取其变量地址,没在内存,变量不能长于整型长度,必须是一个单个的值
- static 修饰函数和变量,修饰函数将函数定义为内部函数,只能在函数所在文件使用,不用担心重名了。修饰变量分全局和局部,全局变量被修饰后将无法被其他文件通过extern使用,只能在本文件内部使用,同样不怕重名了,局部变量修饰后只在函数内部有用了,并且被保存到静态内存区,函数结束后不会销毁,仍然可以使用。
- sizeof 没想到吧这是关键字!!计算变量所占空间是可以不加括号,但是计算类型时为了避免歧义要加括号。还是加括号,最好不要在计算是进行别的操作 。
- if 关键字
//bool与零值的比较好的写法
bool bTestFlag = True;
if(bTestFlag);
if(!bTestFlag);
//float与零值的比较
#define EPSINON 1e-6
float fTestVal = 0.0;
if((fTestVal > -EPSINON)&&(fTestVal <= EPSINON));//EPSINON为定义的精度
//指针与零值进行比较
int * p =NULL;
if(NULL == p);
if(NULL != P);
- 循环 while for 等,注意多层循环时,长循环放在内部,短一点的放在外部,这样减少CPU跨切循环层的次数。
- void 类似抽象基类,不会有实体,void *可以接受任何类型的指针的赋值
- volatile 编译器提供特殊地址的稳定访问
//i只会从内存中取一次,然后第二次给k赋值仍然用上一次的
int i = 10;
int j = i;
int k = i;
//每次都取,保证稳定访问,避免i被修改
volatile int i = 10;
int j = i;
int k = i;
- struct 空结构体大小为1字节,柔性数组
typedef struct st_type{
int i;
int a[];
}type_a;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
符号
- 注释:编译器会把注释换掉改成空格。
- \ 接续符,编译器会把下一行内容放到这一行后面
- \ 转义字符
- 符号合法的长度遵从贪心法,一个合法符号长度越长越好
预处理
- #error error-message 编译程序时,只要遇到#error就会生成一个编译错误提示消息,并停止编译
- #pragma 设定编译器的状态或者只是编译器完成一些特定的动作 #pragma para
- #pragma message("消息文本") 编译器遇到这个文本,就在编译输出窗口把消息文本打印出来
- #pargma code_seg(["section - name"[,"section-class"]]) 设置程序中函数代码存放的代码段
- #pragma once 只要在头文件中的最开始加入这条指令就能够保证头文件被编译一次
- #pragma hdrstop 表示编译头文件到此为止,后面的头文件不再编译了
- #pragma resource “*.dfm”表示把*.dfm文件中的资源加入工程
- #pragma warning 弹出警告
- #pragma comment将一个注释记录放入对象文件或可执行文件中
- #pragma pack(n) //编译器将按照n字节对齐
- #pragma pack() //编译器将取消自定义字节对齐
- #pragma pack() //编译器将取消自定义字节对齐
- #运算符
#define SQR(x) printf("The square of x is %d.\n",((x)*(x))); SQR(8); //输出为 The square of x is 64 #define SQR(x) printf("The square of #x is %d.\n",((x)*(x))); SQR(8); //输出为 The square of 8 is 64
- ##运算符
#define XNAME(n) x ## n //如果这样使用宏 XNAME(8) //会被展开为 x8 //##就是黏合剂
指针和数组
- 32位系统,指针类型取煤器咔出来的内存是4字节,不管什么类型的指针都是4字节,因为4字节够寻址了。可以测试sizeof(void* )来验证。
- 指针初始化,用NULL,被宏定义为 #define NULL 0 C语言对大小写敏感,
- 关键字sizeof是在编译时求值的,看下面的例子
int a[5];
sizeof(a[5]);
//sizeof(a[5])的值也是4,sizeof是关键字不是函数 \
函数求值是在运行的时候,关键字求值是在编译的 \
时候,仅仅根据数组元素的类型来确定其值,并没 \
有真正去访问哪个元素,所以并不会出错。
- &a[0] 和 &a : a[0]是一个元素,a是整个数组,&a和&a[0]值是一样的,但是意义不一样,&a是陕西省政府,&a[0]是西安市政府,都在西安,但是意义不同。
- 数组名在等号左边(左值:编译器认为代表的是地址)和右边(右值:编译器认为代表的是值)的区别,数组名不能当左值,但是数组名可以当右值,代表数组首元素的首地址,int a[10]; a做右值和&a[0]是一个意思。
- 数组和指针的区别和特性
//举个例子
//file1.h
int a[10];
//file2.h
extern int * a;
//在file2中并不能通过指针a来访问数组a,因为指针的访问方式是a的值是指向真正的值的地址,而 \
数组名是数组的首元素的首地址,在file1中a[1]是数组名所代表的数组的首元素的首地址加上一个\
类型需要字节作为数据的真正地址,而file2中a[1]是先取指针a的值,然后找到它指向的值,再加 \
偏移量,错误之处在于把指向的数组的首元素当成了指向数组首元素的地址
extern int a[];
//这样就好了,编译器把a当数组,会把a的值直接看做数组的首元素的首地址
内存管理
- NULL 拴住野指针的链子,定义指针变量的同时最好初始化为NULL,用完指针之后也将变量的值设置为NULL
- 常说的堆栈其实指的是栈,堆是堆,是malloc和new分配的那部分内存
- 常见的内存错误以及对策
1、指针未指向一块合法的内存
struct student
{
char * name;
int score;
}stu,*pstu;
int main()
{
// 1、结构体指针成员未初始化
strcpy(stu.name,"wangchengmeng");//stu.name没有分配内存,定义stu时只是分配了name指针的内存
stu.score = 99; //现在name里面存储着一些乱码,把wangchengmeng给他就是让它
return 0; //向乱码指向的地址存储wangchengmeng,会因为无权限出错
//解决方法是为name指针malloc一块空间
// 2 、没有结构体指针分配足够的内存
pstu = (struct student *)malloc(sizeof(struct student*));
strcpy(pstu->name,"wangchengmeng");// 要分配struct student的内存,却分配了struct student*
pstu->score=99; // 的内存大小
free(pstu);
return 0;
// 3、入口校验
assert(NULL!=p); //p初始化时要用NULL来初始化,然后可以在用p前先判断
}
2、为指针分配的内存太小
char *p1 = "abcdefg";
char *p2 = (char *)malloc(sizeof(char)*strlen(p1));//p1长度为7,但其所占内存大小为8字节。
strcpy(p1,p2); //因为还有结束标志'\0'
//解决方法如下
char *p2 = (char *)malloc(sizeof(char)*(strlen(p1)+1));
//但只有字符串后面要加一,下面就不用
char p2[7] = {'a','b','c','d','e','f'};
3、内存分配后要初始化
int i = 10;
//初始化为有效的值
char * p = (char*)malloc(sizeof(char));
//初始化为NULL
char * p = NULL;
//初始化数组
int a[10] = {0};
int a[10];
memset(a,0,sizeof(a));
4、内存越界:比如数组下标越界
5、内存泄漏:有malloc就要有free,malloc分配连续的内存,free释放它,没有足够的内存就返回NULL,所以要用assert(NULL!=p)来确定分配是否成功。malloc申请0字节的内存也会成功但是只有地址没有容量;free一个指针后要用NULL把这个指针栓起来,避免成为野指针,也避免内存已经释放,但是指针还在使用。