”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 了解 C 中的内存管理、指针和函数指针

了解 C 中的内存管理、指针和函数指针

发布于2024-07-30
浏览:158

Understanding Memory Management, Pointers, and Function Pointers in C

In C programming, efficient memory management and the use of pointers are crucial for creating robust and high-performance applications. This guide provides a comprehensive overview of different types of memory, pointers, references, dynamic memory allocation, and function pointers, complete with examples to help you master these concepts. Whether you are new to C or looking to deepen your understanding, this guide covers essential topics to enhance your coding skills.

Different types of memory.

There is 5 different types of memory that you will encounter when programming in C.

1. Text segment

Your compiled code is stored here. The machine code instructions for your program. The text segment is read only so you cannot change any data but you can access it. Further down we’ll talk about function pointers. Those pointers point to a function in this segment.

2. Initialized Data Segment

Global and static variables are stored here, with specific values before the program runs and stay accessible throughout the program.

The difference between static and global variables is the scope, static variables is accessible in the function or block, it is defined but global variables can be accessed from anywhere in your program.

Normal variables are removed after a function is done executing while static variables remain.

3. Unitialized Data Segment (BSS)

Basically the same as initialized data segment but consists of variables without a specific value assigned to it. They have a value of 0 by default.

4. Heap

Here you have dynamic memory that you as a programmer can manage at run time. You can allocate memory and free memory with functions like malloc, calloc, realloc and free.

5. Stack

You are probably somewhat familiar with the stack. The stack memory manages function executions by storing local variables, arguments, and return values. The memory in the stack is removed after the function is done executing.

Data types and amount of storage.

In C you have different data types and the most common is int, float, double and char. I will not talk much about data types but the important thing is to know how many bytes a specific data type have in memory. Here is a list, keep it in mind.

Int: 4 bytes,
Float: 4 bytes,
Double: 8 bytes,
Char: 1 byte.

You can check the size of a data type with the sizeof() method.

Pointers and References.

When you assign a variable like;

int number = 5; 

The system will store the value of 5 in memory. But where in memory?

In memory there is actually addresses, and that’s how you can keep track of the values you have stored.

A reference is a variable’s address. Quite cool, right?

To access the reference of a variable use & followed by the variable name.

To print the reference to the console, we use the p format specifier.

int number = 5;

printf(“%d”, number);
// prints out 5 

printf(“%p”, &number);
// prints out the ref: 0x7ffd8379a74c

You probably get another address printed out.

Now to keep track of that reference use a pointer to store the reference.

Create a pointer variable by using *.

int number; 

int* pointer = &number;

printf(“%p”, pointer);
// 0x7ffd8379a74c

To get the value from a pointer you can use dereference. To derefernce a value you use * before the pointer. So * is used both to create a pointer and dereference it, but in different contexts.

int number = 5; // create variable.

int* pointer = &number //store the reference in the pointer

printf(“%p”, pointer); // print the reference
// 0x7ffd8379a74c

printf(“%d”, *pointer); // dereference the value. 
// 5

This is powerful because with pointers you can pass values by reference and not copy values. This is memory efficient and performant.

When you pass values to a function as arguments in a high level language you copy the values but in C you can send a reference and manipulate the value directly in memory.

#include 

void flipValues(int *a, int *b) { // receives two pointers of type int
  int temp = *a; // dereference pointer a and store it’s value in temp
  *a = *b; // dereference pointer b and store in pointer a
  *b = temp; // dereference b and change value to the value of temp
}

int main(void) {
  int a = 20;
  int b = 10;
  flipValues(&a, &b); // pass the references of a and b
  printf("a is now %d and b is %d", a, b);
  // a is now 10 and b is 20
  return 0;
}

A pointer can be declared as int* name; or int *name; both styles are correct and interchangeable.

Allocate and Deallocate dynamic memory.

When you declare a variable like int num = 5; inside a function including the main function, that variable is stored in the stack, and when the function is done executing the variable is removed.

But now we will allocate memory dynamically in the heap. Then we have full control over how much memory we need, and it will persist until we deallocate it.

Malloc and Calloc.

We can use the functions malloc or calloc to allocate memory.

Malloc takes one parameter representing the size of memory to allocate in bytes.

Calloc takes two parameters, the amount of items and how much memory in bytes each item occupies.

malloc(size)
calloc(amount, size)

calloc initializes all allocated memory to 0, while malloc leaves the memory uninitialized, making malloc slightly more efficient.

You can use sizeof to indicate how much memory you need. In the example we use malloc to allocate space for 4 integers.

int* data; 
data = malloc(sizeof(int) * 4);

Here is a visualization:

Pointer->[],[],[],[] [],[],[],[] [],[],[],[] [],[],[],[]

  1. Each bracket is a byte so here we see 16 bytes in memory.
  2. The malloc function allocates 4 x 4 bytes in memory, resulting in 16 bytes.
  3. int* data is a pointer of type int, so it points to the 4 first bytes in memory.

Therefore, pointer 1 moves the pointer one step to the right, referring to the next integer in memory, which is 4 bytes away.

int* data; 
  data = malloc(sizeof(int) * 4);

  *data = 10; // change first value to 10.
  *(data   1) = 20; // change second value to 20.
  *(data   2) = 30;
  *(data   3) = 40;

  for(int i = 0; i 



This is how an array work!

When you declare an array, the array name is a pointer to its first element.

int numbers[] = { 10, 20, 30, 40 };
  printf("%p\n", &numbers); 
  printf("%p", &numbers[0]);
  // 0x7ffe91c73c80
  // 0x7ffe91c73c80

As shown, the address of the array name is the same as the address of its first element.

Realloc

Sometimes you want to reallocate memory. A normal use case is when you need more memory then you initially allocated with malloc or calloc.

The realloc function takes 2 parameters. A pointer to where your data currently is located, and the size of memory you need.

int* pointer2 = realloc(*pointer1, size);

The realloc function will first check if the size of data can be stored at the current address and otherwise it will find another address to store it.

It’s unlikely but if there is not enough memory to reallocate the function will return null.

int *ptr1, *ptr2;

// Allocate memory
ptr1 = malloc(4);

// Attempt to resize the memory
ptr2 = realloc(ptr1, 8);

// Check whether realloc is able to resize the memory or not
if (ptr2 == NULL) {
  // If reallocation fails
  printf("Failed. Unable to resize memory");
} else {
  // If reallocation is sucessful
  printf("Success. 8 bytes reallocated at address %p \n", ptr2);
  ptr1 = ptr2;  // Update ptr1 to point to the newly allocated memory
}

Free memory

After you have allocated memory and don’t use it anymore. Deallocate it with the free() function with a pointer to the data to be freed.

After that, it is considered good practice to set the pointer to null so that the address is no longer referenced so that you don’t accidentally use that pointer again.

int *ptr;
ptr = malloc(sizeof(*ptr));

free(ptr);
ptr = NULL;

Remember that the same memory is used in your whole program and other programs running on the computer.

If you don’t free the memory it’s called data leak and you occupy memory for nothing. And if you accidentally change a pointer you have freed you can delete data from another part of your program or another program.

Create a Vector (dynamic array).

You can use your current knowledge to create a dynamic array.

As you may know, you can use structs to group data and are powerful to create data structures.

#include 
#include 

struct List {
  int *data; // Pointer to the list data
  int elements; // Number  of elements in the list 
  int capacity; // How much memmory the list can hold
};

void append(struct List *myList, int item);
void print(struct List *myList);
void free_list(struct List *myList);

int main() {
    struct List list;
    // initialize the list with no elements and capazity of 5 elements
    list.elements = 0;
    list.capacity = 5;
    list.data = malloc(list.capacity * sizeof(int));
    // Error handeling for allocation data
    if (list.data == NULL) {
    printf("Memory allocation failed");
    return 1; // Exit the program with an error code
    }
    // append 10 elements
    for(int i = 0; i elements == list->capacity) {
    list->capacity *= 2;
    list->data = realloc( list->data, list->capacity * sizeof(int) );
    }
    // Add the item to the end of the list
    list->data[list->elements] = item;
    list->elements  ;
}

void print(struct List *list) {
    for (int i = 0; i elements; i  ) {
    printf("%d ", list->data[i]);
    } 
}

void free_list(struct List *list) {
    free(list->data);
    list->data = NULL;
}

Static data

Global variables.

When declaring a variable above the main function they are stored in the data segment and are allocated in memory before the program starts and persists throughout the program and is accessible from all functions and blocks.

#include 
int globalVar = 10; // Stored in initilized data segment
int unitilizedGlobalVar; // Stored in uninitialized data segment

int main() {
    printf("global variable %d\n", globalVar);            printf("global uninitilized variable %d", unitilizedGlobalVar);
    // global variable 10
    // global uninitilized variable 0
    return 0;
}

Static variables

They work the same as global ones but are defined in a certain block but they are also allocated before the program starts and persist throughout the program. This is a good way of keeping the state of a variable. Use the keyword static when declaring the variable.

Here is a silly example where you have a function keeping the state of how many fruits you find in a garden using a static variable.

#include 

void countingFruits(int n) {
    // the variable is static and will remain through function calls and you can store the state of the variable
    static int totalFruits;
    totalFruits  = n;
    printf( "Total fruits: %d\n", totalFruits);
}

void pickingApples(int garden[], int size) {
    // search for apples
    int totalApples = 0;
    for(int i = 0; i 



Understanding Function Pointers in C.

Function pointers are a powerful feature in C that allow you to store the address of a function and call that function through the pointer. They are particularly useful for implementing callback functions and passing functions as arguments to other functions.

Function pointers reference functions in the text segment of memory. The text segment is where the compiled machine code of your program is stored.

Defining a Function Pointer

You define a function pointer by specifying the return type, followed by an asterisk * and the name of the pointer in parentheses, and finally the parameter types. This declaration specifies the signature of the function the pointer can point to.

int(*funcPointer)(int, int)

Using a Function Pointer

To use a function pointer, you assign it the address of a function with a matching signature and then call the function through the pointer.

You can call a function of the same signature from the function pointer

#include 

void greet() {
    printf("Hello!\n");
}

int main() {
    // Declare a function pointer and initialize it to point to the 'greet' function
    void (*funcPtr)();
    funcPtr = greet;

    // Call the function using the function pointer
    funcPtr();

    return 0;
}

Passing Function Pointers as Parameters

Function pointers can be passed as parameters to other functions, allowing for flexible and reusable code. This is commonly used for callbacks.

#include 

// Callback 1
void add(int a, int b) {
    int sum  = a   b;
    printf("%d   %d = %d\n", a, b, sum);
}

// Callback 2
void multiply(int a, int b) {
    int sum  = a * b;
    printf("%d x %d = %d\n", a, b, sum);
}

// Math function recieving a callback
void math(int a, int b, void (*callback)(int, int)) {
    // Call the callback function
    callback(a, b);
}

int main() {
    int a = 2;
    int b = 3;

    // Call math with add callback
    math(a, b, add);

    // Call math with multiply callback
    math(a, b, multiply);

    return 0;
}

Explanation of the Callback Example

Callback Functions: Define two functions, add and multiply, that will be used as callbacks. Each function takes two integers as parameters and prints the result of their respective operations.

Math Function: Define a function math that takes two integers and a function pointer (callback) as parameters. This function calls the callback function with the provided integers.

Main Function: In the main function, call math with different callback functions (add and multiply) to demonstrate how different operations can be performed using the same math function.

The output of the program is:

2   3 = 5
2 x 3 = 6

Thanks for reading and happy coding!

版本声明 本文转载于:https://dev.to/emanuelgustafzon/understanding-memory-management-pointers-and-function-pointers-in-c-8ld如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何限制动态大小的父元素中元素的滚动范围?
    如何限制动态大小的父元素中元素的滚动范围?
    在交互式接口中实现垂直滚动元素的CSS高度限制问题:考虑一个布局,其中我们具有与用户垂直滚动一起移动的可滚动地图div,同时与固定的固定sidebar保持一致。但是,地图的滚动无限期扩展,超过了视口的高度,阻止用户访问页面页脚。$("#map").css({ marginT...
    编程 发布于2025-04-16
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php PHP陷入困境。使用simplexmlelement :: attributes()函数提供了简单的解决方案。此函数可访问对XML元素作为关联数组的属性: - > attributes()为$ attributeName => $ attributeValue){ echo ...
    编程 发布于2025-04-16
  • 在PHP中如何高效检测空数组?
    在PHP中如何高效检测空数组?
    在PHP 中检查一个空数组可以通过各种方法在PHP中确定一个空数组。如果需要验证任何数组元素的存在,则PHP的松散键入允许对数组本身进行直接评估:一种更严格的方法涉及使用count()函数: if(count(count($ playerList)=== 0){ //列表为空。 } 对...
    编程 发布于2025-04-16
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,可以更快地搜索这些前缀。了解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-04-16
  • Go web应用何时关闭数据库连接?
    Go web应用何时关闭数据库连接?
    在GO Web Applications中管理数据库连接很少,考虑以下简化的web应用程序代码:出现的问题:何时应在DB连接上调用Close()方法?,该特定方案将自动关闭程序时,该程序将在EXITS EXITS EXITS出现时自动关闭。但是,其他考虑因素可能保证手动处理。选项1:隐式关闭终止数...
    编程 发布于2025-04-16
  • 为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    在CSS 问题:不正确的代码: 全球范围将所有余量重置为零,如提供的代码所建议的,可能会导致意外的副作用。解决特定的保证金问题是更建议的。 例如,在提供的示例中,将以下代码添加到CSS中,将解决余量问题: body H1 { 保证金顶:-40px; } 此方法更精确,避免了由全局保证金重置引...
    编程 发布于2025-04-16
  • 如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    postgresql:为每个唯一标识符在postgresql中提取最后一行,您可能需要遇到与数据集合中每个不同标识的信息相关的信息。考虑以下数据:[ 1 2014-02-01 kjkj 在数据集中的每个唯一ID中检索最后一行的信息,您可以在操作员上使用Postgres的有效效率: id dat...
    编程 发布于2025-04-16
  • HTML格式标签
    HTML格式标签
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    编程 发布于2025-04-16
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    在node-mysql node-mysql文档最初出于安全原因最初禁用多个语句支持,因为它可能导致SQL注入攻击。要启用此功能,您需要在创建连接时将倍增设置设置为true: var connection = mysql.createconnection({{multipleStatement:...
    编程 发布于2025-04-16
  • 如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    为有效的slug生成首先,该函数用指定的分隔符替换所有非字母或数字字符。此步骤可确保slug遵守URL惯例。随后,它采用ICONV函数将文本简化为us-ascii兼容格式,从而允许更广泛的字符集合兼容性。接下来,该函数使用正则表达式删除了不需要的字符,例如特殊字符和空格。此步骤可确保slug仅包含...
    编程 发布于2025-04-16
  • 如何使用“ JSON”软件包解析JSON阵列?
    如何使用“ JSON”软件包解析JSON阵列?
    parsing JSON与JSON软件包 QUALDALS:考虑以下go代码:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    编程 发布于2025-04-16
  • 在Python中如何创建动态变量?
    在Python中如何创建动态变量?
    在Python 中,动态创建变量的功能可以是一种强大的工具,尤其是在使用复杂的数据结构或算法时,Dynamic Variable Creation的动态变量创建。 Python提供了几种创造性的方法来实现这一目标。利用dictionaries 一种有效的方法是利用字典。字典允许您动态创建密钥并分...
    编程 发布于2025-04-16
  • 为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    查询模式实现缺失:解决“无法找到”错误在Silverlight应用程序中,尝试使用LINQ建立LINQ连接以错误而实现的数据库”,无法找到查询模式的实现。”当省略LINQ名称空间或查询类型缺少IEnumerable 实现时,通常会发生此错误。 解决问题来验证该类型的质量是至关重要的。在此特定实例中...
    编程 发布于2025-04-16
  • 如何将多种用户类型(学生,老师和管理员)重定向到Firebase应用中的各自活动?
    如何将多种用户类型(学生,老师和管理员)重定向到Firebase应用中的各自活动?
    Red: How to Redirect Multiple User Types to Respective ActivitiesUnderstanding the ProblemIn a Firebase-based voting app with three distinct user type...
    编程 发布于2025-04-16
  • 哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    在Python Matplotlib's path.contains_points FunctionMatplotlib's path.contains_points function employs a path object to represent the polygon.它...
    编程 发布于2025-04-16

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3