本人比较菜,学了这么久的计算机,私以为自己基础知识还是有一些的。然而,一写代码就各种错误=_= 这篇文章是为了记录我在做LeetCode时遇到的各种错误以及原因,基本上都是很基础很基础的知识。
写代码注意事项:
1. mysql数据库相关
或者 update salary set sex = if(sex = 'm', 'f', 'm')
或者 update salary set sex = case sex when 'm' then 'f' else 'm' end
或者 update salary set sex = (case when sex = 'm' then 'f' when sex = 'f' then 'm' end)
可延伸为两个数不借助第三个数的情况下交换
a = a + b; b = a - b; a = a - b;
a = a ^ b; b = a ^ b; a = a ^ b;
2. Time Limit Exceeded错误
Run Code时出现这种类型的错误,首先查看代码中的循环部分,是否出现了死循环。此时应该仔细检查循环部分,是否因为某个变量的设置,导致循环无法结束。
如果循环没问题,那就可能是算法问题了,可以尝试换个思路,时间复杂度更低的算法。
3. load of null pointer of type 'const int'
在调用函数返回时,返回值如果是一个常量,则没问题。
返回值若为指针,则需注意会出现这个错误。如果返回的指针地址指向函数内的局部变量,在函数退出时,该变量的存储空间会被销毁,此时去访问该地址就会出现这个错误。
解决办法有以下三种:
建议使用malloc分配空间返回
4. assignment to expression with array type
数组名不能被赋值,即数组名不能作为赋值表达式的左值,初始化时除外。
例如:
int *array[100];
array = (int *)malloc(sizeof(int)*100); 错误
再如:
char str1[];
char str2[] = "hello,world!"
str1 = str2; 错误
而同样的情况下,指针是可以这样赋值的
原因:数组声明后会在内存空间中开辟一段空间,而数组名指向那段内存空间的首地址,它相当于一个常量指针,所存放的地址不变,就像我们不能改变常量的值一样。因此数组名不能作为赋值表达式的左值。
5. warning: incompatible implicit declaration of built-in function ‘xyz’
在调用之前未声明被调用函数(比如未引入头文件),或者被调用函数的定义位置在调用之后,都有可能会出现这个警告。
原因:在上述情况下,编译器不知道被调用函数的返回值信息,C会使用默认声明,默认声明规定函数返回值为int类型。而GCC为一些标准函数内置了定义,如果默认声明和内置定义不符,则会出现这个警告。
6. 本地调试
leetcode中的代码结果不正确时 需要在本地添加main函数 将程序写完整 进行测试 但是我每次测试时都 会遇到segmentation fault的错误 而且这种错误常常伴有二维指针的参数 之前我都是在main函数中定义一个二维数组 调用时传递二维数组的名称 在被调用函数中以array[i][j]的形式来操作该数组 出错位置就在此 在mian函数中可以以这种方式操作数组 但是在被调用函数中 它没有二维数组的概念 只是指向一维数组的指针 若想在被调用函数中像操作二维数组那样操作该参数 应该在main函数中定义一个指针数组 使得指针数组的每一项指向二维数组中每个一维数组的开始地址 同时传递该指针数组给被调用函数 这样才能达到效果
7. 在本地测试时可以得到正确结果 单独执行leetcode上给的例子也可以通过 但提交时就是不通过 找了很久的原因 才发现是我使用了全局变量的原因 不太清楚leetcode测试时的逻辑 但我猜测他在测试每个例子时不会清空全局变量的值 导致后面的例子使用的全局变量不是初始值 当我把全局变量变为参数传递时 则顺利通过
8. overflow in implicit constant conversion
原因:这个错误就是:常量转换溢出。C语言中char, int, float, double,unsigned char, unsigned int 等数值有极限范围,当它们之间(隐式)转换时,可能因数值极限而超界溢出。有的编译器会报告这一类型的错误,并不是所有编译器都会报告
9. 指针数组初始化方法
指针数组中的每个元素都是一个指针,如下的array数组中的每个元素都是一个字符串指针,指向一个一维字符串数组。
char **array;
array = (char **)malloc(sizeof(char *) * 100) ; //array包含100个指针元素 为这100个指针变量分配空间
for(int i = 0; i < 100; i++)
array[i] = (char *)malloc(sizeof(char) * 50); // 为array中的每个指针变量进行初始化 上面的表达式只是为指针变量分配了空间 并没有为它们赋值 此语句为每个指针分配了长度为50个字符的空间 并将该空间的初始地址赋值给array中的指针
10. 当条件分支不止两个时,要考虑分支的执行顺序,否则,会出现错误,本来该执行第三个分支,结果在第一个分支处结束。举例如下:
if(i % 3 == 0)
array[i - 1] = "Fizz";
else if(i % 5 == 0)
array[i - 1] = "Buzz";
else if(i % 15 == 0)
array[i - 1] = "FizzBuzz";
你原本想让它在3的倍数处输出Fizz,在5的倍数处输出Buzz,在3和5共同的倍数处输出FizzBuzz,然而,这段代码永远不会执行到第三个分支。
11. 在被调用函数中使用返回二维数组时使用指针数组还是二维指针?
结果是使用二维指针
首先看一下指针数组和二维指针的区别:
指针数组
int *array[100]; //在定义时已经为该数组开辟了100个整型指针空间,只是它们都指向NULL
for(int i = 0; i < 100; i++)
array[i] = (int *)malloc(sizeof(int)); //为每个指针元素所指向的元素开辟空间,并将指针元素指向开辟的空间首地址
二维指针:
int **array; //定义了一个二维指针,并没有赋初值
array = (int **)malloc(sizeof(int *) * 100) //为100个整型指针元素开辟空间,并将该空间的开始地址赋值给array
for(int i = 0; i < 100; i++)
array[i] = (int *)malloc(sizeof(int)); //为整型指针元素所指向的元素开辟空间,同时将其地址赋值给指针元素
看起来指针数组和二维指针没有差别,都是首先为指针元素开辟空间,再为指针元素所指向的元素开辟空间并将其地址赋值给指针元素,即使得指针元素指向该空间地址。
但是作为函数的返回值来传递时,使用指针数组会出现load of null pointer错误,因为指针数组作为函数内的数组变量,在程序退出函数时,数组内的指针变量会被释放,而这些指针变量所指向的空间为malloc开辟,不会被释放,但是指针变量被释放。
反观二维指针,它的指针变量空间和指针变量所指向的空间都是由malloc开辟,所以在函数退出时,它的空间不会被释放,可以被调用函数使用。
因此,在函数需要返回二维数组时,需要使用二维指针来传递。
12. 当使用哈希同时散列空间过大时,可以使用char类型定义散列数组,节省空间和时间,如下例:
long int hash[200000] = {0};
可以写为:
char hash[200000] = {0};
能够达到同样的效果
char和int类型比较形式相同,都是if(hash[i] == 0) hash[i] = 1;
13. malloc和calloc区别
void *malloc(unsigned int size);
void *calloc(size_t nmenb, size_t size);
malloc和calloc都是在程序执行过程中为变量分配内存空间,他们的区别是malloc只是分配空间,而calloc不仅分配空间,还为所分配空间进行初始化,将nmenb个元素空间置为0.
14. 两数比较
整数:a > b
注意:操作符两边如果类型不同 会默认进行强制转换 如strlen(str) > b 由于strlen函数返回值为无符号类型 b为int 比较时会对b进行强制转换为无符号类型 此时如果b为负数 转换之后的变量b将会是一个很大的无符号整数 导致比较结果失效 这种情况可以设置b为正数或者对前者进行强制类型转换为int类型
字符串:strcmp(a,b) 等于零时相等,小于零时a串小于b,大于零时a串大于b串
浮点型 fabs(a - b) < 0.00001 两数之差的绝对值小于某个很小的数时 可判定两个数相等
15. double free or corruption (fasttop)
多次释放内存错误,例子如下:
hasCycle(struct ListNode *head)
{
struct ListNode *p = head;
p->next = p;
}
在函数hasCycle中对链表的指针进行更改,使得main函数中释放链表时出错。
在这个例子中,判断是否有循环存在,我的思路是每次遍历一个node时,将其next指针指向head或者指向它本身,以此作为标记,当此node再一次出现时,即为有循环存在。
然而,这样破坏了链表的连接性,那些被访问过的节点指向了head或者它本身,导致main函数中释放链表时出现多次释放内存的错误,而且,只有head可以被释放,剩下的node没有指针指向它们。
16. 运算符优先级
693 Binary Number with Alternating Bits
判断一个数的二进制形式是否每两个相邻的bit都不同,而二进制只有0和1。所以我的思路是如果为奇数,最后一位为1,那么前面肯定是0101010101才符合每两个数都不同,跟0x55555555按位异或为0则满足条件,故代码如下:
if(n % 2 == 1)
{
if(n ^ 0x55555555 == 0)
return true;
else
return false;
}
然而结果总不对,后来才知道是因为if(n ^ 0x55555555 == 0)实际执行时为if(n ^ (0x55555555 == 0)),背离了我的意图即if((n ^ 0x55555555) == 0),所以结果不对。
然而,这道题这样改了之后还是不对。因为按位异或要求两个操作数完全相同才能是0。每个无符号数都占32位,即使它的有效位只有三位,如数字5,其余的高位都为零,而按位操作这些高位也会参与,除非你知道这个数占多少位,那么选择相应0x55555555的多少位与之进行异或,所以这样异或之后肯定不会是零,如5^0x55555555=0x55555550
第二种思路:如果改为按位与,所有偶数与0x55555555进行按位与运算为零则满足条件,否则不满足条件,比如10为1010,1010^0101=0,10^0x55555555=0。但存在反例,如数字8,二进制表示为1000,8^0x55555555=0,而8并不满足条件。
第三种思路:将原数右移一位与它本身做按位异或操作,如果满足条件结果将是每个二进制位都为1。然而这种思路也需要考虑高位为零的情况,跟第一种思路类似,不通。
总之,想通过一次按位操作就得到判断似乎是不可达的,必须老老实实依次对该数的每一位与邻位进行比较=_=!
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
-- |
() |
圆括号 |
(表达式)/函数名(形参表) |
-- |
||
. |
成员选择(对象) |
对象.成员名 |
-- |
||
-> |
成员选择(指针) |
对象指针->成员名 |
-- |
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
~ |
按位取反运算符 |
~表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
|||
-- |
自减运算符 |
--变量名/变量名-- |
|||
* |
取值运算符 |
*指针变量 |
|||
& |
取地址运算符 |
&变量名 |
|||
! |
逻辑非运算符 |
!表达式 |
|||
(类型) |
强制类型转换 |
(数据类型)表达式 |
-- |
||
sizeof |
长度运算符 |
sizeof(表达式) |
-- |
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
|||
% |
余数(取模) |
整型表达式%整型表达式 |
|||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
|||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
|||
< |
小于 |
表达式<表达式 |
|||
<= |
小于等于 |
表达式<=表达式 |
|||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1?表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
-- |
/= |
除后赋值 |
变量/=表达式 |
-- |
||
*= |
乘后赋值 |
变量*=表达式 |
-- |
||
%= |
取模后赋值 |
变量%=表达式 |
-- |
||
+= |
加后赋值 |
变量+=表达式 |
-- |
||
-= |
减后赋值 |
变量-=表达式 |
-- |
||
<<= |
左移后赋值 |
变量<<=表达式 |
-- |
||
>>= |
右移后赋值 |
变量>>=表达式 |
-- |
||
&= |
按位与后赋值 |
变量&=表达式 |
-- |
||
^= |
按位异或后赋值 |
变量^=表达式 |
-- |
||
|= |
按位或后赋值 |
变量|=表达式 |
-- |
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
-- |
17. free(): invalid next size (fast): 0x0000000000d00c40 ***
18. 在涉及到二叉树结构时,切记每次malloc为node分配空间之后,同时将左子树和右子树指向空,第897. Increasing Order Search Tree出错原因记录。