优先级最高的并不是真正意思上的运算符
| 优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
| 1 | [ ] | 数字下标 | 数组名[常量表达式] | 左到右 | |
| 2 | ( ) | 圆括号 | (表达式)/函数名(形参表) | 左到右 | |
| 3 | . | 成员选择(对象) | 对象.成员名 | 左到右 | |
| 4 | -> | 成员选择(指针) | 对象指针->成员名 |
单目运算符
| 优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 |
|---|---|---|---|---|
| 1 | - | 负号运算符 | -表达式 | 右到左 |
| 2 | (类型) | 强制类型转换 | (数据类型)表达式 | 右到左 |
| 3 | ++ | 自增运算符 | ++变量名/变量名++ | 右到左 |
| 4 | - - | 自减运算符 | –变量名/变量名– | 右到左 |
| 5 | * | 取值运算符 | *指针变量 | 右到左 |
| 6 | & | 取地址运算符 | &变量名 | 右到左 |
| 7 | ! | 逻辑非运算符 | !表达式 | 右到左 |
| 8 | ~ | 按位取反运算符 | ~表达式 | 右到左 |
| 9 | sizeof | 长度运算符 | sizeof(表达式) | 右到左 |
双目运算符
| 优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 |
|---|---|---|---|---|
| 1 | / | |||
| 2 | * | |||
| 3 | % |
| 优先级问题 | 表达式 | 经常误认为的结果 | 实际结果 |
|---|---|---|---|
| .的优先级高于* | *p.f | p所指对象的字段f | 对p取f偏移,作为指针,然后进行解除引用操作,*(p.f) |
| ()高于[ ] | int (*ap)[n] | xxxx | ap是指向一个具有 n个int数组的指针 |
| [ ]高于 * | int *ap[ ] | ap是个指向int数组的指针,int(*ap)[ ] | ap是个元素为int指针的数组 int *(ap[ ]) |
| 函数( )高于* | int *fp( ) | fp是个函数指针,所指函数返回int。int(*fp)( ) | fp是个函数,返回int *, int * (fp()) |
| ==和!=高于位操作 | (val & mask !=0) | (val & mask) != 0 | val & (mask!=0) |
| ==和!=高于赋值符 | c = getchar( ) != EOF | (c = getchar())!=EOF | c = (getchar != EOF) |
| 算术运算符高于移位运算符 | msb<< 4 + lsb | (mab<<4) +lsb | msb<<(4+lsb) |
排序
快速排序
1 | void Quick_Sort(int *arr, int begin, int end)//快速排序,升序 |
1 | int cmp(const void*a,const void*b) |
二维数组的快速排序
原题:输入:score = [[10,6,9,1],[7,5,11,2],[4,8,3,15]], k = 2
输出:[[7,5,11,2],[10,6,9,1],[4,8,3,15]]
解释:在上图中,S 表示学生,E 表示考试。
- 下标为 1 的学生在第 2 场考试取得的分数为 11 ,这是考试的最高分,所以 TA 需要排在第一。
- 下标为 0 的学生在第 2 场考试取得的分数为 9 ,这是考试的第二高分,所以 TA 需要排在第二。
- 下标为 2 的学生在第 2 场考试取得的分数为 3 ,这是考试的最低分,所以 TA 需要排在第三。
1 | int sortCol; |
指针
双指针用法
注意:避免访问未初始化的指针
eg:
1 | int *a;//未初始化的指针 |
数组的名字是数组第一个元素的地址
1 | int a[5]={1,2,3,4,5}; |
用指针直接定义一个字符串,用下标逐个获取每个元素
1 | char *str="i love you!"; |
数组和指针的区别
数组名只是一个地址,不可修改。而指针变量是一个左值,是可修改的
指针数组
存放指针变量的数组
1 | int a=1; |
数组指针
//指向整个数组的指针
//之前的指针指向的是数组某个元素的地址
1 | int temp[5]={1,2,3,4,5}; |
二维数组和指针
1 | int array[5][5]={0}; |
1 | *(array+i) == array[i]; |
数组指针和二维数组
1 | int array[2][3]={{0,1,2},{3,4,5}}; |
void指针和NULL指针
//void指针,通用指针
1 | int num=1; |
回溯算法
递归和回溯相辅相成
(纯暴力,不高效)
组合问题
切割问题
子集问题
排列问题
棋盘问题
抽象为树形结构
1 | void backtracking() |
滑动窗口
(属于双指针)
哈希表
高效的散列,俗称哈希(基于快速存取的角度设计的,典型的空间换时间的做法)
通过把关键码值key(编号)映射到表中一个位置(数组的下标)来访问记录,以加快访问的速度。这个映射函数就叫做散列函数,存放记录的数组叫做散列表
| 键KEY | 组员的编号,如1,5,19…… |
|---|---|
| 值VALUE | 组员的其他信息(包含姓名,年龄,战斗力等等) |
| 索引 | 数组的下标0,1,2,3,4(用以快速定位和检索数据) |
| 哈希桶 | 保存索引的数组(链表或者数组)数组成员为每一个索引值相同的多个元素 |
| 哈希函数 | 将文件编号映射到索引上,采用求余法。如:文件编号 19 |
1eg
1 |
|
二分查找
有序升序数组查找target,没有则返回-1
1 | int search(int* nums, int numsSize, int target){ |
二叉树
暴力直接建树
1 |
|
Binary Search Tree:二叉搜索树(BST):降低搜索复杂度
特点:每一个根节点一定比左节点大,比右节点小
1 |
|
链表
实例:链表的中间节点
1 | 输入:head = [1,2,3,4,5] |
1 | struct ListNode* middleNode(struct ListNode* head){ |
实例:合并两个有序链表
1 | 输入:l1 = [1,2,4], l2 = [1,3,4] |
1 | /** |
二叉链表层序遍历
1 | /*题目: |
1 |
|
位运算
位运算的由来
在计算机里面,任何数据最终都是用数字来表示的(不管是我们平时用的软件,看的图片,视频,还是文字)。
并且计算机运算单元只认识高低电位,转化成我们认识的逻辑,也就是 0 1 。
这就是导致计算机里面任何数据最终都是用二进制(0 1)来保存的数字。只是我们平时看到的图片、文字、软件都只从二进行数字转化而来的。
位运算符
常用位操作
判断奇偶
(x & 1) == 1 —等价—> (x % 2 == 1)
(x & 1) == 0 —等价—> (x % 2 == 0)
x / 2 —等价—> x >> 1
x &= (x - 1) ——> 把x最低位的二进制1给去掉
x & -x —–> 得到最低位的1
x & x —–> 00 << n)
指定位置的位运算
将X最右边的n位清零:x & (
获取x的第n位值:(x >> n) & 1
获取x的第n位的幂值:x & (1 << n)
仅将第n位置为1:x | (1 << n)
仅将第n位置为0:x & ((1 << n))((1 << (n + 1)) - 1))
将x最高位至第n位(含)清零:x & ((1 << n) - 1)
将第n位至第0位(含)清零:x & (
异或结合律
x ^ 0 = x, x ^ x = 0
x ^ (0) = ~x, x ^ (x) = ~0
a ^ b = c, a ^ c = b, b ^ c = a
(有没有点乘法结合律的意思)
字母表示:(a ^ b) ^ c = a ^ (b ^ c)
图形表示:(☆ ^ ◇) ^ △ = ☆ ^ (◇ ^ △)
UINT32_C
UINT32_C是一个宏,它定义了类型uint_least32_t的整数常数.
c - 1 << 31不能用 ‘int’类型表示吗?
在此代码上
1 | uint32_t z; |
最佳答案
使1无符号:
1 | uint32_t z; |
异或交换
1 | int x=10,y=7; |
这个函数功能:返回输入数据中,二进制中‘1’的个数。
对于不同的使用类型,可以采用采用以下函数:
1 | __builtin_popcount = int |
1 __builtin_ctz( ) / __buitlin_ctzll( )
用法:返回括号内数的二进制表示形式中末尾0的个数。
1 | int x=__builtin_ctz(64); |
2 __builtin_clz( ) / __builtin_clzll( )
注:ll指long long,是64位
用法:返回括号内数的二进制表示形式中前导0的个数。
1 | int x=__builtin_clz(63); |
3 __builtin_popcount( )
用法:返回括号内数的二进制表示形式中1的个数。
1 | int x= __builtin_popcount(4095); |
4 __builtin_parity( )
用法:返回括号内数的二进制表示形式中1的个数的奇偶性(偶:0,奇:1)。
1 | int x=__builtin_parity(1); |
5 __builtin_ffs( )
用法:返回括号内数的二进制表示形式中最后一个1在第几位(从后往前)。
1 | int x=__builtin_ffs(84); |
6 __builtin_sqrt( )
用法:快速开平方。(8位)
7 __builtin_sqrtf( )
用法:快速开平方。(4位)
来自陕西
C语言刷题常用基础知识
一、数组
数组的申请
a. 一维数组的申请
1
int* num = (int*)malloc(sizeof(int) * 10);
b. 二维数组的申请
1
2
3
4int** num = (int**)malloc(sizeof(int*) * 10);
for (int i = 0; i < 10; i++) {
num[i] = (int*)malloc(sizeof(int) * 10);
}c. 数组都赋0值
对于malloc申请的数组要这样:
1
2int* num = (int*)malloc(sizeof(int) * 10);
memset(num, 0, sizeof(int) * 10);对于非malloc申请的数组要这样:
1
2int num[10];
memset(num, 0, sizeof(num));比较函数(入参为malloc申请的数组)
a. 一维数组
1
2
3
4
5
6int cmp(const void* a, const void* b) {
int* aa = (int*)a;
int* bb = (int*)b;
//return *aa - *bb; //从小到大排序
return *bb - *aa; //从大到小排序
}b. 二维数组
1
2
3
4
5
6
7
8
9
10
11
12
13//假设数组是2列
int cmp(const void* a, const void* b) {
int* aa = *(int**)a;
int* bb = *(int**)b;
//0列相同时,比较1列
if (aa[0] == bb[0]) {
//return aa[1] - bb[1]; //从小到大排序
return bb[1] - aa[1]; //从大到小排序
}
//0列不相同时
//return aa[1] - bb[1]; //从小到大排序
return bb[1] - aa[1]; //从大到小排序
}比较函数有了后,就可以进行排序,用qsort
1
2
3//比如对 int* nums 进行排序,数组大小为10,比较函数为cmp
//第一个参数为数组,第二个参数为数组大小,第三个参数为数组里单个元素的大小,第四个参数为比较函数cmp
qsort(nums, 10, sizeof(int), cmp);力扣实现函数中
int* returnSize
及
int** returnColumnSize
的说明
int* returnSize 用来存二维数组的行的大小,用指针是方面时实修改大小,比如有10行,就这样写:
1
*returnSize = 10;
int** returnColumnSize 用来存二维数组列的大小,这里是用二维指针存,第一维用来指向这个数组,第二维用来指向每列的大小,给returnColumnSize赋值的时候要先申请空间,比如有size列,每列大小都为10,就这样写:
1
2
3
4*returnColumnSize = (int*)malloc(sizeof(int) * size);
for (int i = 0; i < size; i++) {
(*returnColumnSize)[i] = 10;
}
1 | int** myMalloc(int r, int c, int* returnSize, int** returnColumnSizes){ |
二、字符串
常用字符串函数有如下这些:
1 | strstr(arr, tmp); //在arr中从左到右查找第一个tmp,找到返回tmp开头的字符串的指针 |
这里把完整的分割字符串过程写一下:
1 | //strtok会使原字符串arr会变化成去掉第一个分割后的剩余字符串,所以分割前请先复制原串 |
三、指针
指针是C语言中比较常用的类型,也是C语言的灵魂,也是难点,出错点。主要还是知道到底想要表达什么意思也就自然而然了。指针是地址的意思,不同类型的变量所需地址空间大小不一样,所以也就需要区分不同类型的指针。“*”表示指针,“&”表示取地址。在判断指针是指向什么类型的时候,可以把变量去掉,剩下的就是指针所指向的。
1 | int* a; //指向整型的指针,指向 int* |
与指针相关的数据结构,如列表、树等,在后面算法总结中再书写。
C语言快速比较两数大小——fmax,fmin函数
分配函数
malloc
数组的申请
a. 一维数组的申请
1
int* num = (int*)malloc(sizeof(int) * 10);
b. 二维数组的申请
1
2
3
4int** num = (int**)malloc(sizeof(int*) * 10);
for (int i = 0; i < 10; i++) {
num[i] = (int*)malloc(sizeof(int) * 10);
}c. 数组都赋0值
对于malloc申请的数组要这样:
1
2int* num = (int*)malloc(sizeof(int) * 10);
memset(num, 0, sizeof(int) * 10);对于非malloc申请的数组要这样:
1
2int num[10];
memset(num, 0, sizeof(num));比较函数(入参为malloc申请的数组)
a. 一维数组
1
2
3
4
5
6int cmp(const void* a, const void* b) {
int* aa = (int*)a;
int* bb = (int*)b;
//return *aa - *bb; //从小到大排序
return *bb - *aa; //从大到小排序
}b. 二维数组
1
2
3
4
5
6
7
8
9
10
11
12
13//假设数组是2列
int cmp(const void* a, const void* b) {
int* aa = *(int**)a;
int* bb = *(int**)b;
//0列相同时,比较1列
if (aa[0] == bb[0]) {
//return aa[1] - bb[1]; //从小到大排序
return bb[1] - aa[1]; //从大到小排序
}
//0列不相同时
//return aa[1] - bb[1]; //从小到大排序
return bb[1] - aa[1]; //从大到小排序
}比较函数有了后,就可以进行排序,用qsort
1
2
3//比如对 int* nums 进行排序,数组大小为10,比较函数为cmp
//第一个参数为数组,第二个参数为数组大小,第三个参数为数组里单个元素的大小,第四个参数为比较函数cmp
qsort(nums, 10, sizeof(int), cmp);力扣实现函数中
int* returnSize及int** returnColumnSize 的说明
int* returnSize 用来存二维数组的行的大小,用指针是方面时实修改大小,比如有10行,就这样写:
1
*returnSize = 10;
int** returnColumnSize 用来存二维数组列的大小,这里是用二维指针存,第一维用来指向这个数组,第二维用来指向每列的大小,给returnColumnSize赋值的时候要先申请空间,比如有size列,每列大小都为10,就这样写:
1
2
3
4*returnColumnSize = (int*)malloc(sizeof(int) * size);
for (int i = 0; i < size; i++) {
(*returnColumnSize)[i] = 10;
}
realloc
C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
- ptr – 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
- size – 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。
1 |
|
typedef的4种用法
1) 为基本数据类型定义新的类型名
1 | typedef unsigned int COUNT; |
2) 为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称
1 | typedef struct tagPoint |
3) 为数组定义简洁的类型名称
1 | typedef int INT_ARRAY_100[100]; |
4) 为指针定义简洁的名称
1 | typedef char* PCHAR; |
枚举
enum(枚举)
枚举是 C 语言中的一种基本数据类型,用于定义一组具有离散值的常量。,它可以让数据更简洁,更易读。
枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。
定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。
枚举语法定义格式为:
1 | enum 枚举名 {枚举元素1,枚举元素2,……}; |
函数指针和结构体函数指针
函数指针
指针指向的是函数的入口地址
1 | void callback_c()//被回调的函数 |