《C陷阱与缺陷》笔记
2019-01-09 本文已影响0人
DayDayUpppppp
第一章 词法陷阱
- 整数常量
int a = 010; //如果整数常量的第一个字符是0开头的,那么该常量表示的是八进制 int a = 10; //上面两个定义是完全不一样的,第一个是八进制表示的。
第二章 语法陷阱
-
理解函数声明
float f; float f(); float *f; float *f(); float (*f)();
float (*f)(); //函数声明,函数的参数是void,返回值是float (*f)(); //执行对应的地址的函数
(float (*)()) //"指向返回值是float参数是void的函数指针的"的类型转换符
// typedef 简化函数指针类型 float add(float a,float b) { cout<<a<<" "<<b<<endl; return a+b; } typedef float(*pfunType)(float, float); int main() { pfunType p = add; p(3.33, 2.22); return 0; }
-
注意作为结束语句的分号
if之后不小心多写了一个分号if(a>b); //一不小心多写了一个分号 a = 100;
那么,代码变成
if(a>b) { } a = 100;
第三章 语义陷阱
- 指针与数组
-
声明
int * p; int arr[];
-
定义
int * p = &val; int arr[]=[1,2,3,4,5];
-
sizeof
char * p = "1234567"; sizeof(p); // 4 ,指针的大小 strlen(p); // char arr[]="1234567"; sizeof(arr); //8,字符串的真实的大小,包含最后的\n
- 非数组的指针
char * p = NULL; //试图打印内存地址是0的内容 printf("printf null addr : %s \n",p); //这个行为是未定义的,取决于不同的编译器的实现方式
第四章 连接
-
检查外部类型
-
分析下面的代码:
file_a.cint val = 100;
file_b.c
extern char val; cout<<val<<endl;
文件的定义的类型和外部声明的类型,不一致,会出现什么情况?
大多数编译器不能检出这样的错误,所以,这是一种很危险的行为。尤其是下面的这个情况:
char p[] = "1234567"; //定义是数组,却声明为指针
char * p; //定义是数组,却声明为指针 cout<<p[1]<<endl; //妥妥的core掉
原因:
-
-
头文件
- 头文件的“比较科学”写法:
头文件只写对应.c文件里面的变量声明
- 头文件的“比较科学”写法:
//head.h
extern int config_ip;
extern int config_pwd;
int config_ip;
int config_pwd;
static int val; //不对外部可见
第六章 预处理
- 宏
基本用法:
#宏常量
#define num 100
#宏函数
#define max(a,b) return a>b?a:b
#define swap(a,b) \
int tmp = a;\
a = b;\
b = tmp;
但是,宏的本质是文本替换,但是在很多场景下面会出现问题。用inline和const更加安全一点。
#define max(a,b) a>b?a:b
max(a,b)+1;
//替换之后,a>b?a:b+1 //和原来的有一些歧义,在某些case上面是错误的。
-
typedef (类型定义)与宏的区别
- 区别1: 比宏更加安全的定义
typedef char* PCHAR; PCHAR pa,pb; // = char * pa;char * pb; 如果写成宏的话,可能是这样 #define PCHAR char PCHAR pa,pb; // = char * pa,pb; //= char * pa; char pb;
- 区别2:用在旧的C代码中,帮助struct。
// 用在旧的C代码中,帮助struct。以前的代码中,声明 // struct新对象时,必须要带上struct, // 即形式为: struct 结构名对象名,如: struct tagPOINT1 { int x; int y; } ; struct tagPOINT1 p1; //而在C++中,则可以直接写:结构名对象名,即: tagPOINT1 p1; typedef struct tagPOINT { int x; int y; }POINT; POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,
- 区别3:
在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。
原声明:int *(*a[5])(int, char*); 变量名为a,直接用一个新别名pFun替换a就可以了: typedef int *(*pFun)(int, char*); 原声明的最简化版: pFun a[5];
-
关于typedef
image.png