Функция malloc()
выделяет как минимум объем запрашиваемой памяти, хотя может быть и больше. Однако объем памяти, который предоставляет malloc()
, на самом деле не является центральной проблемой.
Язык программирования C предназначен для скорости и эффективности, что означает, что многие проверки, которые не выполняются другими языками, не выполняются. Таким образом, вы можете написать программу, которая делает что-то неправильно, и она по-прежнему будет работать при определенных обстоятельствах и в других обстоятельствах не работает.
В C указатель - это адрес ячейки памяти. C не проверяет, является ли адрес действительным адресом или нет. C не проверяет, что объем памяти, который вы пытаетесь использовать, - это правильный объем памяти.
Итак, это аннотированная версия вашей программы.
#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main() {
char *description; // create a variable that will contain the address of a character
clrscr();
// allocate an array of characters. the number of characters is 2 so this
// will allocate a minimum size memory area that will hold 2 characters.
// malloc() may round up the size or it may not but all you can count on
// is 2 characters in size.
description = malloc(2*sizeof(char));
// copy a constant string to the memory location pointed to by the variable
// description. the strcpy() function does not check on the limits of the
// memory. after the malloc() is done there is no size information available
// to strcpy() or any other of the C runtime library functions in the C Standard Library
// this is copying 11 characters plus the end of string for a total of 12 characters
// so to be correct the memory area pointed to by description should be at least
// 12 characters however we know from the malloc() above it is guaranteed to
// be only 2 characters so we are going past the memory area with this strcpy().
strcpy(description,"Hello there!");
// however we get lucky and it works anyway.
printf("%s",description);
// tell the memory allocation library that you are done with the memory and it
// can be used for something else now. the pointer will probably still be good
// for a while because the memory allocation, which gets its memory from the
// operating system, does not normally give any freed memory back to the OS.
// instead it normally just keeps it until the application terminates.
// as long as this freed memory is not used for something else, more than
// likely whatever you put there will remain there as is. however the moment
// the memory is given to something else, the values will change.
free(description);
printf("%s",description);
getch();
}
Вы можете получить представление о том, что происходит, если вы пытаетесь следующий пример программы, которая является модификацией твоей, что вместо использования malloc()
использует переменные в стеке.
#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main() {
char array1[12] = { 0 };
char array2[2] = { 0 };
char array3[12] = { 0 };
char *description;
printf("Before\n array1 %s\n", array1);
printf(" array2 %s\n", array2);
printf(" array3 %s\n", array3);
description = &array2[0];
strcpy(description, "Hello there!");
printf("description %s\n", description);
printf("\nAfter\n array1 %s\n", array1);
printf(" array2 %s\n", array2);
printf(" array3 %s\n", array3);
}
с помощью Visual Studio 2013 и работает в режиме отладки, я получаю предупреждение о памяти переписывается, как завершает приложение. Когда я делаю сборку и запускаю ее, ошибок нет, и я получаю следующий вывод. Как вы можете видеть, функция strcpy()
просто скопировала символы, переписывающие смежную память. Похоже, что компилятор Visual Studio 2013 выравнивает память на двойной границе слова, так что в соседней области памяти видны только последние несколько символов строки. Visual Studio заполнила переменную array2[]
так, чтобы следующая переменная была разделена на двойную границу слова.
Before
array1
array2
array3
description Hello there!
After
array1
array2 Hello there!
array3 ere!
Если мы изменяем выше программы к следующему:
#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main() {
char array1[12] = { 0 };
char array2[2] = { 0 };
char array3[12] = { 0 };
char *description;
int *values;
printf("Before\n array1 %s\n", array1);
printf(" array2 %s\n", array2);
printf(" array3 %s\n", array3);
description = &array2[0];
strcpy(description, "Hello there!");
printf("description %s\n", description);
printf("\nAfter\n array1 %s\n", array1);
printf(" array2 %s\n", array2);
printf(" array3 %s\n", array3);
description = malloc(8 * sizeof(char));
strcpy(description, "this");
printf("\n\nMalloc\n first description %p %s\n", description, description);
free(description);
values = malloc(1 * sizeof(int));
*values = 0;
printf(" pointer %p and value %d\n", values, *values);
printf(" second description %p %s\n", description, description);
}
Тогда мы получаем следующий вывод. В этом случае нам повезло и нам дали ту же область памяти на malloc()
int
, поэтому, когда мы изменили int
, мы также изменили область, на которую указывает description
, потому что после ее освобождения malloc()
повторно использовал область для следующего выделения.
Before
array1
array2
array3
description Hello there!
After
array1
array2 Hello there!
array3 ere!
Malloc
first description 00944B28 this
pointer 00944B28 and value 0
second description 00944B28
Ресурс собственности Принцип
Этот пример демонстрирует два правила при использовании malloc()
и free()
.
Выделите malloc()
сумму, в которой вы нуждаетесь, и не превышайте объем запрашиваемой памяти. Если вам нужно больше, посмотрите на функцию realloc()
.
Как только вы освободите область памяти, используя free()
, никогда не используйте это значение указателя снова. После освобождения вы больше не владеете областью памяти.
Когда вы используете malloc()
, вы становитесь владельцем памяти, но только ту память, которую вы запросили. Когда вы используете free()
, вы отказываетесь от владения памятью, и вы больше не должны ее использовать, потому что вы больше не владеете ею.
Вы стреляете в ногу. Это традиционное и дизайнерское решение, которое C-язык позволяет вам стрелять в ногу. – user3528438
Ну, системный язык, такой как C, должен позволять вам стрелять в ногу. Решение состоит в том, чтобы не делать этого. – DeiDei
Чтобы сделать приведенные выше комментарии немного более откровенными, C требует, чтобы вы знали, что, черт возьми, это ваши – StoryTeller