c复习随笔

25/2/18

运算符优先级

一共有十五个优先级:

1. ()  []  .  ->
2. !  ~   -(负号) ++  --   &(取变量地址)*   (type)(强制类型)    sizeof 
3. * / %
4. + - 
5. << 
6. = < <= 
7. == != 
8. & 
9. ^ 
10. | 
11. &&
12. ||
13. ?:
14. =  +=  -=  *=  /=   %=  |=   ^=   &=   >>=   <<=
15. ,

结合性:2、13、14 是从右至左,其他都是从左至右。

口诀:

- 括号成员第一        //括号运算符[]  () 成员运算符.  ->
- 全体单目第二        //所有的单目运算符比如++、 --、 +(正)、 -(负) 、指针运算*、& 
- 乘除余三,加减四     //这个"余"是指取余运算即%
- 移位五,关系六      //移位运算符:<< >> ,关系:> < >= <= 等
- 等于(与)不等排第七  //即== 和!=
- 位与异或和位或      //这几个都是位运算: 位与(&)异或(^)位或(|) 
- 逻辑或跟与          //逻辑运算符:|| 和 &&,注意顺序:优先级(||)低于优先级(&&) 
- 条件高于赋值        //三目运算符优先级排到13,只比赋值运算符和","高
- 逗号运算级最低      //逗号运算符优先级最低

const 修饰符

const默认修饰左边的内容,如果左边没有东西则修饰其右边的内容

const int *a const左边没有东西,因此const修饰int,则指针指向的内容不可通过指针修改

int const *a const左边有东西,因此const修饰int

int* const a const修饰*,即指针不能改变指向

const int * const a 第一个const修饰int,第二个const修饰*,即指针指向内容不可修改,也不能改变指针指向

转义字符

转义字符用于表示一些特殊的字符,以反斜杠 \ 开始,常见的转义字符包括:

  • \n - 换行符
  • \t - 制表符
  • \r - 回车符
  • \\ - 反斜杠
  • \' - 单引号
  • \" - 双引号
  • \0 - 空字符

引用&

引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。

引用的声明方法:类型标识符 &引用名=目标变量名;

举个例子:

int a;

int &ra=a; //定义引用ra,它是变量a的引用,即别名

说明:

(1)&在此不是求地址运算,而是起标识作用。

(2)类型标识符是指目标变量的类型。

(3)声明引用时,必须同时对其进行初始化。

(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。

ra=1; 等价于 a=1;

(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。

(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。

引用的主要作用:

(1)在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。

(2)用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。

(3)引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。

(4)使用引用的时机。流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。

25/2/20

指针与数组关系

arr[i] 等价于 *(arr+i);

int *p=new int[40] ( );  在分配空间后,对每个int进行了默认的初始化操作。即p[n]的值(n的取值范围是0-39)都是为0的。 

int *p=new int[40] (0);  这样是起不到对数组初始化为0的效果的,而且语法是错误的。

正常的应该是用大括号,int *p=new int[40]{0,1,2,3,4};

delete

首先new和delete是匹配使用的,因此对于一个new运算符声明的指针,只能匹配一个delete使用,且要用delete运算符来释放内存,

其次,在调用delete释放内存时,需要调用析构函数,原则是:析构函数看指针是什么类型的,就调用那个类的析构函数,然后按继承的顺序,依次来调用父类的析构

sizeof

sizeof 是可用来计算字符串的总长度,包括\0,而strlen计算不包含\0的字符串的长度。

当数组作为函数的参数时,其自动转换为指向函数的指针,sizeof(str_arg)表示的是指针的长度,在32为计算机上为4位

strcat

strcat 是 C 标准库中的一个函数,用于将两个字符串连接起来。具体来说,它将第二个字符串的副本追加到第一个字符串的末尾,并返回第一个字符串的起始地址。这里有几个关键点需要注意:

函数原型:char *strcat(char *dest, const char *src);

dest 是目标字符串,它必须有足够的空间来容纳追加的内容以及一个额外的空字符(\0),这个空字符用于标记字符串的结束。 src 是源字符串,其内容将被追加到 dest 的末尾。

缓冲区大小: 调用 strcat 之前,必须确保 dest 数组有足够的空间来容纳 src 中的所有字符以及 dest 中已有的字符和结尾的空字符。如果空间不足,将发生缓冲区溢出,这可能导致未定义行为,包括程序崩溃或安全漏洞。

空字符的处理: strcat 会查找 dest 中的空字符来确定在哪里开始追加 src 的内容。然后,它会复制 src 中的字符到 dest 中,直到遇到 src 中的空字符为止,但不会在 dest 的新内容末尾添加另一个空字符(因为 src 的内容已经以空字符结尾,而 strcat 只是复制内容)。然而,这里有个重要的澄清:我之前的描述有误,strcat 实际上会在整个操作结束后确保 dest 以一个空字符结尾(如果操作没有导致缓冲区溢出的话)。换句话说,strcat 会正确处理空字符,使得 dest 成为一个有效的、以空字符结尾的字符串。

返回值: strcat 返回 dest 的起始地址,这使得函数调用可以很容易地被嵌套或用在需要字符串地址的表达式中。

安全性: 由于 strcat 不检查 dest 的大小,因此在使用时很容易出错。为了避免缓冲区溢出,可以使用 strncat 函数,它允许你指定最多可以追加多少个字符。

#include <stdio.h>
#include <string.h>

int main() {
    char dest[50] = "Hello, "; // 确保有足够的空间
    const char *src = "world!";
    strcat(dest, src);
    printf("%s\n", dest); // 输出: Hello, world!
    return 0;
}

在这个例子中,dest 数组有足够的空间来容纳 "Hello, " 和 "world!" 以及它们之间的空字符。因此,调用 strcat 是安全的,并且会按预期工作。