柔性数组(变长数组)介绍
柔性数组简介
柔性数组,或称为可变长度数组,是一种在C语言结构体定义中使用的特殊数组,它允许结构体拥有一个可变大小的数组成员。柔性数组成员必须是结构体的最后一个成员,且它不占用结构体大小的计算,这使得可以动态地分配超出结构体声明大小的内存,从而容纳变长的数据。
优点
- 动态内存管理:使用柔性数组可以根据需要动态地分配更多的内存,这在处理不确定大小的数据时非常有用。
- 内存连续性:柔性数组的数据存储在单一连续的内存块中,这有利于提高内存访问效率。
- 简化指针操作:通过减少额外的指针或分配,柔性数组可以简化代码和降低出错率。
如何使用柔性数组
要在C语言中使用柔性数组,你需要在结构体定义中将最后一个元素声明为未指定大小的数组。这里是一个典型的使用示例:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int length;
double data[]; // 柔性数组成员
} FlexibleArray;
int main() {
int desiredLength = 5;
// 分配内存时,包括结构体基础大小和数组所需的额外空间
FlexibleArray *array = (FlexibleArray *)malloc(sizeof(FlexibleArray) + sizeof(double) * desiredLength);
array->length = desiredLength;
for (int i = 0; i < array->length; i++) {
array->data[i] = i * 2.0;
}
for (int i = 0; i < array->length; i++) {
printf("Element %d = %f\n", i, array->data[i]);
}
free(array);
return 0;
}
注意事项
- 柔性数组成员不占用结构体大小的计算。
- 只有位于结构体最后一个成员位置的数组可以被声明为柔性数组。
- 分配含有柔性数组的结构体时,需要手动计算额外内存的需求。
- 使用完毕后,需要手动释放内存以避免内存泄露。
在网络通信中使用柔性数组
消息结构
// message.h
#ifndef MESSAGE_H
#define MESSAGE_H
#include <stdint.h>
typedef struct {
uint32_t length; // 消息长度
char data[0]; // 数据部分
} message_t;
#endif
服务端
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "message.h"
int main(int argc, char *argv[]) {
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr, "ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) printf("ERROR opening socket\n");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
printf("ERROR on binding\n");
listen(sockfd, 5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) printf("ERROR on accept\n");
uint32_t msg_length;
n = read(newsockfd, &msg_length, sizeof(msg_length));
if (n < 0) printf("ERROR reading from socket\n");
msg_length = ntohl(msg_length); // 确保网络字节序转换为主机字节序
message_t *msg = (message_t*)malloc(sizeof(message_t) + msg_length - 1); // 分配额外的空间
n = read(newsockfd, msg->data, msg_length);
if (n < 0) printf("ERROR reading from socket\n");
printf("Here is the message: %s\n", msg->data);
close(newsockfd);
close(sockfd);
return 0;
}
客户端
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include "message.h"
int main(int argc, char *argv[]) {
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
printf("ERROR opening socket\n");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
printf("ERROR connecting\n");
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
uint32_t msg_length = strlen(buffer);
// 发送消息长度
uint32_t n_msg_length = htonl(msg_length); // 转换为网络字节序
write(sockfd, &n_msg_length, sizeof(n_msg_length));
// 发送消息数据
n = write(sockfd, buffer, msg_length);
if (n < 0)
printf("ERROR writing to socket\n");
close(sockfd);
return 0;
}