C 与 C++ 基础易错点总结
kecho

本文参考《算法笔记》记录一些个人认为容易出错和容易忘记的知识点。

一、输入与输出

  • C 中的 scanfprintf 比 C++ 中的 cincout 效率高,如果发现超时可改用 scanfprintf
  • scanf 输入字符串(char 数组)不用加 &
  • scanf 中对于 double 型变量的格式符为 %lf,而 printf%f 。常见格式控制符如下:
数据类型 格式符 举例
int %d scanf("%d", &n);printf("%d", n);
long long %lld scanf("%lld", &n);printf("%lld", n);
float %f scanf("%f", &f);printf("%f", f);
double %lfprintf中为 %f scanf("%lf", &d);printf("%f", d);
char %c scanf("%c", &c);printf("%c", c);
字符串(char 数组 %s scanf("%s", str);printf("%s", str);
int %md 不满 m 位右对齐输出,高位补空格,满则原样输出
int %0md 不满 m 位右对齐输出,高位补 0 ,满则原样输出
float / double %.mf 保留 m 位小数输出(采用“四舍六入五成双”规则)
  • %cscanf 对其它格式符(包含 %s)以空白符(空格、换行、Tab等)为结束标志;
  • getchar() 能读入一个字符,能识别换行符,用法:char c = getchar(); putchar( c );
  • gets(str) 能读入一行,以换行符为结束标志,用法:char str[10]; gets(str); puts(str);
  • 要读入带有空格的字符串可使用 scanfgetchar() 循环读入字符(要注意排除换行符),或者直接用 gets(str)
  • sscanf(str,"%d",n) 表示把字符数组 str 中的内容以 “%d” 的格式写入到 n 中(从左至右),sprintf(str,"%d",n) 表示把 n 以 “%d” 的格式写入到 str 字符数组中(从右至左)。可用来进行类型转换。
  • C++ 中读入一整行:
    • 字符数组:char str[100]; cin.getLine(str,101);
    • 字符串:string str; getLine(cin,str);
  • cin 或 cout 耗时长,可在之前加上 std::ios::sync_with_stdio(false);
  • cout 可通过 #include <iomanip> 控制输出格式,如函数 setiosflags()setprecision()等。

二、变量与常量

  • 对于整数型,题目要求 10^9 以内(或占32 位)用 int 类型;10^18 以内(或占64 位)使用 long long 型;
  • 对于浮点型,尽量只使用 double ( 注:float 占32 位,精度 6-7 位;double 占 64 位,精度 15-16 位);
  • 小写字母的 ASCII 码比大写字母的大 32(不用记住对应的 ASCII 码,只要记住 小 > 大 = 32);
  • 零 –> false,非零整数(包括负整数) –> true,false –> 0,true –> 1 ;
  • const 用来定义常量,宏定义除了定义常量,也可以定义任何语句或片段(注意宏定义的原封不动替换陷阱),如 #define ADD(a,b) ((a)+(b))

三、运算符与循环

  • 3 / 2 == 1 对比 3 / 2.0 == 1.5
  • i++++i 的差别;
  • 条件A || 条件B 按顺序判断,有一个为 true 就直接返回 true,后面的就不判断;&& 同理,有一个为 false 就直接返回 false
  • 常用 (1 << 30) - 1 表示常量无穷大(const int INF = (1 << 30) - 1),或者用十六进制 0x3fffffff 表示;
  • C 语言中不允许在 for 循环结构的表达式 1 中定义变量,而 C++ 可以(如 for(int i = 0; i < 10 ; i++))。

四、数组

  • 数组大小只能是常量,不能是变量;
  • 数组的初值(不同编译器不同):
    • 1)int a[10]; 所有值都为随机值(大部分)或 0,而 int a; 单个变量未初始化一般为 0
    • 2)int a[10] = {0}; //或{} 所有值都为 0
    • 3)int a[10] = {1,2,3}; 未被赋初值的为随机值或 0(大部分)
  • 如果数组大小较大(大概 10^6 级别),则需要将其定义在主函数外面,否则程序会异常退出。原因是函数内部申请的局部变量来自系统栈,允许申请的空间较小;而函数外申请的全局变量来自静态存储区,允许申请的空间较大;
  • 字符数组末尾都会有一个空字符 \0 ,这是 puts()printf() 输出字符串时用来识别结束标志的,如果没有它则会输出一堆乱码。scanf%s 格式和 gets() 函数会自动在字符数组末尾加上 \0,而 getchar() 则需要手动添加。字符数组的长度要比实际存储字符串长度多 1 位。

五、指针、引用、函数

  • 指针是一个 unsigned 的函数,存放的是数据地址,记 p = &a;,指针未初始化则指向随机地址;
  • int* p1, p2;p1int* 型,而 p2int 型,要想定义两个指针应该用 int *p1, *p2
  • 两个 int 型指针相减,得到的是两个指针之间差了几个 int,即以 4B 为单位。int 型指针加 1 同理,也是以 4B 为单位。
  • 数组可作为形式参数,但不可作为返回值。作为形式参数时数组的第一维不用写长度(二维数组的第二维要写长度),实参无论一维还是二维都只要填数组名,在函数中对数组元素的修改等同于对原数组元素的修改;
  • 指针作为参数调用:swap(&a,&b);
  • 引用作为参数调用(常量不可使用引用,C++ 才支持引用):swap(a,b);

六、结构体

  • 结构体中成员变量不能有自己本身类型的变量,但可以有指向自己本身类型的指针变量;
  • C++ 中结构体的定义( C 中 struct person 是一个整体,而 C++ 中可省略 struct
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //第一种
    struct person{
    int id;
    person* p;
    }; //结构体类型
    person a,*q;

    //第二种
    typedef struct personA{
    int id;
    personA* p; //此时还不能用别名 person* p;
    }person; //结构体类型别名
    person a,*q;
    //personA a,*q;

    //第三种
    struct person{
    int id;
    person* p;
    }a,*q; //结构体变量

    //访问变量
    q -> id = 1;
    (*q).id = 2;
  • C 中结构体的定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //在C中会报错
    /*struct person{
    int id;
    };
    struct person a;
    */

    //第一种
    typedef struct personA{
    int id;
    struct personA* p; //此时还不能用别名 person* p;
    }person; //结构体类型别名
    person a,*q;
    //struct personA a,b,*q;

    //第二种
    struct person{
    int id;
    struct person* p; //对比 C++ 要多个 struct 关键字
    }a,*q; //结构体变量

    七、常见函数

  • 字符数组相关函数
    要包含头文件 #include "string.h" ( C++ 为 #inlude <cstring>
函数 含义
strlen(字符数组) 返回第一个 \0 之前的字符个数
strcmp(字符数组1, 字符数组2) 比较两个字符串,小于返回负整数,等于返回 0,大于返回正整数
strcpy(字符数组1, 字符数组2) 把字符数组 2 复制给字符数组 1
strcat(字符数组1, 字符数组2) 把字符数组 2 接到字符数组 1 后面
  • Math 函数
    要包含头文件 #include "math.h" ( C++ 为 #inlude <cmath>
函数 含义
fabs(double x) 求绝对值
floor(double x) 向下取整
ceil(double x) 向上取整
pow(double x, double y) 求 x^y
sqrt(double x) 求算术平方根
log(double x) 以 e 为底,要指定底数可用换底公式(loga(b)=loge(b)/loge(a)
round(double x) 四舍五入取整
sin、cos、tan(double x) 三角函数
asin、acos、atan(double x) 反三角函数
  • memset 函数

作用:给数组中每个元素赋相同的值
格式:memset( 数组名, 值, sizeof( 数组名 )) (多维数组也只要数组名)

要包含头文件 #include "string.h" ( C++ 为 #inlude <cstring>
只建议初学者使用 memset 赋 0-1,因为 memset 使用按字节赋值,即对每个字节赋同样的值,而 0 的二进制补码全为 0,-1 的二进制补码全为 1,不容易弄错。如果要对数组赋其它数字(如 1),请使用 fill 函数(但 memset 的执行速度快)

八、浮点数的比较

浮点数在计算机中存储不总是精确的,有时不好使用 == 来判断是否相等,在一定误差范围内可视为相等。此处误差取 10^-8

1
2
3
4
5
6
7
const double eps = 1e-8;

#define Equ(a,b) ((fabs((a)-(b)))<(eps))
#define More(a,b) (((a)-(b))>(eps))
#define Less(a,b) (((a)-(b))<(-eps))
#define MoreEqu(a,b) (((a)-(b))>(-eps))
#define LessEqu(a,b) (((a)-(b))<(eps))
  • 本文标题:C 与 C++ 基础易错点总结
  • 本文作者:kecho
  • 创建时间:2022-04-30 17:05:48
  • 本文链接:https://blog.kecho.top/2022/C 与 C++ 基础易错点总结.html
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论