c语言踩坑心得

Python018

c语言踩坑心得,第1张

在c语言中,0和NULL等价,NULL是个define ((void *)0)

malloc申请 连续 的内存空间,但是不会擦除该内存的数据,如果你忘记初始化了,会用到上次遗留的内容,所以最好用memset初始化为0

calloc按块申请内存空间,有个参数n,数组的话不再是n*sizeof(sth.),而是(n,sizeof(sth.)),并且会初始化内存数据为0

申请了内存空间的指针才可以被free,不然会报错

当声明了一个指针时,它指向的地址是随机的,比如 int *a,这个a就是随机指向的,是个野指针

所以为了让它确定指向,必须要初始化,比如 int *a=NULL

当指针成功申请了内存空间后,它指向了某个内存地址,可能是0xabcdef之类的这个时候调用了free,内存被回收了,但是它的指向没有变,是个野指针。

所以在free之后必须将指针重新赋值,比如 free(a)a=NULL

我们可以用指向指针的指针来封装一个free函数,在free后,将指针赋值为空

一个启发式的实现如下

sizeof一个数组时,他是数组一共占的大小

sizeof一个指针时,他是指针的大小

sizeof计算的是栈中分配的空间大小

size_t是一个无符号整数,当它和int比较大小时,会将int变为无符号整数比较。

int a=-1size_t b=1 如果进行比较a>b,那么程序里将得到真值true

strcpy是一个不安全的函数,当dest字符串的容量小于src的长度就会引起缓冲区溢出或者字符串没有结束符号 '\0'

strncpy是一个相对安全的函数,但注意地非常多

当dest字符串容量远大于src的长度时,如果你的n设定为dest的容量,那么他会在赋值完毕之后,在空的地方全部写入 '\0' ,非常耗费时间

当dest字符串容量小于等于src的长度时,src会被截断或刚好复制完毕,但是这时候strncpy并不会帮你加上'\0',即使成功复制。也就是说,如果n设定为src的长度(strlen,返回长度不算 '\0' ),需要程序员去补上字符串结束符

一个启发式的实现如下

当我们使用全局变量的时候,如果不加static,那么多个文件一起编译的时候,其他文件不能定义这个同名变量。这样封装性不好,如果我们不想让其他文件使用,只要在全局变量前加上static就可以了。这是static重要作用之一,隐藏这个全局变量。要注意static的变量放在静态数据区,默认值是0。 static char[10]static int a字符串就是空的,整数就是0

那如果我们想引用其他文件的变量,可以加上extern对同名变量进行声明

开辟空间时,尽量以4为约数,如 char a[12]

定义变量的时候,尽量从上到下,由小到大排列

声明变量是易变的,以阻止编译器对指令进行优化,确保数据从内存中读取,而不是寄存器(缓存)中

同const一样,有volatile指针,在结构体中要小心

https://stackoverflow.com/questions/2044565/volatile-struct-semantics

snprintf 函数是C语言中非常常用的一个函数,它的作用是格式化一段文本放到指定的缓存中。和它类似的还有sprintf, vsnprintf等。它们都完成类似的功能,但又有些区别,如sprintf 是不会检查输出缓存的长度的,需要调用者确保长度是足够的。vsnprintf 函数和snprintf 很像,但它是通过 va_list 来传入格式化参数,所以一般用于传入了可变的需要格式化字符串的函数内。而我们最常用的就是snprintf 了,因为它具有缓冲区的溢出保护。

snprintf函数的原型如下:

其中: str 传入输出缓冲区的地址;size指出输出缓冲区的大小;format 为传入的格式化字符串;其后的参数就是格式化的参数了。

该函数返回一个整数,表示格式化后的文本大小。通过这个返回值,我们可以做很多事情。也正是对这个返回值的理解不够或者使用错误,我们往往会踩一些坑。

最普通的用法是使用该函数来格式化一段文本,如:

该函数具有缓冲区保护的功能,如果格式化输出内容超过了缓冲区的最大长度,那么输出字符串会被截断到缓冲区的最大长度。同时无论如何,字符串最后的结尾 ‘’ 是一定会有的,保证了输出的字符串不会溢出。所以这也是广大程序员喜欢使用这个函数的原因。

如果我们需要格式化一个具有多个元素的数组,那么这个时候就可以充分利用snprintf的返回值了。我们只需要反复调用snprintf 并移动缓冲区的起始位置就能实现。代码如下:

这段代码看起来能正常工作,通常也确实可以正常工作,但是前提条件是 szText的缓冲区大小是足够大的。如果缓冲区的大小不够会发生什么情况?你不妨改小szText再试试。

那么为什么会出现问题呢?首先我们要深入理解一下snprintf 返回值的含义。按前面所述,返回值表示格式化后文本的大小,注意不是返回输出缓冲内的字符串长度。什么意思呢,也就是说,如果输出的缓冲足够大,大到可以容纳所有的输出文本,那么这个返回的长度恰恰就是输出缓存中的字符串长度。如果输出缓冲不够大,或者根本就没有传入输出缓冲这个参数呢?返回值就和实际的输出文本长度不一样了。为什么是这样?其实这个函数本来也是可以用来计算格式化所需要的最小缓存大小,并根据返回值来实际分配输出缓冲的内存。如下代码所示的用法:

有人会疑惑,为什么需要这样写?上面这个例子可能不明显。但是如果需要格式化的内容不固定,长度变化范围很大。那么就有可能不太好使用固定大小的缓冲。

理解了这个返回值的含义和用法,再回到上面那个循环调用的例子。我们需要加上对长度的校验就没有问题了:

如果有什么疑问,欢迎一起讨论。