我的代码有问题。代码编译并在没有警告或错误的情况下运行,并且我想要这样做,但Valgrind找到了我无法修复的内存泄漏。我好几天都无法自己纠正代码。你能指出我做错了吗?
这是练习,source.c是我自己制作的代码,其他一切都已经给出了。想法是我需要在连续的插槽中创建包含Student结构的动态数组。
据我了解,Valgrind认为在create_student中malloc为数组输入分配了太少的内存然后测试函数(我们大学的服务器端的东西),同时用它的某些方式指向我的名字字符串指向NULL指针并输出内存边界。
我尝试了几种方法,这不会给编译器(Code :: Blocks)带来任何错误或警告,并按预期工作,但Valgrind仍然不喜欢它..
提前谢谢你!
Valgrind输出:
==358== Invalid read of size 8
==358== at 0x4016CB: test_create_student (test_source.c:38)
==358== by 0x406340: srunner_run_all (in /tmc/test/test)
==358== by 0x40288A: tmc_run_tests (tmc-check.c:121)
==358== by 0x402544: main (test_source.c:310)
==358== Address 0x518d768 is 0 bytes after a block of size 56 alloc'd
==358== at 0x4C244E8: malloc (vg_replace_malloc.c:236)
==358== by 0x402C5D: create_student (source.c:18)
==358== by 0x401690: test_create_student (test_source.c:35)
==358== by 0x406340: srunner_run_all (in /tmc/test/test)
==358== by 0x40288A: tmc_run_tests (tmc-check.c:121)
==358== by 0x402544: main (test_source.c:310)
==358==
==358== Invalid read of size 1
==358== at 0x4C25D94: strcmp (mc_replace_strmem.c:426)
==358== by 0x4016DC: test_create_student (test_source.c:38)
==358== by 0x406340: srunner_run_all (in /tmc/test/test)
==358== by 0x40288A: tmc_run_tests (tmc-check.c:121)
==358== by 0x402544: main (test_source.c:310)
==358== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==358==
==358==
==358== Process terminating with default action of signal 11 (SIGSEGV)
==358== Access not within mapped region at address 0x0
==358== at 0x4C25D94: strcmp (mc_replace_strmem.c:426)
==358== by 0x4016DC: test_create_student (test_source.c:38)
==358== by 0x406340: srunner_run_all (in /tmc/test/test)
==358== by 0x40288A: tmc_run_tests (tmc-check.c:121)
==358== by 0x402544: main (test_source.c:310)
==358== If you believe this happened as a result of a stack
==358== overflow in your program's main thread (unlikely but
==358== possible), you can try to increase the size of the
==358== main thread stack using the --main-stacksize= flag.
==358== The main thread stack size used in this run was 8388608.
这是源文件:
#include "source.h"
#include "string.h"
#include "stdlib.h"
/* Parameters:
* s: pointer to the Students main structure (allocated by caller)
* name: name of student
* id: Student ID
* age: age
* course: course code
* Returns: pointer to the student element in the array
*/
Student *create_student(Students *s, const char *name, const char *id,
unsigned char age, const char *course)
{
if(s->count == 0){
s->array = malloc(sizeof(Student));
if(s->array == NULL)
return NULL;
}
if(s->count > 0){
Student *nptr = realloc(s->array, (s->count+1)*sizeof(Student));
if(nptr == NULL)
return NULL;
else
s->array = nptr;
}
s->array[s->count].name = malloc((strlen(name)+1)); /*Allocating memory for name pointer*/
if(s->array[s->count].name == NULL)
return NULL;
memcpy(s->array[s->count].name, name, strlen(name)); /*Copying name string to the *name array*/
s->array[s->count].name[strlen(name)] = '\0';
if(strlen(id) > 8|| strlen(course) > 16) /*Checks if the strings are correct length*/
return NULL;
memcpy(s->array[s->count].id, id, strlen(id)); /*Copying rest of the files*/
s->array[s->count].id[strlen(id)] = '\0';
memcpy(s->array[s->count].course, course, strlen(course));
s->array[s->count].course[strlen(course)] = '\0';
s->array[s->count].age = age;
s->array[s->count].points = NULL;
s->array[s->count].numPoints = 0;
s->count++; /*Count is incremented by one as the first student is created*/
s->array = &s->array[0]; /*Now *array points at the first entry in the array*/
return &s->array[s->count]; /*Returns newly created pointer to the student*/
}
/* Parameters:
* s: pointer to the Students main structure
* id: Student ID to be looked for
* course: Course code to be looked for
* Returns: pointer to the student element in array, if found. NULL if not found
*/
Student *find_student(Students *s, const char *id, const char *course)
{
Student *ptr = s->array; /*Creating pointer that points at the current student structure (starts from the beginning)*/
int r1, r2;
for(unsigned int i = 0; i < s->count; i++){
r1 = strcmp(ptr->course, course); /*Checks for ID and course number*/
r2 = strcmp(ptr->id, id);
if(r1 == 0 && r2 == 0){
return ptr; /*Returns pointer to the student structure if matches*/
}
ptr++;
}
return NULL; /*Returns NULL if no such student found*/
}
/* Parameters:
* s: pointer to the Students main structure
* id: Student ID to be deleted
* course: Course from which student is deleted
* Returns: 1 if deletion was successful, 0 if not (e.g. student not found)
*/
int delete_student(Students *s, const char *id, const char *course)
{
Student *st0 = s->array; /*Pointer at the first element in the array*/
Student *stl = st0; /*Pointer at the last element in the array*/
for(int i = 0; i < (s->count-1); i++)
stl++;
if(find_student(s, id, course) == st0){ /*1. Deleting the first element in the array*/
s->count--;
free(st0->name);
for(int i = 0; i < s->count; i++)
memcpy(st0+i, st0+(i+1), sizeof(Student));
s->array = realloc(s->array, (s->count)*sizeof(Student));
return 1;
}
else if(find_student(s, id, course) == stl){ /*2. Deleting the last element in the array*/
s->count--;
free(stl->name);
s->array = realloc(s->array, (s->count)*sizeof(Student));
return 1;
}
else if(find_student(s, id, course) != NULL){ /*3. Deleting the element in the middle of array*/
int a = 0;
while(st0 != find_student(s, id, course)){
a++;
st0++;
}
free(st0->name);
s->count--;
for(int i = 0; i < s->count-a; i++){
memcpy(st0+i, st0+(i+1), sizeof(Student));
}
s->array = realloc(s->array, (s->count)*sizeof(Student));
return 1;
}
return 0;
}
/* Parameters:
* s: pointer to the Students main structure
* id: student ID to set the points
* course: course ID to set the points
* points: array of points to be set to the student (will replace previous entry)
* len: length of the points array
* Returns: 1 if setting points was successful, 0 if not (e.g. student not found)
*/
int set_points(Students *s, const char *id, const char *course, const float *points, int len)
{
Student *st = find_student(s, id, course);
if(st != NULL){
if(st->points == NULL){
st->points = malloc(len * sizeof(int));
for(int i = 0; i < len; i++)
st->points[i] = points[i];
st->numPoints = len;
return 1;
}
else{
st->points = realloc(st->points, len * sizeof(int));
for(int i = 0; i < len; i++)
st->points[i] = points[i];
st->numPoints = len;
return 1;
}
}
return 0;
}
这是source.h:
typedef struct student Student;
struct student {
char *name; // name of the student
char id[8]; // null-terminated student ID
unsigned char age;
char course[16]; // null-terminated course code;
float *points; // pointer to dynamic array of exercise points
unsigned int numPoints; // length of the above array
};
typedef struct {
unsigned int count; // size of the students array
Student *array; // pointer to the first element in the array
} Students;
Student *create_student(Students *s, const char *name, const char *id,
unsigned char age, const char *course);
Student *find_student(Students *s, const char *id, const char *course);
int delete_student(Students *s, const char *id, const char *course);
int set_points(Students *s, const char *id, const char *course, const float *points, int len);
这是主要的:
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "source.h"
void print_students(Students *s)
{
Student *st = s->array;
for (unsigned int i = 0; i < s->count; i++) {
printf("%s (%s), Course: %s, Age: %d\n", st->name, st->id, st->course, st->age);
if (st->numPoints) {
printf(" -- Points: ");
for (unsigned int j = 0; j < st->numPoints; j++)
printf("%f ", st->points[j]);
printf("\n");
}
st++;
}
}
void initialize_reg(Students *reg) {
assert(reg != NULL);
reg->count = 0;
reg->array = NULL;
create_student(reg, "Teemu Teekkari", "00000A", 20, "ELEC-A1100");
create_student(reg, "Matti Meikäläinen", "12345B", 28, "ELEC-A1100");
create_student(reg, "Wow", "33333C", 28, "ELEC-A1100");
create_student(reg, "Much Student", "98765H", 28, "ELEC-A1100");
create_student(reg, "Such course", "12121R", 28, "ELEC-A1111");
create_student(reg, "Amaze", "11111T", 28, "ELEC-A1111");
}
int main()
{
Students s;
s.count = 0;
s.array = NULL;
// create a group of students using create_student
initialize_reg(&s);
print_students(&s);
// Try find_student
Student *sf3 = find_student(&s, "33333C", "ELEC-A1100");
Student *sf6 = find_student(&s, "11111T", "ELEC-A1111");
if (!sf3) {
printf("Did not find existing student 33333C\n");
} else if (strcmp(sf3->id, "33333C")) {
printf("Incorrect student ID %s when should have been 33333C\n", sf3->id);
}
if (!sf6) {
printf("Did not find existing student 11111T\n");
} else if (strcmp(sf6->id, "11111T")) {
printf("Incorrect student ID %s when should have been 11111T\n", sf6->id);
}
// Try delete_student with existing student
if (!delete_student(&s, "12121R", "ELEC-A1111")) {
printf("Delete student failed for existing student\n");
}
printf("-----\n");
float p[] = {3.0, 1.0, 4.0, 4.5};
set_points(&s, "00000A", "ELEC-A1100", p, 4);
print_students(&s);
printf("-----\n");
// Try delete_student with non_existing student
delete_student(&s, "33333C", "ELEC-A1100");
print_students(&s);
}
这是Valgrind引用的test_source:
#include <check.h>
#include "tmc-check.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include "../src/source.h"
void release_memory(Students *s) {
unsigned int i;
if (s->array) {
for (i = 0; i < s->count; i++) {
Student *st = &(s->array[i]);
if (st->name)
free(st->name);
if (st->points)
free(st->points);
}
free(s->array);
}
}
START_TEST(test_create_student) {
Students reg;
//assert(reg != NULL);
reg.count = 0;
reg.array = NULL;
char buf[160];
//char *name = malloc(strlen("Teemu Teekkari") + 1);
//strcpy(name, "Teemu Teekkari");
char *name = "Teemu Teekkari";
Student *s1 = create_student(®, name, "00000A", 20, "ELEC-A1100");
fail_unless(s1 != NULL, "[Task 3.4.a] create_student returned NULL.\n");
if (strcmp(name, s1->name)) {
sprintf(buf, "[Task 3.4.a] Student name should be %s, was %s.\n", name, s1->name);
release_memory(®);
fail(buf);
}
if (strcmp("00000A", s1->id)) {
sprintf(buf, "[Task 3.4.a] Student ID should be %s, was %s.\n", "00000A", s1->id);
release_memory(®);
fail(buf);
}
if (s1->age != 20) {
sprintf(buf, "[Task 3.4.a] Student age should be 20, was %d\n.", s1->age);
release_memory(®);
fail(buf);
}
if (strcmp("ELEC-A1100", s1->course)) {
sprintf(buf, "[Task 3.4.a] Course code not should be %s, was %s.\n", "ELEC-A1100", s1->course);
release_memory(®);
fail(buf);
}
if (s1->points != NULL) {
sprintf(buf, "[Task 3.4.a] Points array should be NULL, was %p.\n", s1->points);
release_memory(®);
fail(buf);
}
if (s1->numPoints != 0) {
sprintf(buf, "[Task 3.4.a] numPoints should be 0, was %d.\n", s1->numPoints);
release_memory(®);
fail(buf);
}
//free(name);
//fail_unless(!strcmp("Teemu Teekkari", s1->name), "[Task 17.1] Student name was not allocated from heap.");
if (reg.count != 1) {
sprintf(buf, "[Task 3.4.a] After adding a student, student count should be 1, was %d.\n",
reg.count);
release_memory(®);
fail(buf);
}
if (reg.array != s1) {
release_memory(®);
fail("[Task 3.4.a] After adding one student, the returned value does not point to the beginning of array.\n");
}
Student *s2 = create_student(®, "Peppilotta Sikuriina Rullakartiina Kissanminttu Efraimintytar Pitkatossu", "12345B", 10, "ELEC-A1100");
if ((reg.array) + 1 != s2) {
release_memory(®);
fail("[Task 3.4.a] After adding second student, the returned value does not point to the second array member.\n");
}
if (reg.count != 2) {
sprintf(buf, "[Task 3.4.a] After adding second student, the student count should be 2, was %d.\n",
reg.count);
release_memory(®);
fail(buf);
}
#if 0
Student *s3 = create_student(®, "Ylimaaraisia Merkkeja", "99999Ffoofoofoofoo", 60, "ELEC-A1111-even-16-characters-is-too-much-for-a-course-code");
assert(s3 != NULL);
if (strcmp("99999Ff", s3->id)) {
sprintf("[Task 3.4.a] Too long student id truncated incorrectly: %s, should be %s\n",
s3->id, "99999Ff");
release_memory(®);
fail(buf);
}
if (strcmp("ELEC-A1111-even", s3->course)) {
sprintf(buf, "[Task 3.4.a] Too long course code truncated incorrectly: %s, should be %s\n",
s3->course, "ELEC-A1111-even");
release_memory(®);
fail(buf);
}
#endif
release_memory(®);
}
END_TEST
Students *initialize_reg(Students *reg) {
//Students *reg = malloc(sizeof(Students));
assert(reg != NULL);
reg->count = 0;
reg->array = NULL;
create_student(reg, "Teemu Teekkari", "00000A", 20, "ELEC-A1100");
create_student(reg, "Matti Meikäläinen", "12345B", 28, "ELEC-A1100");
create_student(reg, "Wow", "33333C", 28, "ELEC-A1100");
create_student(reg, "Much Student", "98765H", 28, "ELEC-A1100");
create_student(reg, "Such course", "12121R", 28, "ELEC-A1111");
create_student(reg, "Amaze", "11111T", 28, "ELEC-A1111");
return reg;
}
START_TEST(test_find_student) {
Students regb;
char buf[160];
Students *reg = initialize_reg(®b);
Student *sf3 = find_student(reg, "33333C", "ELEC-A1100");
Student *sf6 = find_student(reg, "11111T", "ELEC-A1111");
if (sf3 == NULL) {
release_memory(reg);
fail("[Task 3.4.b] find_student returned NULL for existing student ID %s.\n", "33333C");
}
if (strcmp(sf3->id, "33333C")) {
sprintf(buf, "[Task 3.4.b] find_student returned student with wrong id. Searched for: %s, returned: %s", "33333C", sf3->id);
release_memory(reg);
fail(buf);
}
if (sf6 == NULL) {
release_memory(reg);
fail("[Task 3.4.b] find_student returned NULL for existing student %s.\n", "11111T");
}
if (strcmp(sf6->id, "11111T")) {
sprintf(buf, "[Task 3.4.b] find_student returned student with wrong id. Searched for: %s, returned: %s", "11111T", sf6->id);
release_memory(reg);
fail(buf);
}
if (NULL != find_student(reg, "98989D", "ELEC-A1112")) {
release_memory(reg);
fail("[Task 3.4.b] find_student should return NULL for nonexistent students.\n");
}
/* fail_unless(NULL == find_student(reg, "98989D", "ELEC-A1100"), "[Task 17.2] find_student should return NULL for nonexistent students.");
fail_unless(NULL == find_student(reg, "33333C", "ELEC-A1112"), "[Task 17.2] find_student should return NULL for nonexistent students.");
fail_unless(NULL == find_student(reg, "33333C", "ELEC-A1111"), "[Task 17.2] find_student should return NULL for nonexistent students.");*/
release_memory(reg);
}
END_TEST
START_TEST(test_delete_student) {
char buf[160];
Students regb;
Students *reg = initialize_reg(®b);
if (reg->count != 6) {
sprintf(buf, "[Task 3.4.c] Wrong student count after adding 6 students, you have %d\n.",
reg->count);
release_memory(reg);
fail(buf);
}
if (!delete_student(reg, "11111T", "ELEC-A1111")) {
sprintf(buf, "[Task 3.4.c] delete_student() failed for existing student 11111T.\n");
release_memory(reg);
fail(buf);
}
if (reg->count != 5) {
sprintf(buf, "[Task 3.4.c] After deleting one student, student count should be 5, you had %d.\n",
reg->count);
release_memory(reg);
fail(buf);
}
if (!delete_student(reg, "33333C", "ELEC-A1100")) {
sprintf(buf, "[Task 3.4.c] delete_student() failed for existing student 33333C.\n");
release_memory(reg);
fail(buf);
}
//fail_unless(reg->count == 4, "[Task 17.3] Wrong student count after deletion.");
assert((reg->array + 2) != NULL);
if (strcmp((reg->array + 2)->id, "98765H")) {
sprintf(buf, "[Task 3.4.c] After deleting student 33333C, student %s should be in 3rd array position. You have %s.\n", "98765H", (reg->array + 2)->id);
release_memory(reg);
fail(buf);
}
if (delete_student(reg, "33330C", "ELEC-A1101")) {
release_memory(reg);
fail("[Task 3.4.c] delete_student() should have failed for nonexisting student 33330C.\n");
}
/*fail_unless(delete_student(reg, "00000A", "ELEC-A1111") == 0, "[Task 17.3] delete_student() should have failed for nonexisting student.");
fail_unless(delete_student(reg, "12121R", "ELEC-A1100") == 0, "[Task 17.3] delete_student() should have failed for nonexisting student.");
fail_unless(delete_student(reg, "00000A", "ELEC-A1100"), "[Task 17.3] delete_student() failed for existing student 00000A.");
fail_unless(delete_student(reg, "12345B", "ELEC-A1100"), "[Task 17.3] delete_student() failed for existing student 12345B.");
fail_unless(delete_student(reg, "98765H", "ELEC-A1100"), "[Task 17.3] delete_student() failed for existing student 98765H.");
fail_unless(delete_student(reg, "12121R", "ELEC-A1111"), "[Task 17.3] delete_student() failed for existing student 12121R.");
fail_unless(reg->count == 0, "[Task 17.3] Course register should be empty after deleting all students.");*/
release_memory(reg);
}
END_TEST
void pr_array(char *buf, float *arr, int n) {
char b[40];
sprintf(buf, "{");
while (n--) {
sprintf(b, "%.1f", *arr++);
if (n)
strcat(b, ", ");
strcat(buf, b);
}
strcat(buf, "}");
}
START_TEST(test_set_points) {
Students regb;
Students *reg = initialize_reg(®b);
char arrbuf[80];
char buf[160];
float p[4];
int i;
for (i = 0; i < 4; i++) {
p[i] = (float)(rand() % 10) / 2;
}
pr_array(arrbuf, p, 4);
if (!set_points(reg, "00000A", "ELEC-A1100", p, 4)) {
sprintf(buf, "[Task 3.4.d] set_points() returned 0 for student 00000A with array %s, but it should have succeeded.\n",
arrbuf);
release_memory(reg);
fail(buf);
}
Student *st = &(reg->array[0]);
if (st->points == NULL) {
sprintf(buf, "[Task 3.4.d] Points array not created for array %s (is still NULL).\n",
arrbuf);
release_memory(reg);
fail(buf);
}
char arr2[80];
pr_array(arr2, st->points, 4);
for (i = 0; i < 4; i++) {
if (st->points[i] != p[i]) {
sprintf(buf, "[Task 3.4.d] Point array differs. Should be %s. You have %s\n",
arrbuf, arr2);
release_memory(reg);
fail(buf);
}
}
/*float q[] = {2, 5, 1, 6, 12, 1};
fail_unless(set_points(reg, "00000A", "ELEC-A1100", q, 6) == 1, "[Task 17.4] set_points() did not return 1 on success or failed when it was not supposed to");
fail_unless(st->points != NULL, "[Task 17.4] Points array for student not created");
fail_unless(st->points[4] == 12, "[Task 17.4] Setting points failed on student");
fail_unless(set_points(reg, "00000A", "ELEC-A1111", p, 3) == 0, "[Task 17.4] set_points() did not return 0 on failure");*/
release_memory(reg);
}
END_TEST
int main(int argc, const char *argv[]) {
srand((unsigned) time(NULL));
Suite *s = suite_create("Test-3.4");
/* TODO: define tests */
tmc_register_test(s, test_create_student, "3.4.a");
tmc_register_test(s, test_find_student, "3.4.b");
tmc_register_test(s, test_delete_student, "3.4.c");
tmc_register_test(s, test_set_points, "3.4.d");
return tmc_run_tests(argc, argv, s);
}
答案 0 :(得分:1)
使用测试源代码,结合我的上一条评论,现在很容易看到问题:
您返回一个Student
指针,指向超出分配的内存。
到create_student
函数返回时,您已增加s->count
,因此它是“数组”s->array
中的条目数。但是大小数组索引是零基础的,那时最大索引是s->count - 1
。
答案 1 :(得分:1)
create_student()
函数以:
s->count++; /*Count is incremented by one as the first student is created*/
s->array = &s->array[0]; /*Now *array points at the first entry in the array*/
return &s->array[s->count]; /*Returns newly created pointer to the student*/
我对这段代码非常怀疑。
第一行很好;现在存储的学生比函数运行之前还多了一个。评论虽然破了。
第二行被破坏,它重新分配array
,一个新分配的动态指针。你永远不应该这样做。幸运的是,这是一个无操作,将其设置为自己。评论暗示你认为它做了一些事情,这让它变得可怕。
第三条线是超级恐怖的;它返回一个指向的指针,一旦超过数组,请记住count
已递增。
如果使用count == 0
调用,函数malloc()
为单个Student
的空间,则返回指向第一个数组中第二个元素的指针。糟糕!