Gcc generates static libraries and dynamic libraries as well as the generation and use of static .a and .so library files. And review concepts such as global constants, global variables, local variables, static variables, heaps, and stacks, and program and verify them in Ubuntu (x86) systems and STM32 (Keil) respectively.

1.gcc generates static libraries and dynamic libraries

1.1 What are static libraries and dynamic libraries

Static libraries and dynamic libraries are library files stored on disk and contain functions, variables and other executable objects that can be called by programs. They are all the results of compilation and linking. However, there are several differences between them:

1. Static libraries are linked by copying the code of the library file into the executable file when compiling and linking, while dynamic libraries are linked by loading and linking when the program is running.

2. The static library has been determined when compiling and linking. Even if the library file itself is updated, the linker still needs to be recompiled. Dynamic libraries can be updated without recompiling the linker.

3. Static libraries increase the size of the executable file because the code of the library file is copied into the executable file. Dynamic libraries do not directly increase the size of the executable file because they are only loaded at runtime.

4. Static libraries are faster to call than dynamic libraries because they have been compiled and linked into the executable file and do not need to be loaded at runtime. However, static libraries increase the size of the executable file. The calling speed of dynamic libraries is relatively slow because they need to be loaded at runtime, but the use of dynamic libraries can reduce the size of the executable file and save storage space.

Generally speaking, static libraries are suitable for small programs because they can reduce the loading speed at runtime. The dynamic library is suitable for larger programs because it can reduce the size of the executable file and save storage space.

1.2hello instance application

1. File creation

Use gedit to create the file: hello.h hello.c main,c

ps: In order to facilitate the management of file storage locations, create a file directory before creating the file.

mkdir test1
cd test1 ##This is to move to the creation directory

Create a file

hello.h

#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif//HELLO_H

hello.c

#include<stdio.h>
void hello(const char *name)
{
printf("Hello %s\
",name);
}

main.c

#include"hello.h"
int main()
{
hello("everyone");
return 0;
}

(3). Compile with gcc and get the .o file

gcc -c hello.c

Use ls to view the generated files after generation

2. Generate static library

1) Create a static library
Tools for creating static libraries: ar
Static library file naming convention: with lib as the prefix, it is an .a file

ar -crv libmyhello.a hello.o

(2) Use static libraries in programs

①gcc -o hello main.c -L. -lmyhello

Note: For custom static libraries, main.c can also be placed between -L. and -lmyhello, otherwise myhello is not defined.
-L.: Indicates that the connected library is in the current directory

gcc main.c libmyhello.a -o hello

First generate main.o gcc -c main.c
Generate executable file gcc -o hello main.c libmyhello.a

(3) Verify the characteristics of the static library
After deleting the static library and running the executable file, it is found that the program still runs normally, indicating that the static library has no connection with program execution. At the same time, it also shows that the static library is linked into the code when the program is compiled.

./hello

3. Use of dynamic libraries

(1). Create dynamic library
Tool for creating dynamic libraries: gcc
Dynamic library file naming convention: with lib as the prefix, it is a .so file
gcc -shared -fPIC -o libmyhello.so hello.o

shared: indicates specifying to generate a dynamic link library and cannot be omitted
-fPIC: indicates compilation into position-independent code and cannot be omitted

-o in the command must not be omitted

(2). Execute dynamic library in program
gcc -o hello main.c -L. -lmyhello
gcc main.c libmyhello.so -o hello

If you run the executable file hello again, an error will appear.

Solution to the problem: Copy libmyhello.so to the directory /usr/lib. Because when running, the library file is found in /usr/lib.

mv libmyhello.so /usr/lib

4. Comparison between static libraries and dynamic libraries

gcc compiles and obtains the .o file gcc -c hello.c
Create a static library ar -crv libmyhello.a hello.o
Create a dynamic library gcc -shared -fPIC -o libmyhello.so hello.o
Use the library to generate an executable file gcc -o hello main.c -L. -lmyhello
Execute the executable file ./hello

When executing the executable file, an error will be reported. It can be seen that when the static library and the dynamic library exist at the same time, the program will give priority to the dynamic library.

1.3: Example 2 using library

Create file A1.c A2.c A.h test.c

A1.c

#include<stdio.h>
void print1(int arg)
{
printf("A1 print arg:%d\
",arg);
}

A2.c

#include<stdio.h>
void print2(char *arg)
{
printf("A2 printf arg:%s\
",arg);
}

A. h

#ifndef A_H
#define A_H
void print1(int);
void print2(char *);
#endif

test.c

#include<stdio.h>
#include"A.h"
int main()
{
print1(1);
print2("test");
exit(0);
}

2. Summary

Through the practice process of using gcc to generate static libraries and dynamic libraries, you can basically generate static libraries and dynamic libraries proficiently. In the comparison of the two libraries, the difference between the two can be clearly seen. Although some minor problems were encountered during the process, they were quickly resolved. As long as you slowly practice it a few times, you will soon be able to master it. The executable file is obtained through compilation and linking. Use tools to compile the source code to obtain the .o file. The next step is to link the .o file to obtain the executable file.

3. Write a C program

Review concepts such as global constants, global variables, local variables, static variables, heaps, and stacks, and perform programming and verification in Ubuntu (x86) system and STM32 (Keil) (STM32 printf information to the host computer serial port assistant through the serial port). 1) Summarize the allocation addresses of heap, stack, global, local and other variables in C programs under Ubuntu and stm32, and conduct comparative analysis; 2) Deepen the understanding of the memory address mapping of ARM Cortex-M/stm32F10x.

Write an ubantu c program.

#include <stdio.h>

// global constants
const int global_constant = 10;

//global variables
int global_variable = 20;

void function()
{
    // local variables
    int local_variable = 30;

    // static variables
    static int static_variable = 40;
}

int main()
{
    function();
    return 0;
}

Compile and run this program to obtain the address information. Compile using gcc compiler on Ubuntu and execute the resulting executable file

gcc memory_allocation.c -o memory_allocation
./memory_allocation

Add code to your program to print the address of the variable and output the results to a terminal or serial port assistant. For example, add the following code in the function() function:

void function()
{
    // local variables
    int local_variable = 30;

    //Print the address of local variables
    printf("Local variable address: %p\
", & amp;local_variable);

    // static variables
    static int static_variable = 40;

    //Print the address of the static variable
    printf("Static variable address: %p\
", & amp;static_variable);
}

On STM32, you can use HAL library functions and serial port printing to output address information. For example:

#include "stm32f10x.h"
#include "stdio.h"

//global variables
int global_variable = 20;

void function()
{
    // local variables
    int local_variable = 30;

    //Print the address of local variables
    printf("Local variable address: %p\
", & amp;local_variable);

    // static variables
    static int static_variable = 40;
    
    //Print the address of the static variable
    printf("Static variable address: %p\
", & amp;static_variable);
}

int main(void)
{
    //Initialize serial port
    USART_Init(/* serial port parameters */);
    
    //Open serial port
    USART_Cmd(USART1, ENABLE);

    // Output the address of global variables
    printf("Global variable address: %p\
", & amp;global_variable);

    function();

    while (1)
    {
        // main loop
    }
}

Use Keil to compile and burn the program on STM32. Monitor the output through the serial port assistant to view the address of the variable.

Four: Summary

By comparing the allocation addresses of heap, stack, global and local variables in C programs under Ubuntu and STM32, as well as the memory address mapping of ARM Cortex-M/stm32F10x, you can deepen your understanding of these concepts and memory address mapping.

Generally speaking, variables within the program are allocated on the stack from high addresses to low addresses on the stack, and from low addresses to high addresses on the heap.

Under Ubuntu, the address storage in the stack area grows upward, and the address storage in the heap area also grows upward;
Under STM32, the address storage in the stack area grows downward, but the address storage in the heap area grows upward.

But why does the address value of the stack area also increase under Ubuntu?
:

In the Ubuntu environment, the address value of the stack area will also grow because the operating system uses segmentation or paging technology to implement memory management.

Segmentation is a technique that divides memory into different segments, each with different characteristics and purposes. Each process has its own code segment, data segment and stack segment. When a process is created, the operating system allocates an independent memory space for each segment, and the address space of each segment is independent.

When a function is called, a new stack frame is created in the stack segment to store the function’s local variables, parameters, and return address. When the function returns, its corresponding stack frame will be destroyed, and the stack pointer will point to the next available stack frame.

Since each process has its own memory space, the stack spaces of different processes are independent. When a process is switched to the background, its memory space is still reserved so that it can continue to be used when it gets the CPU again.

Therefore, even in the Ubuntu environment, the address value of the stack area will grow with function calls and returns, but this is limited to the stack space of a single process, and the stack spaces of different processes are independent of each other.

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Cloud native entry-level skills treeHomepageOverview 16044 people are learning the system