百度笔试题 求大牛给个思路

sj13426074890 2009-10-20 09:28:08
1. 考虑一个字符串替换的过程,在一个文本文件中含有一些文本内容和一些需要替换的变量,变量的格式为“$Var$”,原来的“$”使用“$$”进行转义,原来的“$$”表示为“$$$”。我们将含有变量的文件称为模板(文件名为t),文本文件的平均长度为100K。另外,还有一系列的变量文件,里面为变量名和变量值的对应关系(文件名为1.v , 2.v… n.v),每个变量文件包含的变量数在百万数量级,且变量排列次序不定。现要求将,模板里的变量分别用变量文件里的变量替换,并将生成的文件写成 (1.r, 2.r… n.r)。
要求:从算法和实现上和实现技术上的细节对程序进行优化,尽量使程序高效。程序运行环境为2G内存,4CPU。阐明主要思路,给出伪码和说明,可以着重指出你使用的优化技术。
例子:模板文件为
This is an $FF$ $$. I like $FF$ and $FA$。
变量文件为
1.v
FF : banana
FA : apple
2.v
FA: 苹果
FF : 香蕉
则生成文件为
1.r
This is an banana $$. I like banana and apple。
2.r
This is an香蕉 $$. I like 香蕉and苹果。
...全文
1198 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
king-0325 2009-10-24
  • 打赏
  • 举报
回复
shangmian de da ma hen niu cha a
yutaooo 2009-10-24
  • 打赏
  • 举报
回复

/*
* template.h
*/
#ifndef CSDN_TEMPLATE_H
#define CSDN_TEMPLATE_H

struct template_record;

struct template_record *create_template_record();
void destroy_template_record(struct template_record *record);
int init_template_record(struct template_record *record, char *buffer);
struct template_record *clone_template_record(struct template_record *record);
int replace_template_record(struct template_record *record, char *name, char *value);
void prnt_template_record(struct template_record *record);
void output_template_record(struct template_record *record, char *output_filename);

#endif /* CSDN_TEMPLATE_H */


/*
* template.c
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "mem_alloc.h"
#include "template.h"

#define MAX_TOKEN_NUM 4096
#define VARIABLE_ENTRY_NUM 2048
#define HASH_SLOT_NUM 2048

#define HASH_INDEX(str) ( ELFHash(str, strlen(str)) % (HASH_SLOT_NUM-1) )

struct template_hash_entry {
size_t token_entry;
int did;
size_t next;
};

struct template_record {
char *buffer;
int token_num;
char *tokens[MAX_TOKEN_NUM];
size_t var_map[HASH_SLOT_NUM];
size_t last_entry;
struct template_hash_entry var_array[VARIABLE_ENTRY_NUM];
};

size_t ELFHash(char* str, size_t len) {
size_t hash = 0;
size_t x = 0;
size_t i = 0;
for (i = 0; i < len; str++, i++) {
hash = (hash << 4) + (*str);
if((x = hash & 0xF0000000L) != 0) {
hash ^= (x >> 24);
}
hash &= ~x;
}
return hash;
}

struct template_record *create_template_record() {
int i;
struct template_record *record = NULL;

record = mem_alloc(sizeof(struct template_record));

memset(record, 0, sizeof(struct template_record));

for (i = 0; i < HASH_SLOT_NUM; i++)
record->var_map[i] = -1;

return record;
};


void destroy_template_record(struct template_record *record) {
mem_free(record);
}

int init_template_record(struct template_record *record, char *buffer) {
size_t i = 0, index = 0;
char *ptr, *str;
struct template_hash_entry *entry;

record->buffer = buffer;

ptr = str = record->buffer;

record->tokens[i] = str;

while (*str != '\0') {

while (*ptr != '\0' && *ptr != '$')
ptr++;

if (ptr > str) {
record->tokens[i++] = str;
str = ptr;
}

if (*ptr == '$') {
*ptr++ = '\0';

assert("bad string: '\0' followed '$' !!!"
&& *ptr != '\0');
if (*ptr == '\0')
return -1;

str = ptr;

if (*ptr++ != '$') {
while (*ptr != '\0' && *ptr != '$')
ptr++;
*ptr++ = '\0';
record->tokens[i] = str;

entry = &record->var_array[record->last_entry];
entry->token_entry = i;
index = HASH_INDEX(str);
entry->next = record->var_map[index];
record->var_map[index] = record->last_entry;
record->last_entry++;
i++;
} else {
if (*ptr != '\0' && *ptr == '$') {
ptr++;
record->tokens[i++] = "$$";
}
else
record->tokens[i++] = "$";
}

str = ptr;
}
}

record->token_num = i;

return 0;
}

int replace_template_record(struct template_record *record, char *name, char *value) {
size_t index;
struct template_hash_entry *entry;
int count;

index = record->var_map[HASH_INDEX(name)];

count = 0;
while (index != -1) {
entry = &record->var_array[index];
if (entry->did == 0 && strcmp(record->tokens[entry->token_entry], name) == 0) {
entry->did = 1;
record->tokens[entry->token_entry] = value;
count++;
}
index = entry->next;
}
return count;
}

struct template_record *clone_template_record(struct template_record *record) {
struct template_record *new_record;

new_record = mem_alloc(sizeof(struct template_record));

memcpy(new_record, record, sizeof(struct template_record));

return new_record;
}

void prnt_template_record(struct template_record *record) {
size_t i = 0;
size_t index;
struct template_hash_entry *ptr;

printf("======== tokens ========\n");
printf("token_num: %05d\n", record->token_num);
for (i = 0; i < record->token_num; i++)
printf("[%05d]: %s\n", i, record->tokens[i]);

printf("===== var_map ======\n");
for (i = 0; i < HASH_SLOT_NUM; i++) {
index = record->var_map[i];
if (index != -1) {
ptr = &record->var_array[index];
printf("[%05d]: (%1d, %s, %05d)", i, ptr->did,
record->tokens[ptr->token_entry], ptr->next);

index = ptr->next;
while (index != -1) {
ptr = &record->var_array[index];
printf("->(%1d, %s, %05d)", ptr->did,
record->tokens[ptr->token_entry], ptr->next);
index = ptr->next;
}

printf("\n");
}
}

printf("======== var_array ========\n");
printf("last_entry: %05d\n", record->last_entry);
for (i = 0; i < record->last_entry; i++)
printf("[%05d] (%1d, %s, %05d)\n", i,
record->var_array[i].did,
record->tokens[record->var_array[i].token_entry],
record->var_array[i].next);
}

void output_template_record(struct template_record *record, char *output_filename) {
FILE *fp;
size_t i;

fp = fopen(output_filename, "w+");

for (i = 0; i < record->token_num; i++)
fprintf(fp, "%s",record->tokens[i]);

fclose(fp);
}
yutaooo 2009-10-24
  • 打赏
  • 举报
回复

/*
* replace.h
*/
#ifndef CSDN_REPLACE_H
#define CSDN_REPLACE_H

struct job_replace {
char *output_filename;
char *template_buffer;
char *variable_buffer;
struct template_record *record_template;
struct variable_record *record_variable;
};

void enqueue_job_queue(struct job_replace *job);
struct job_replace *dequeue_job_queue();
void *task_job(void *arg);

#endif /* CSDN_REPLACE_H */


/*
* replace.c
*/
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include "mem_alloc.h"
#include "template.h"
#include "variable.h"
#include "replace.h"

#define MAX_JOB_ENTRY_NUM 1024

struct job_queue_entry {
struct job_queue_entry *next;
struct job_queue_entry *prev;
struct job_replace *job;
};

static struct job_queue_entry job_head = {&job_head, &job_head, NULL};
static pthread_mutex_t mutex_queue = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_queue_empty = PTHREAD_COND_INITIALIZER;;

void enqueue_job_queue(struct job_replace *job) {
struct job_queue_entry *entry;

entry = mem_alloc(sizeof(struct job_queue_entry));

pthread_mutex_lock(&mutex_queue);
entry->job = job;
entry->next = &job_head;
entry->prev = job_head.prev;
entry->prev->next = entry;

pthread_cond_broadcast(&cond_queue_empty);
pthread_mutex_unlock(&mutex_queue);
}

struct job_replace *dequeue_job_queue() {
struct job_queue_entry *entry;
struct job_replace *job;

pthread_mutex_lock(&mutex_queue);
while (job_head.next == &job_head)
pthread_cond_wait(&cond_queue_empty, &mutex_queue);

entry = job_head.next;
job_head.next = entry->next;
entry->next->prev = &job_head;

job = entry->job;
pthread_mutex_unlock(&mutex_queue);

mem_free(entry);

return job;
}

void do_job(struct job_replace *job) {
size_t i, last_entry;
struct variable_entry *entry;

last_entry = job->record_variable->last_entry;
entry = job->record_variable->var_array;
for (i = 0; i < last_entry; i++, entry++)
replace_template_record(job->record_template, entry->name, entry->value);
prnt_template_record(job->record_template);
output_template_record(job->record_template, job->output_filename);
}

void *task_job(void *arg) {
struct job_replace *job;

pthread_detach(pthread_self());

printf("task %d start ...\n", (int)arg);
for (;;) {
job = dequeue_job_queue();
printf("task %d working ...\n", (int)arg);
do_job(job);

mem_free(job->variable_buffer);
mem_free(job->record_template);
mem_free(job->record_variable);
mem_free(job);
}

return (void *)0;
}
yutaooo 2009-10-24
  • 打赏
  • 举报
回复
希望继续讨论下去。我对这个也有点兴趣。因此附上代码。
这是一个有问题的版本。在内存分配过程中,应该是有可能发生死锁。直觉上,将内存分配只放在主线程,任意线程可以释放似乎是个解决办法。没有仔细想,希望听听大侠们的意见。代码比较粗糙,请见谅。



#
# Makefile
#

all: test test_template test_variable gen_variable

test: test.o replace.o template.o variable.o mem_alloc.o Makefile
gcc -o test -pthread test.o replace.o template.o variable.o mem_alloc.o

test.o: test.c Makefile
gcc -ggdb -c -o test.o test.c

replace.o: replace.c Makefile
gcc -ggdb -c -o replace.o replace.c

gen_variable: gen_variable.o Makefile
gcc -o gen_variable gen_variable.o

gen_variable.o: gen_variable.c Makefile
gcc -ggdb -c -o gen_variable.o gen_variable.c

test_template: test_template.o template.o mem_alloc.o
gcc -o test_template -pthread test_template.o template.o mem_alloc.o

test_variable: test_variable.o variable.o mem_alloc.o
gcc -o test_variable -pthread test_variable.o variable.o mem_alloc.o

test_template.o: test_template.c Makefile
gcc -ggdb -c -o test_template.o test_template.c

template.o: template.c Makefile
gcc -ggdb -c -o template.o template.c

test_variable.o: test_variable.c Makefile
gcc -ggdb -c -o test_variable.o test_variable.c

variable.o: variable.c Makefile
gcc -ggdb -c -o variable.o variable.c

mem_alloc.o: mem_alloc.c Makefile
gcc -ggdb -c -o mem_alloc.o mem_alloc.c

clean:
rm -f core test test.o replace.o test_template test_template.o template.o \
test_variable test_variable.o variable.o mem_alloc.o



/*
* test.c
*/
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include "mem_alloc.h"
#include "template.h"
#include "variable.h"
#include "replace.h"

#define TEMPLATE_BUFFER_SIZE 100*1024
#define VARIABLE_BUFFER_SIZE 10*1024*1024

static char template_buffer[TEMPLATE_BUFFER_SIZE];

int fill_buffer(void *buffer, size_t size, char *filename) {
int fd;

fd = open(filename, O_RDONLY);
if (fd == -1)
return -1;

if (read(fd, buffer, size) < 0)
return -1;

close(fd);

return 0;
}

int main(int argc, char *argv[]) {
int i;
char *template_filename, *variable_filename;
char *variable_buffer;
struct template_record *record_template, *record_template_template;
struct variable_record *record_variable;
struct job_replace *job;
pthread_t tid;

if (argc < 3) {
printf("usage: ./test T-file V-file-1 [V-file-2] ... [V-file-n]\n");
return -1;
}

template_filename = argv[1];

if (fill_buffer(template_buffer, TEMPLATE_BUFFER_SIZE, template_filename) != 0)
return -1;

if ( (record_template_template = create_template_record()) == NULL)
return -1;

if (init_template_record(record_template_template, template_buffer) != 0) {
free(record_template_template);
return -1;
}

/* create 4 thread */
pthread_create(&tid, NULL, task_job, (void *)0);
pthread_create(&tid, NULL, task_job, (void *)1);
pthread_create(&tid, NULL, task_job, (void *)2);
pthread_create(&tid, NULL, task_job, (void *)3);

/* enqueue job queue */
i = 2;
while ((variable_filename = argv[i++]) != NULL) {
variable_buffer = mem_alloc(VARIABLE_BUFFER_SIZE);

if (fill_buffer(variable_buffer, VARIABLE_BUFFER_SIZE, variable_filename) != 0) {
printf("fill_buffer() with variable_filename: %s failed\n", variable_filename);
mem_free(variable_buffer);
continue;
}

record_variable = create_variable_record();

if ( init_variable_record(record_variable, variable_buffer) != 0) {
printf("bad variable file. filename: %s\n", variable_filename);
mem_free(record_variable);
mem_free(variable_buffer);
continue;
}

record_template = clone_template_record(record_template_template);

job = mem_alloc(sizeof(struct job_replace));

*variable_filename = 'r';
job->output_filename = variable_filename;
job->template_buffer = template_buffer;
job->variable_buffer = variable_buffer;
job->record_template = record_template;
job->record_variable = record_variable;

enqueue_job_queue(job);
}

destroy_template_record(record_template_template);

pthread_exit((void *)0);
return 0;
}

lzx258 2009-10-24
  • 打赏
  • 举报
回复
看看 大牛们
thinke365 2009-10-24
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 whg01 的回复:]
内存足够的情况下,用hash反而会变慢。
针对这个题的条件,不建议用hash。
[/Quote]
不对吧?
thinke365 2009-10-24
  • 打赏
  • 举报
回复
hash_map + 多线程
yutaooo 2009-10-24
  • 打赏
  • 举报
回复
CSDN 太奇怪了。不能连续发3次。感谢LS。可以发全了。



/*
* variable.h
*/
#ifndef CSDN_VARIABLE_H
#define CSDN_VARIABLE_H

#define MAX_VARIABLE_NUM 10*1024*1024

struct variable_entry {
char *name;
char *value;
};

struct variable_record {
char *buffer;
size_t last_entry;
struct variable_entry var_array[MAX_VARIABLE_NUM];
};

struct variable_record *create_variable_record();
void destroy_variable_record(struct variable_record *record);
int init_variable_record(struct variable_record *record, char *buffer);
void prnt_variable_record(struct variable_record *record);

#endif /* CSDN_VARIABLE_H */


/*
* variable.c
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "mem_alloc.h"
#include "variable.h"

struct variable_record *create_variable_record() {
struct variable_record *record = NULL;

record = mem_alloc(sizeof(struct variable_record));

memset(record, 0, sizeof(struct variable_record));

return record;
}

void destroy_variable_record(struct variable_record *record) {
mem_free(record);
}

int init_variable_record(struct variable_record *record, char *buffer) {
struct variable_entry *entry;
char *ptr, *key, *value;

record->buffer = buffer;

ptr = record->buffer;
while (*ptr != 0) {
key = NULL; value = NULL;

/* forward space */
while (*ptr != '\0' && *ptr == ' ')
ptr++;
if (*ptr == '\0' || !isalpha(*ptr))
return -1;

/* key */
key = ptr;
while (isalpha(*ptr))
ptr++;

if (*ptr == ' ') {
*ptr++ = '\0';
while (*ptr != '\0' && *ptr == ' ')
*ptr++ = '\0';
}

if (*ptr != ':')
return -1;

*ptr++ = '\0';

while (*ptr != '\0' && *ptr == ' ')
*ptr++ = '\0';

if (*ptr == '\0' || !isalpha(*ptr))
return -1;

/* value */
value = ptr;
while (isalpha(*ptr))
*ptr++;

if (*ptr == ' ') {
*ptr++ = '\0';
while (*ptr != '\0' && *ptr == ' ')
*ptr++ = '\0';
}

if (*ptr != '\n' && *ptr != '\0')
return -1;
*ptr++ = '\0';

if (key != NULL && value != NULL) {
entry = &record->var_array[record->last_entry++];
entry->name = key;
entry->value = value;
}
}

return 0;
}

void prnt_variable_record(struct variable_record *record) {
size_t i;
struct variable_entry *p;

printf("===== var_array =====\n");
printf("last_entry: %08d\n", record->last_entry);
for (i = 0; i < record->last_entry; i++)
printf("[%08d]: (%s, %s)\n", i, record->var_array[i].name,
record->var_array[i].value);
}


/*
* mem_alloc.h
*/
#ifndef CSDN_MEM_ALLOC_H
#define CSDN_MEM_ALLOC_H

void *mem_alloc(size_t size);
void mem_free(void *ptr);

#endif /* CSDN_MEM_ALLOC_H */


/*
* mem_alloc.c
*/
#include <stdlib.h>
#include <pthread.h>

static pthread_mutex_t mutex_mem = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_mem = PTHREAD_COND_INITIALIZER;

void *mem_alloc(size_t size) {
void *ptr;

pthread_mutex_lock(&mutex_mem);
while ( (ptr = malloc(size)) == NULL)
pthread_cond_wait(&cond_mem, &mutex_mem);
pthread_mutex_unlock(&mutex_mem);

return ptr;
}

void mem_free(void *ptr) {
pthread_mutex_lock(&mutex_mem);
free(ptr);
pthread_cond_broadcast(&cond_mem);
pthread_mutex_unlock(&mutex_mem);
}

whg01 2009-10-20
  • 打赏
  • 举报
回复
结构体的作用:
1. 快速判断变量应该在什么地方替换,以及替换成什么内容。
2. 生成最终结果时,遇到变量替换,就去结构体数组里读取。
sj13426074890 2009-10-20
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 threeleafzerg007 的回复:]
瞎掰两句:

把文本文件读入内存(100K )O(text)

将文本中所有需要替换的变量 抽取至内存中 建立一张hash表  O(text)

然后开始分别遍历 v1 v2 v3 v4.. vn  O(variable * n)

如果v1 FF 是香蕉(且hash表非空 即文本文件需要)则 FF对应的hash表挂上结点 香蕉 v1 , v2 FF 是banana 则FF 对应hash表的结点链表上挂上 banana FF

所有变量文件遍历完了

根据内存中的文本文件 和变量hash表就可以生成 r1 r2 r3 ... rn了  O(text * n)


[/Quote]

我也是这么想的,感觉不太对,几百万个那样的话,怎么处理冲突。
sj13426074890 2009-10-20
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 whg01 的回复:]
应该是:
1.r
This is an banana $. I like banana and apple。
2.r
This is an香蕉 $. I like 香蕉and苹果。
楼主是不是多写了个$。
基本思路:
因为变量文件的规模要远远大于模板文件,所以对模板文件的信息进行排序,然后用二分查找来看变量应该替换模板文件中哪个位置的内容。
大概过程:
首先解析模板文件,处理$转义,并提取出模板中所有变量的名称,存储到结构体数组M中。结构体数组要按变量名称进行快速排序。结构体至少要包含如下信息:变量名称,在处理完$转移后模板的位置,变量替换后的值,是否进行了变量替换,在模板中出现的位置序号。

然后读取解析每个变量,根据变量名在M中进行二分查找,处理。
最后再从头到尾把模板合并输出。

如果说了这些你还做不出来,那百度面试你就危险了。

[/Quote]

我大致看了一遍 就是对你那个结构体的作用不太懂,
whg01 2009-10-20
  • 打赏
  • 举报
回复
内存足够的情况下,用hash反而会变慢。
针对这个题的条件,不建议用hash。
grpubr 2009-10-20
  • 打赏
  • 举报
回复
占楼编辑
threeleafzerg007 2009-10-20
  • 打赏
  • 举报
回复
瞎掰两句:

把文本文件读入内存(100K )O(text)

将文本中所有需要替换的变量 抽取至内存中 建立一张hash表 O(text)

然后开始分别遍历 v1 v2 v3 v4.. vn O(variable * n)

如果v1 FF 是香蕉(且hash表非空 即文本文件需要)则 FF对应的hash表挂上结点 香蕉 v1 , v2 FF 是banana 则FF 对应hash表的结点链表上挂上 banana FF

所有变量文件遍历完了

根据内存中的文本文件 和变量hash表就可以生成 r1 r2 r3 ... rn了 O(text * n)


delphiwcdj 2009-10-20
  • 打赏
  • 举报
回复
up
whg01 2009-10-20
  • 打赏
  • 举报
回复
应该是:
1.r
This is an banana $. I like banana and apple。
2.r
This is an香蕉 $. I like 香蕉and苹果。
楼主是不是多写了个$。
基本思路:
因为变量文件的规模要远远大于模板文件,所以对模板文件的信息进行排序,然后用二分查找来看变量应该替换模板文件中哪个位置的内容。
大概过程:
首先解析模板文件,处理$转义,并提取出模板中所有变量的名称,存储到结构体数组M中。结构体数组要按变量名称进行快速排序。结构体至少要包含如下信息:变量名称,在处理完$转移后模板的位置,变量替换后的值,是否进行了变量替换,在模板中出现的位置序号。

然后读取解析每个变量,根据变量名在M中进行二分查找,处理。
最后再从头到尾把模板合并输出。

如果说了这些你还做不出来,那百度面试你就危险了。


chenzhp 2009-10-20
  • 打赏
  • 举报
回复
我觉得自己好烂啊。。。。。。。
Arkwyd 2009-10-20
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 arkwyd 的回复:]
文本文件长度约100K,变量个数,估计高一点,10K个,占内存不到100K。变量文件个数不定,每个包含百万个变量,约1M。每文件大小可估计,10M。由此,文本文件常驻内存是可能的,变量文件就免了。

// 此结构用于遍历文本文件,找出所有变量
struct tag_variable
{
    long pos;    // 在文本文件中的位置
    char name[20];
}
typedef struct tag_variable  var;

声明 var 数组,有元素10K个。
遍历一次文本文件,填充var数组,同时建立二叉搜索树。
并行:对每个变量文件过滤并排序,得新变量文件。
并行:对每个新变量文件作一次遍历,记录下每个var结构与本文件中对应的变量替换内容的一一映射。因为文本文件与变量文件都是有序的,所以这个操作是无需回退的。顺序访问即可。
并行:对文本文件中的每个变量,依次替换,写入.r文件。



[/Quote]

刚写得急了点,结构中的pos域可以省掉。
Arkwyd 2009-10-20
  • 打赏
  • 举报
回复
文本文件长度约100K,变量个数,估计高一点,10K个,占内存不到100K。变量文件个数不定,每个包含百万个变量,约1M。每文件大小可估计,10M。由此,文本文件常驻内存是可能的,变量文件就免了。

// 此结构用于遍历文本文件,找出所有变量
struct tag_variable
{
long pos; // 在文本文件中的位置
char name[20];
}
typedef struct tag_variable var;

声明 var 数组,有元素10K个。
遍历一次文本文件,填充var数组,同时建立二叉搜索树。
并行:对每个变量文件过滤并排序,得新变量文件。
并行:对每个新变量文件作一次遍历,记录下每个var结构与本文件中对应的变量替换内容的一一映射。因为文本文件与变量文件都是有序的,所以这个操作是无需回退的。顺序访问即可。
并行:对文本文件中的每个变量,依次替换,写入.r文件。


pcboyxhy 2009-10-20
  • 打赏
  • 举报
回复
如果所有文件读入内存也能放的下,一次性读入,内存中建表
加载更多回复(13)

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧