Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Embedded C language self-cultivation 05: zero length array

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/03 Report--

5.1 what is a zero-length array

As the name implies, a zero-length array is an array of length 0.

The ANSI C standard states that when defining an array, the length of the array must be a constant, that is, the length of the array is determined at compile time. You can define an array in ANSI C as follows:

Int a [10]

The new C99 standard stipulates that a variable length array can be defined.

Int len;int a [len]

That is, the length of the array is uncertain at compile time, determined when the program is running, and can even be specified by the user. For example, we can define an array and then specify the size of the array when the program is running, and we can initialize the array by entering data, as shown in the sample code.

Int main (void) {int len; printf ("input array len:"); scanf ("% d", & len); int a [len]; for (int itemia, "hello wanglitao!\ n"); puts (buf- > a); free (buf); return 0;}

In this program, we use malloc to request a piece of memory with the size of sizeof (buffer) + 20, that is, 24 bytes. Four bytes are used to store the structure type variables pointed to by the struct pointer buf, and the other 20 bytes are the memory space we really use. We can access this memory directly through the structure member a.

Through this flexible dynamic memory request method, a memory buffer represented by this buffer structure can be adjusted at any time, large or small. This feature is very useful in some situations. For example, many online video sites now support video playback in multiple formats: universal definition, high definition, ultra-definition, 1080p, Blu-ray and even 4K. If our local program needs to apply for a buffer in memory to cache the decoded video data, then the buffer size is different for different playback formats. If we apply for memory according to the 4K standard, we will not be able to use such a large buffer and waste memory when playing Puqing video. Using the variable-length structure, we can flexibly apply for different sizes of buffer according to the user's playback format, which greatly saves memory space.

5.3 use of zero-length arrays in the kernel

Zero-length arrays are generally used in the kernel in the form of variable-length structures. Today we will analyze the USB driver in the Linux kernel. In the network card driver, you may be familiar with a name: socket buffer, or socket buffer, which is used to transmit network packets. Similarly, in the USB driver, there is a similar thing called URB, whose full name is USB request block, or USB request block, which is used to transmit USB packets.

Struct urb {struct kref kref; void * hcpriv; atomic_t use_count; atomic_t reject; int unlinked; struct list_head urb_list; struct list_head anchor_list; struct usb_anchor * anchor; struct usb_device * dev; struct usb_host_endpoint * ep; unsigned int pipe; unsigned int stream_id; int status; unsigned int transfer_flags; void * transfer_buffer; dma_addr_t transfer_dma Struct scatterlist * sg; int num_mapped_sgs; int num_sgs; U32 transfer_buffer_length; U32 actual_length; unsigned char * setup_packet; dma_addr_t setup_dma; int start_frame; int number_of_packets; int interval; int error_count; void * context; usb_complete_t complete; struct usb_iso_packet_descriptor iso_frame_desc [0];}

In this structure, the transmission direction, transmission address, transmission size and transmission mode of USB packets are defined. We will not delve into these details, we will only look at the last member:

Struct usb_iso_packet_descriptor iso_frame_desc [0]

At the end of the URB structure, a zero-length array is defined, which is mainly used for synchronous transmission of USB. USB has four transmission modes: interrupt transmission, control transmission, batch transmission and synchronous transmission. Different USB devices have different requirements for transmission speed and data security, and the transmission modes adopted are different. USB camera requires high real-time transmission of video or image, and it doesn't care very much about the frame loss of data. It doesn't matter if you lose a frame, and then send it down. So the USB camera adopts USB synchronous transmission mode.

Now the USB camera on Taobao, which opens its instructions, generally supports a variety of resolutions: from 16x16 to HD 720p. For video transmission with different resolutions, for one frame of image data, the requirements for the size and number of USB transmission packets are different. So how on earth should USB be designed to adapt to this different size of data transmission requirements, but does not affect other transmission modes of USB? The answer lies in the zero-length array inside the structure.

When users set different resolutions to transmit video, USB needs to use different sizes and number of packets to transmit a frame of video data. The variable length structure formed by a zero-length array can meet this requirement. According to the size of a frame of image data, the memory space can be applied flexibly to meet the data transmission of different sizes. But this zero-length array does not occupy the storage space of the structure, when USB uses other modes of transmission, it is not affected, so it can be treated as if the zero-length array does not exist. So, I have to say, this kind of design is really wonderful!

5.4 think: why not use pointers instead of zero-length arrays?

On various occasions, you may often see the words that an array name is equivalent to a pointer when passed as a function parameter. Here, we must not be confused by this sentence: when an array name is passed as a function parameter, it does pass an address, but the array name is by no means a pointer, and the two are not the same thing. The array name is used to represent the address of a block of contiguous memory space, and the pointer is a variable, and the compiler allocates a separate memory space to hold the address of the variable it points to. Let's take a look at the following program.

Struct buffer1 {int len; int a [0];}; struct buffer2 {int len; int * a;}; int main (void) {printf ("buffer1:% d\ n", sizeof (struct buffer1)); printf ("buffer2:% d\ n", sizeof (struct buffer2)); return 0;}

The running results are as follows:

Buffer1:4buffer2:8

For a pointer variable, the compiler allocates a separate storage space for the pointer variable, and then stores the address of another variable on this storage space. Let's say that the pointer points to the variable. On the other hand, the compiler will not assign another storage space to the array name, it is just a symbol, like the function name, to represent an address. Let's move on to another program.

/ / hello.cint array1 [10] = {1 void 2, 3, 5, 6, 7, 8, 9}; int array2 [0]; int * p = & array1 [5]; int main (void) {return 0;}

In this program, we define a normal array, a zero-length array and a pointer variable, respectively. Where the value of the pointer variable p is the address of the array element array1 [5], that is, the pointer p points to arraay1 [5]. We then compile the program using the arm cross-compiler and disassemble it.

$arm-linux-gnueabi-gcc hello.c-o a.out$ arm-linux-gnueabi-objdump-D a.out

From the assembly code generated by disassembly, we find the assembly code for array1 and pointer variable p.

00021024: 21024: 00000001 andeq r0, r0, r1 21028: 00000002 andeq r0, r0, r2 2102c: 00000003 andeq r0, r0, r3 21030: 00000004 andeq r0, r0, r4 21034: 00000005 andeq r0, r0, r5 21038: 00000006 andeq r0, r0, r6 2103c: 00000007 andeq r0, r0, r7 21040: 00000008 andeq r0, r0, r8 21044: 00000009 andeq r0, r0 R9 21048: 00000000 andeq r0, r0, r00002104c

: 2104c: 00021038 andeq r1, r2, r8, lsr r0Disassembly of section .bss: 00021050: 21050: 00000000 andeq r0, r0, r0

From the assembly code, you can see that for the array array1 [10] of length 10, the compiler allocates a total of 40 bytes of storage space from 0x21024--0x21048, but does not allocate storage space separately to the array name array1. The array name array1 only represents the first address of the 40 consecutive storage spaces, that is, the address of the array element array1 [0]. For the zero-length array array2 [0], the compiler does not allocate storage space to it, and array2 is just a symbol to represent an address in memory, which we can find by looking at the symbol table of the executable file a.out.

$readelf-s a.out 88: 00021024 40 OBJECT GLOBAL DEFAULT 23 array1 89: 00021054 0 NOTYPE GLOBAL DEFAULT 24 _ bss_end__ 90: 00021050 0 NOTYPE GLOBAL DEFAULT 23 _ edata 91: 0002104c 4 OBJECT GLOBAL DEFAULT 23 p 92: 000104800 0 FUNC GLOBAL DEFAULT 14 _ fini 93: 00021054 0 NOTYPE GLOBAL DEFAULT 24 _ bss_end__ 94: 0002101c 0 NOTYPE GLOBAL DEFAULT 23 _ data_start_ 96: 00000000 0 NOTYPE WEAK DEFAULT UND _ _ gmon_start__ 97: 00021020 0 OBJECT GLOBAL HIDDEN 23 _ _ dso_handle 98: 00010488 4 OBJECT GLOBAL DEFAULT 15 _ IO_stdin_used 99: 0001041c 96 FUNC GLOBAL DEFAULT 13 _ _ libc_csu_init 100: 00021054 0 OBJECT GLOBAL DEFAULT 24 array2 101: 00021054 0 NOTYPE GLOBAL DEFAULT 24 _ end 102: 000102d8 0 FUNC GLOBAL DEFAULT 13 _ start 103: 00021054 0 NOTYPE GLOBAL DEFAULT 24 _ _ end__ 104: 00021050 0 NOTYPE GLOBAL DEFAULT 24 _ _ bss_start 105: 00010400 28 FUNC GLOBAL DEFAULT 13 main 107: 00021050 0 OBJECT GLOBAL HIDDEN 23 _ _ TMC_END__ 110: 00010294 0 FUNC GLOBAL DEFAULT 11 _ init

As you can see from the symbol table, the address of array2 is 0x21054, after the bss section of the program. The default address indicated by the array2 symbol is an unused memory space, and the compiler will never assign it another memory space alone to store the array name. See here, you may understand: array name and pointer are not the same thing, although the array name as a function parameter, can be used as an address, but the two can not be equated. Kitchen knives can sometimes be used as weapons, but you can't say kitchen knives are weapons.

As for why you don't use pointers, it's very simple. If you use a pointer, the pointer itself will take up storage space, not to mention, according to the case study of the USB driver above, you will find that it is far less ingenious than the zero-length array-it does not cause redundancy in the definition of the structure, and it is easy to use.

This tutorial is adapted from the C language embedded Linux Advanced programming Video tutorial No. 05, the electronic version of the book can join the QQ group: 475504428 download, more embedded video tutorials, you can follow:

Official account of Wechat: Otaku tribe (armlinuxfun)

51CTO College-Mr. Wang Litao: http://edu.51cto.com/sd/d344f

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report