c language more common errors caused by a memory allocation memory bounds _ _ _ uninitialized memory structure implicit memory is too small _ Pointer

This article is reproduced from a blogger's blog garden essays, and do a very minor modifications, the original address: https://www.cnblogs.com/haore147/p/3647348.html

1. Pointer does not point to a legitimate memory

  Defines a pointer variable, but not for the allocated memory, i.e., a valid pointer does not point in the simple example is not cited here a few examples of more subtle.

1.1 uninitialized pointer structure member

 

1

2

3

4

5

6

7

8

9

10

11

struct student

{

  char *name;

  int score;

}stu,*pstu;

<br>int main()

{

  strcpy(stu.name,"Jimy");

  stu.score = 99;

  return 0;

}  


Many beginners make this mistake do not know how it was. This defines the structure variables stu, but he did not expect the internal structure char * name which members when defining structure variables stu, just to name the pointer variable itself is allocated four bytes. name pointer did not refer to a valid address (do not even know the size), this time its internal memory of just some garbled. So call the strcpy function, the string is "Jimy" referring to the memory garbled copy, but this memory name pointer simply do not have access to, result in an error. The solution is to malloc a space for the name pointer.

Similarly, when one commits an error as follows:

1

2

3

4

5

6

7

8

int main()

{

  pstu = (struct student*)malloc(sizeof(struct student));

  strcpy(pstu->name,"Jimy");

  pstu->score = 99;

  free(pstu);

  return 0;

}  


Pstu pointer variable is allocated memory, but also did not allocate memory pointer to the name. Errors in the first case above, the solution is the same. Here with a malloc gives a false impression, that also gives name pointer allocated memory. Two pointers in relation with twice the memory open , so you should do this:

 

1

2

3

4

5

6

7

8

int main()

{

  pstu = (struct student*)malloc(sizeof(struct student));

       pstu->name = (char *)malloc( 10 * sizeof(char) );

  strcpy(pstu->name,"Jimy");

  pstu->score = 99;

  free(pstu->name);

  free(pstu);

  return 0;

}  

   

 

1.2 not allocate enough memory for a pointer structure

 

1

2

3

4

5

6

7

8

int main()

{

  pstu = (struct student*)malloc(sizeof(struct student*));

  strcpy(pstu->name,"Jimy");

  pstu->score = 99;

  free(pstu);

  return 0;

}  


When memory is allocated pstu, allocated memory size is not suitable. Here the sizeof (struct student) mistakenly written as sizeof (struct student *). Of course, the same name is not allocated memory pointer. Solution Ibid.

 

1.3 at the inlet check function pointers do

 

  一般在函数入口处使用

1

assert(NULL != p)

对参数进行校验。在非参数的地方使用

1

if(NULL != p)

来校验。但这都有一个要求,即p在定义的同时被初始化为NULL了。比如上面的例子,即使用if(NULL != p)校验也起不了作用,因为name指针并没有被初始化为NULL,其内部是一个非NULL的乱码。

  assert是一个宏,而不是函数,包含在assert.h头文件中。如果其后面括号里的值为假,则程序终止运行,并提示出错;如果后面括号里的值为真,则继续运行后面的代码。这个宏只在Debug版本上起作用,而在Release版本被编译器完全优化掉,这样就不会影响代码的性能。

  有人也许会问,既然在Release版本被编译器完全优化掉,那Release版本是不是就完全没有这个参数入口校验了呢?这样的话那不就跟不使用它效果一样吗?是的,使用assert宏的地方在Release版本里面确实没有了这些校验。

  但是我们要知道,assert宏只是帮助我们调试代码用的,它的一切作用就是让我们尽可能的在调试函数的时候把错误排除掉,而不是等到Release之后。它本身并没有除错功能。再有一点就是,参数出现错误并非本函数有问题,而是调用者传过来的实参有问题。assert宏可以帮助我们定位错误,而不是排除错误。

  这里其实涉及到了c语言的编程风格问题。

 

2. 为指针分配的内存太小

  为指针分配了内存,但是内存大小不够,导致出现越界错误。

1

2

3

char *p1 = “abcdefg”;

char *p2 = (char *)malloc(sizeof(char)*strlen(p1));

strcpy(p2,p1);


p1是字符串常量,其长度为7个字符,但其所占内存大小为8个byte。初学者往往忘了字符串常量的结束标志“\0”。这样的话将导致p1字符串中最后一个空字符“\0”没有被拷贝到p2中。解决的办法是加上这个字符串结束标志符:

1

char *p2 = (char *)malloc(sizeof(char)*strlen(p1)+1*sizeof(char));


这里需要注意的是,只有字符串常量才有结束标志符。比如下面这种写法就没有结束标志符了:

1

char a[7] = {‘a’,’b’,’c’,’d’,’e’,’f’,’g’};


另外,不要因为char类型大小为1个byte就省略sizof(char)这种写法。这样只会使你的代码可移植性下降。

 

3.内存分配成功,但并未初始化

 

  犯这个错误往往是由于没有初始化的概念或者是以为内存分配好之后其值自然为0。未初始化指针变量也许看起来不那么严重,但是它确确实实是个非常严重的问题,而且往往出现这种错误很难找到原因。

  曾经有一个学生在写一个windows程序时,想调用字库的某个字体。而调用这个字库需要填充一个结构体。他很自然的定义了一个结构体变量,然后把他想要的字库代码赋值给了相关的变量。但是,问题就来了,不管怎么调试,他所需要的这种字体效果总是不出来。我(陈正冲)在检查了他的代码之后,没有发现什么问题,于是单步调试。在观察这个结构体变量的内存时,发现有几个成员的值为乱码。就是其中某一个乱码惹得祸!因为系统会按照这个结构体中的某些特定成员的值去字库中寻找匹配的字体,当这些值与字库中某种字体的某些项匹配时,就调用这种字体。但是很不幸,正是因为这几个乱码,导致没有找到相匹配的字体!因为系统并无法区分什么数据是乱码,什么数据是有效的数据。只要有数据,系统就理所当然的认为它是有效的。

  也许这种严重的问题并不多见,但是也绝不能掉以轻心。所以在定义一个变量时,第一件事就是初始化。你可以把它初始化为一个有效的值,比如:

1

2

int i = 10

char *p = (char *)malloc(sizeof(char));


但是往往这个时候我们还不确定这个变量的初值,这样的话可以初始化为0或NULL。

1

2

int i = 0

char *p = NULL;

 

如果定义的是数组的话,可以这样初始化:

1

int a[10] = {0};


或者用memset函数来初始化为0:

1

memset(a,0,sizeof(a));

  

memset函数有三个参数,第一个是要被设置的内存起始地址;第二个参数是要被设置的值;第三个参数是要被设置的内存大小,单位为byte。这里并不想过多的讨论memset函数的用法,如果想了解更多,请参考相关资料。


至于指针变量如果未被初始化,会导致if语句或assert宏校验失败。这一点,上面已有分析。

 

 

4.内存越界

 

内存分配成功,且已经初始化,但是操作越过了内存的边界。这种错误经常是由于操作数组或指针时出现“多1”或“少1”。比如:

1

2

3

4

5

int a[10] = {0};

for (i=0; i<=10; i++)

{

  a[i] = i;

} 

所以,for循环的循环变量一定要使用半开半闭的区间,而且如果不是特殊情况,循环变量尽量从0开始。

Guess you like

Origin blog.csdn.net/shanshenyuyou/article/details/94549373