# 转义字符的个数陷阱

#include<stdio.h>
#include<string.h>
int main()
{
    printf("%d", strlen("c:\test\121"));//7
    return 0;
}
// 剖析
/*
计算字符串长度时转义字符只算一个字符
另外需特别注意的转义字符
\ddd ddd 表示 1~3 个八进制数字 如:\33  \121
\xdd dd 表示 2 个十六进制数字 如: \x30

# 关键字的定义陷阱

switch 语句中包含的关键字有 break,default,case,if 等关键字,唯独不包含 continue。

define 不是关键字,而是一种指令。

# 逻辑操作符的定义陷阱

/*
真或假的定义
  0  假
非0	真
逻辑操作符的定义(真返回1,假返回0)
&& - 逻辑与
&& 真 -> 真
&& 不执行也不判断 -> 假
&& 假 -> 假
|| - 逻辑或
真 || 不执行也不判断 -> 真
假 || 真 -> 真
假 || 假 -> 假
*/
#include<stdio.h>
int main()
{
    int i= 0, a = 0, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++;
    printf("a = %d, b = %d, c = %d, d = %d", a, b, c, d);
    return 0;
}
//过程剖析
/*
按照逻辑与的定义,
此时a先调用自身,值为0,
判断为假,a++ && 不执行也不判断,
因此`a++ && ++b`为0,
0为假,0 && 不执行也不判断,
所以,a=1,b=2,c=3,d=4
*/

# 赋值操作符的真假陷阱

#include<stdio.h>
int main()
{
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        if (i = 5)
            printf("%d\n",d);
    }
    return 0;
}
// 剖析
/*
上述代码 if 语句中是赋值语句,i 一直会被赋予值 5,
而非 0 即表示真,所以此循环为死循环。
*/

# sizeof 关键字的大小比较陷阱 (隐式提升,整形提升)

#include<stdio.h>
int i;
int main()
{
    --i;
    if(i > sizeof(i))
        printf(">\n");
    else
        printf("<\n");
   
    return 0;
}
// 未初始化的全局变量默认为 0
//sizeof () 默认大小为无符号数
// 此处 i 被提升为无符号类型来与 sizeof (i) 进行比较

# sizeof 关键字的地址陷阱 (一维数组)

/*
先介绍下`sizeof`关键字的常规操作
整型变量a
sizeof(a)
sizeof a
int 关键字
只有sizeof(int)
此外,sizeof只会判断传入数据的类型并返回相应字节,
当sizeof计算数组时,假如数组名为arr,
只有`sizeof(arr)`中的arr代表整个数组,即相当于计算整个数组的`和`的字节,
另外,`sizeof(&arr)`中&的arr代表的也是整个数组,由于&是取址符,故&arr为地址。
*/
#include<stdio.h>
int main()
{
    int a[] = {1, 2, 3, 4};
    printf("% d\n", sizeof (a [0]));//4 整型,[] 操作符,此处表示为地址取值操作 - 是数值
    
    //(4/8) 指针存储地址,地址的类型判定为指针,32位与64位各不同-是地址
    printf("%d\n", sizeof(a + 0));
    //(4/8) 指针类型,此处a表示数组首地址,+1时地址跳跃一个int字节-是地址
    printf("%d\n", sizeof(a + 1));
    
    //(4/8) 取址符,是指针类型-是地址
    printf("%d\n", sizeof(&a + 0));
    //(4/8) 指针类型,sizeof只判定传入类型并不理会传入的是什么-是地址
    printf("%d\n", sizeof(&a + 1));
    //(4/8) 指针类型,数组第一个元素地址跳跃一个int字节-是地址
    printf("%d\n", sizeof(&a[0] + 1));
    
    printf("% d\n", sizeof (*a));//4 整型,表示为地址首元素
    printf("% d", sizeof (*&a));//16 整型数组,表示为整个数组的值
    return 0;
}
/*
除了sizeof(a)这种写法,其他不带&的写法中的数组变量名都为数组首元素。
##注意点
数组名[] 取值
*       这种写法也可表示为取值
&数组名  整个数组的地址
*/

# sizeof 关键字的地址陷阱 (二维数组)

#include<stdio.h>
int main()
{
    int a[3][4] = {0};
    printf("%d\n", sizeof(*a));//16 a 相当于该数组首元素地址
    printf("%d\n", sizeof(a[0]));//16 相当于 sizeof (数组名)
    printf("%d\n", sizeof(a[3]));//16 sizeof 只判断类型并不会访问,等价于 a [0]
    printf("%d\n", sizeof(a[0][0]));//4 值
    
    printf("%d\n", sizeof(a[0] + 1));//(4/8) 地址,a [0] 表示第一行数组的首地址
    printf("%d\n", sizeof(*(a[0] + 1)));//4  值
    
    printf("%d\n", sizeof(a + 1));//(4/8) 地址,第二行的地址
    printf("%d\n", sizeof(&a + 1));//(4/8) 地址
    
    printf("%d\n", sizeof(&a[0] + 1));//(4/8) 地址,第二行的地址
    printf("%d", sizeof(*(&a[0] + 1)));//16 值
    
    return 0;
}
/*
二维数组中,
a [0] 的表示等同于 sizeof (数组名) 即一维数组名
a [0] + 1 中 a [0] 表示为数组第一个二维数组的首地址
a + 1 中 a 表示二维数组首元素地址
&a [0] 中 a [0] 表示二维数组第一个数组的数组地址
*/

# strlen 库函数的地址陷阱

#include<stdio.h>
#include<string.h>
int main()
{
    char a[] = {'a', 'b', 'c', 'd'};
    
    printf("%d\n", strlen(a));// 随机值 '\0' 位置不明,a 为数组首元素地址
    
    //err  strlen 只接收地址,此处传值会间接为传入数据选取空间并产生非法访问
    printf("%d\n", strlen(*a));
    //err  同上
    printf("%d\n", strlen(a[1]));
    
    printf("%d\n", strlen(&a));// 随机值 取址符取整个数组地址
    printf("%d\n", strlen(&a + 1));// 随机值 - 4 整个数组地址跳跃一个数组字节
    printf("%d\n", strlen(&a[0] + 1));// 随机值 - 1 数组第一个元素地址跳跃一个数组元素字节
    return 0;
}

# 常量字符串的地址陷阱

#include<stdio.h>
#include<string.h>
int main()
{
    char* p = "abcdef";// 传入是字符串首地址
    
    //sizeof 关键字
    printf("%d\n", sizeof(p));//(4/8) 地址
    printf("%d\n", sizeof(p[1]));//1  值,等价写法 *(p + 1) == p [1]
    printf("%d\n", sizeof(&p));//(4/8) 指针地址
    printf("%d\n", sizeof(&p + 1));//(4/8) 指针地址跳跃一个指针字节
    printf("%d\n", sizeof(&p[0] + 1));//(4/8) 地址,字符 b 跳跃一个字符字节
    
    //strlen 库函数
    printf("%d\n", strlen(p));//6 
    
    printf("%d\n", strlen(*p));//err 值,非法访问
    printf("%d\n", strlen(p[1]));//err 值,非法访问
    
    printf("%d\n", strlen(&p));// 随机值 指针的地址
    printf("%d\n", strlen(&p + 1));// 随机值 - 4 指针的地址跳跃一个指针字节
    printf("%d", strlen(&p[0] + 1));//5 地址,字符 a 的地址跳跃一个字符字节
    
    return 0;
}