C语言第六章Memory Management

Working With Memory

Memory Management

Understanding memory is an important aspect of C programming. When you declare a variable using a basic data type, C automatically allocates space for the variable in an area of memory called the stack(栈).
An int variable, for example, is typically(usually) allocated 4 bytes when declared. We know this by using the sizeof operator:

#include <stdio.h>

int main() {
    int x;
    printf("%d", sizeof(x)); /* output: 4 */
    return 0;
}

As another example, an array with a specified size is allocated contiguous blocks of memory with each block the size for one element:

#include <stdio.h>

int main() {
    int arr[10];
    printf("%d", sizeof(arr)); /* output: 40 */
    return 0;
}

So long as(只要) your program explicitly declares a basic data type or an array size, memory is automatically managed. However, you have probably already been wishing to implement a program where the array size is undecided until runtime.

Dynamic memory allocation is the process of allocating and freeing memory as needed. Now you can prompt(提示) at runtime for the number of array elements and then create an array with that many elements. Dynamic memory is managed with pointers that point to newly allocated blocks of memory in an area called the heap.

In addition to automatic memory management using the stack and dynamic memory allocation using the heap, there is statically managed data in main memory for variables that persist for the lifetime of the program(对于在程序的生命周期内持续存在的变量).

difference between char p[]=“solo”; and char*p=“solo”;
char p[]=“solo” creates an array of size strlen(“solo”) + 1 bytes and the string literal is copied into the string buffer. The string stored in p[] is modifiable. char *p = “solo” declares a pointer to a string in read-only memory. This string cannot be modified without crashing the program or causing undefined behavior. For this reason, you should declare p as const char * which will issue a compiler warning if you try to modify the string. You can still change what ‘p’ points to, you just cannot modify the string itself. The other difference is that sizeof() array p will yield the size of the memory block in bytes, while sizeof() pointer p will yield the size of the pointer.

char p [] is an character array in which array variable p can modify it’s value but char *p is a constant value which that is we can not modify it through index by index but can do by whole as a string.

#include <stdio.h>
#include<string.h>
int main() {
	char p[]="sololearn";
	*(p+1)='b';
	printf("%s",p/* OUTPUT : sblolearn */);
	return 0;
}
#include <stdio.h>
#include<string.h>
int main() {
	char *p="sololearn";
	*(p+1)='b';
	printf("%s",p/* OUTPUT : timeout error */);
	return 0; 
}

size of stack is so small about 1 mb, other hand size for heap in 64 bit window can up to 1gb, so using heap is more popular day by day

For your information, Both stack and heap variables stores in RAM Memory. Stack --> Memory Allocated during Compile Time Heap --> Memory Allocated During run Time. If it’s a large class or structure, you may want it on the heap instead.

Memory Management Functions

The stdlib.h library includes memory management functions.
The statement #include <stdlib.h> at the top of your program gives you access to the following:

malloc(bytes) Returns a pointer to a contiguous block of memory that is of size bytes.

calloc(num_items, item_size) Returns a pointer to a contiguous block of memory that has num_items items, each of size item_size bytes. Typically used for arrays, structures, and other derived data types. The allocated memory is initialized to 0.

realloc(ptr, bytes) Resizes the memory pointed to by ptr to size bytes. The newly allocated memory is not initialized.

free(ptr) Releases the block of memory pointed to by ptr.

When you no longer need a block of allocated memory, use the function free() to make the block available to be allocated again.

The malloc Function

The malloc() function allocates a specified number of contiguous bytes in memory.

#include <stdio.h>
#include <stdlib.h>
int main() {
  int *ptr;
  ptr = malloc(10*sizeof(*ptr));  /* a block of 10 ints */
  if (ptr != NULL) {
    *(ptr+2) = 50;  /* assign 50 to third int */
  }
  printf("3rd elem equals to %d", *(ptr + 2));
  return 0;
}

malloc returns a pointer to the allocated memory.
Notice that sizeof was applied to *ptr instead of int, making the code more robust should the *ptr declaration be changed to a different data type later.

used the statement if(ptr!=NULL) this is because sometimes memory allocation is not possible as arrays take up contiguous memory space which maynot be possible for computer with less RAM thereby returning NULL value to ptr.

the library function malloc is used to allocate a block of memory on the heap. The program accesses this block of memory via a pointer that malloc returns. When the memory is no longer needed, the pointer is passed to free which deallocates the memory so that it can be used for other purposes.

The allocated memory is contiguous and can be treated as an array. Instead of using brackets [ ] to refer to elements, pointer arithmetic is used to traverse the array. You are advised to use + to refer to array elements. Using ++ or += changes the address stored by the pointer.

If the allocation is unsuccessful, NULL is returned. Because of this, you should include code to check for a NULL pointer.

A simple two-dimensional array requires *(rows*columns)sizeof(datatype) bytes of memory.

The free Function

The free() function is a memory management function that is called to release memory. By freeing memory, you make more available for use later in your program.

#include <stdio.h>
#include <stdlib.h>

int main() {  
  int *ptr;
  ptr = malloc(10*sizeof(*ptr));  /* a block of 10 ints */
  if (ptr != NULL)
    *(ptr+2) = 50;  /* assign 50 to third int */
  printf("%d\n", *(ptr+2));  /* 50 */

  free(ptr);

  return 0;
}

calloc and realloc

The calloc Function

The calloc() function allocates memory based on the size of a specific item, such as a structure.
The program below uses calloc to allocate memory for a structure and malloc to allocate memory for the string within the structure:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  int num;
  char *info;
} record;

int main() {
  record *recs;
  int num_recs = 2;
  int k;
  char str[ ] = "This is information";

  recs = calloc(num_recs, sizeof(record));
  if (recs != NULL) {
    for (k = 0; k < num_recs; k++) {
      (recs+k)->num = k;
      (recs+k)->info = malloc(sizeof(str));
      strcpy((recs+k)->info, str);
    }
  }

  for (k = 0; k < num_recs; k++) {
    printf("%d\t%s\n", (recs+k)->num, (recs+k)->info);
  }

  return 0;
}

calloc allocates blocks of memory within a contiguous block of memory for an array of structure elements. You can navigate from one structure to the next with pointer arithmetic.

After allocating room for a structure, memory must be allocated for the string within the structure. Using a pointer for the info member allows any length string to be stored.

Dynamically allocated structures are the basis of linked lists and binary trees as well as(以及) other data structures.

The realloc Function

The realloc() function expands a current block to include additional memory.

#include <stdio.h>
#include <stdlib.h>

int main() {
  int *ptr;
  ptr = malloc(10*sizeof(*ptr));  /* a block of 10 ints */
  if (ptr != NULL) {
    *(ptr+2) = 50;  /* assign 50 to third int */
  }
  ptr = realloc(ptr, 100*sizeof(*ptr)); /* 100 ints */
  *(ptr+30) = 75;
  printf("%d %d", *(ptr+2), *(ptr+30));

  return 0;
}

realloc leaves the original content in memory and expands the block to allow for more storage.

sizeof pointer depends on the type of your machine if you run your program on a 32 bit machine the size of pointer will be 4 and if it is in 64 bit machine it will be 8.

malloc,realloc and calloc return a void pointer that we can not assign to an int* ,therefore we should explicity mention (int *) before function. but we should do this when we use c++ compiler, if we use C compiler there is no need for type casting.

Because realloc() returns NULL on failure, this sample code may leak memory if the reallocation fails. If NULL is assigned to the pointer variable you will lose access to the memory block you allocated (and you can no longer free it). int *temp = realloc(ptr, sizeof(*ptr) * 100); if (temp) { ptr = temp; } The solution is to assign the return value of realloc() to a temporary pointer variable as shown above. If the temporary is NULL (meaning the reallocation failed) then you still have access to the initial memory block through the first pointer and the memory can be freed. If the reallocation succeeds then you can assign the temporary pointer to the first pointer and avoid accidentally leaking memory.

See also https://en.cppreference.com/w/c/memory/realloc and https://stackoverflow.com/questions/46461236/does-realloc-free-the-existing-memory?rq=1 for more information about realloc.
Reference: http://www.cplusplus.com/reference/cstdlib/realloc/

Dynamic Strings & Arrays

Allocating Memory for Strings

When allocating memory for a string pointer, you may want to use string length rather than the sizeof operator for calculating bytes.
Consider the following program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
  char str20[20];
  char *str = NULL;

  strcpy(str20, "12345");
  printf("str20 size: %d\n", sizeof(str20));
  printf("str20 length: %d\n", strlen(str20));
  str = malloc(strlen(str20)+1); /* make room for \0 */
  strcpy(str, str20);
  printf("%s", str);

  return 0;
}

This approach is better memory management because you aren’t allocating more space than is needed to a pointer. When using strlen to determine the number of bytes needed for a string, be sure to include one extra byte for the NULL character ‘\0’.
A char is always one byte, so there is no need to multiply the memory requirements by sizeof(char).

Dynamic Arrays

Many algorithms implement a dynamic array because this allows the number of elements to grow as needed.
Because elements are not allocated all at once, dynamic arrays typically use a structure to keep track of current array size, current capacity, and a pointer to the elements, as in the following program.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  int *elements;
  int size;
  int cap;
} dyn_array;

int main() {
  dyn_array arr;
  int i;

  /* initialize array */
  arr.size = 0;
  arr.elements = calloc(1, sizeof(*arr.elements));
  arr.cap = 1;  /* room for 1 element */

  /* expand by 5 more elements */
  arr.elements = realloc(arr.elements, (5 + arr.cap)*sizeof(*arr.elements));
  if (arr.elements != NULL)
    arr.cap += 5; /* increase capacity */

  /* add an element and increase size */  
  if (arr.size < arr.cap) {
    arr.elements[arr.size] = 50; /* add element to array */
    arr.size++;
  }
  else
    printf("Need to expand array.");

  /* display array elements */
  for (i = 0; i < arr.cap; i++)
    printf("Element %d: %d\n", i, arr.elements[ i ]);

  return 0;
}

To expand by more elements:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  int *elements;
  int size;
  int cap;
} dyn_array;

int main() {
  dyn_array arr;
  int i;

  /* initialize array */
  arr.size = 0;
  arr.elements = calloc(1, sizeof(*arr.elements));
  arr.cap = 1;  /* room for 1 element */

  /* expand by 5 more elements */
  arr.elements = realloc(arr.elements, (5 + arr.cap)*sizeof(*arr.elements));
  if (arr.elements != NULL)
    arr.cap += 5; /* increase capacity */

  /* add an element and increase size */  
  if (arr.size < arr.cap) {
    arr.elements[arr.size] = 50; /* add element to array */
    arr.size++;
  }
  else
    printf("Need to expand array.");

  /* display array elements */
  for (i = 0; i < arr.cap; i++)
    printf("Element %d: %d\n", i, arr.elements[ i ]);

  return 0;
}

Adding an element to the array increases its size:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  int *elements;
  int size;
  int cap;
} dyn_array;

int main() {
  dyn_array arr;
  int i;

  /* initialize array */
  arr.size = 0;
  arr.elements = calloc(1, sizeof(*arr.elements));
  arr.cap = 1;  /* room for 1 element */

  /* expand by 5 more elements */
  arr.elements = realloc(arr.elements, (5 + arr.cap)*sizeof(*arr.elements));
  if (arr.elements != NULL)
    arr.cap += 5; /* increase capacity */

  /* add an element and increase size */  
  if (arr.size < arr.cap) {
    arr.elements[arr.size] = 50; /* add element to array */
    arr.size++;
  }
  else
    printf("Need to expand array.");

  /* display array elements */
  for (i = 0; i < arr.cap; i++)
    printf("Element %d: %d\n", i, arr.elements[ i ]);

  return 0;
}

arr.element is a pointer, not an integer. I tested it:

printf("Size of elements: %d\n", (int) sizeof(arr.elements));
printf("Size of *elements: %d\n", (int) sizeof(*(arr.elements)));

And got: Size of elements: 8 Size of *elements: 4

The entire program is written in main() for demonstration purposes. To properly implement a dynamic array, sub-tasks should be broken down into functions such as init_array(), increase_array(), add_element(), and display_array(). The error checking was also skipped to keep the demo short.

Here’s the code corrected and splitted in functions.

/* Dynamic Array 

Author: Marco Bongini */

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  int *elements;
  int size;
  int cap;
} dyn_array;

void init_array(dyn_array* const);
void increase_array(dyn_array* const, const int);
void add_element(dyn_array* const, const int);
void display_array(const dyn_array* const);

int main() {
  dyn_array arr;

  init_array(&arr);
  increase_array(&arr, 5);
  add_element(&arr, 50);
  display_array(&arr);
 
  return 0;
}

/* initialize array */
void init_array(dyn_array *a)
{
  a->size = 0;
  a->elements = calloc(1, sizeof(*a->elements));
  a->cap = 1;  /* room for 1 element */
}

/* expand by n more elements */
void increase_array(dyn_array *a, const int n)
{
  a->elements = realloc(a->elements, (n + a->cap)*sizeof(*a->elements));
  if (a->elements != NULL)
        a->cap += n; /* increase capacity */
}

/* add an element and increase size */  
void add_element(dyn_array *a, const int element)
{
  if (a->size < a->cap) {
        a->elements[a->size] = element; /* add element to array */
        a->size++;
  }
  else
    puts("Need to expand array");
}

/* display array elements */
void display_array(const dyn_array *const a)
{
    int i;
    for (i = 0; i < a->cap; i++)
        printf("Element %d: %d\n", i, a->elements[ i ]);
}

to allocate memory for an int array and then expand the memory for more elements.

int *arr = malloc(4 * sizeof(int));
arr = realloc(arr, 8 * sizeof(int));

You shouldn’t do this. realloc could fail, and if it fails you assign 0 to arr, thus you lose the pointer to that memory. You should declare another variable holding the next pointer and if it succeeds assign the new pointer to the old one.

int* arr = malloc(4 * sizeof(int));
int* new_array = realloc(arr, 8 * sizeof(int));
if(new_array) {
	arr = new_array;
}
else {
/* Handle fail here */
}

Or you can do:

if(realloc(arr, 8 * sizeof(*arr)) != NULL) {
	arr = realloc(arr, 8 * sizeof(*arr));
	printf("Can expand!\n");
}
free(arr);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值