C/C++经验技巧总结

C语言编程实践中的易错点总结

2018-01-15  本文已影响7人  XDgbh

持续更新中。。。

字符串相关易错点

方法1:使用全局声明的字符数组变量,函数返回数组名(也就是字符串的首地址)。缺点是所有人都可以修改这个内存。
方法2:函数内使用静态字符数组,函数返回数组名(也就是字符串的首地址)。缺点是下一次调用的时候将覆盖掉这个数组内容,而且这块静态内存会一直存在,如果很大,不用的时候也没法主动释放,很浪费内存
方法3:函数内使用malloc()函数动态分配内存,函数返回内存指针(也就是字符串的首地址)。但缺点是调用者可能不知道函数内开辟了动态内存,而忘记用free()函数释放内存。
方法4:函数调用者在调用前主动分配动态内存,然后传入内存首地址做函数的形参,函数不需要再返回字符串地址。因为是调用者自己开辟的内存,所以很容易想起要free()释放内存。这种方法适合需要字符串的作用域范围大的场景,只要还没有free()释放内存,那么在其他的代码块或者函数内也都可以使用这个动态内存。

void getString(char * str, int size)
{
   char str_temp[] = "";
   。。。操作str_temp得到一个想要的字符串
   strncpy(str, str_temp, size);   
 //上面四种方法在具体的字符串赋值时都应该使用strncpy()函数,而不能直接赋值。
}

调用函数:
char * buffer = malloc(sizeof(char)*size);
getString(buffer, size);  //将字符串长度也传入,防止溢出错误
。。。
free(buffer);  //容易记得用完释放内存。

方法5:类似于方法4,但是是用char buffer[size];代替char * buffer = malloc(sizeof(char)*size);,使用局部变量内存来代替malloc开辟的堆内存,可以省掉free()的麻烦。使得这个buffer字符串的作用域限制在这个代码块或函数中,离开这个代码块或函数后会被自动释放。

指针使用错误

理解分析的步骤:
1、首先,找到左边起的第一个普通变量名(非关键字)开始分析。如上面例子找到变量名do_stharr[10]
2、根据变量名周围的优先级情况来进行下一个分析:
—— 2.1)先把变量名所在的被括号扩起的一部分先当做一个整体,如上例子(*do_sth)(* arr[10])。先可以轻松读出do_sth是一个指向什么的指针,arr是一个数组且数组的每一个元素都是一个指向什么的指针。
——2.2)看整体的后缀符号,若是()说明这是一个函数名,到此处可知do_sth是一个指向函数的指针;若是[]说明这是一个数组,到此处可知,数组arr的每一个元素都是指向一个长度为20的字符数组的指针。
——2.3)看整体的前缀符号,星号*表示“后面的所有字符整体是一个指针,指向前面的数据类型”。如例1表示该函数的返回值也是一个指针。
3、再看剩下的前面的修饰符数据类型说明,如上例1char * const,表示该函数的返回值是一个类型为char的常量指针(该指针指向一个地址之后不能再指向其他地址,但是不管指向的地址内容如何改变)。若是const char *或者char const *都表示一个类型为指向只可读字符的指针(该指针只能用来读取地址中的数据,不能用来修改该数据,但是可以更改指向的地址)。

综上可知:
char * const * (*do_sth)(int *p); ——do_sth是一个指向一个函数的指针,该函数的返回值也是另一个指针,该指针指向一个char型的指针常量。函数接收的参数是一个指向int型的指针变量。
char (* arr[10])[20];——arr是一个有10个元素的数组,每个元素都是一个指针,每个指针分别指向一个有20个char型元素的数组。
char * (* func[5])(char **str);——func是一个有5个元素的数组,每个元素都是一个分别指向一个函数的指针,函数的返回值是一个指向char型的指针。函数的参数str是一个指向char *(char型指针)类型的指针。

break关键字使用错误

static关键字使用

关于操作符优先级

malloc()函数使用注意

void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法
》》》注意:
1)memset函数按字节对内存块进行初始化,每个字节是8bit位,所以ch的值不管取多大,也只能把二进制形式的后八位存到该字节内存。
2)int num; memset(&num, 1, sizeof(int));因为是对每个字节赋值ch(这里是1),所以不要误以为这是给int型变量赋值1,真正的是给每个字节都赋值00000001,结果num的4字节存入的值为:00000001 00000001 00000001 00000001,换算成10进制就是num==16843009。
3)memset(ptr, 0, sizeof(内存块字节大小));这一句不能简单的写成memset(ptr, 0, sizeof(ptr));。sizeof(ptr)是求得一个指针(即一个地址)的大小,32位编译器默认一个地址大小是4个字节,并不是指的ptr指针指向的数组或结构体的内存大小。

union联合类型什么时候使用合适

union secondary_characteristics{   /*第二特征,两个互斥的成员*/
  char has_fur;
  short num_of_legs_in_excess_of_4;
};
struct animals{
  char has_backbone;    /*是否有脊椎*/
  union secondary_characteristics character;
};

这样做的好处是可以节省内存,如果程序运行期间,要存储几百万个动物,那么每个动物节省一个字节,也可以省下几百万字节,也就节省下几十兆的运行内存。

union bits32_tag{
  int whole;                              /*一个32位的整型值*/
  struct { char c0, c1, c2, c3; } byte;   /*4个8位的字节*/   
} value32;

分支语句switch—case注意点

if语句连续判断有讲究

printf函数的参数从右往左执行再输出

int main()
{
    int arr[] = { 0, 1, 2, 3, 4, 5 };
    int *p_arr1 = arr;
    printf("%d %d \n", *p_arr1, *(p_arr1++));   //print是从右往左执行再输出:1 0,而不是输出0 0
    int *p_arr2 = arr;
    printf("%d %d \n", *p_arr2, *(++p_arr2));   //print是从右往左执行再输出:1 1,而不是输出0 1
}
上一篇下一篇

猜你喜欢

热点阅读