c - 在 C 中使用 void 函数操作结构
问题描述
所以我的任务是创建一个仿字符串结构并在我的仿字符串结构上实现所有常用的字符串函数。我被困在名为 append 的 strcat 实现的测试中,第一个测试失败(segfault)是第 5 行。我创建新结构的函数应该没问题,因为它通过了所有测试,但我只是将它包括在内。
我已经能够成功地为我的人造字符串结构实现长度、获取、设置和复制功能。
结构:
struct text {
int capacity;
char *content;
};
typedef struct text text;
我创建新结构的功能:
text *newText(char *s) {
printf("new Text from %s\n", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %d\n", sizeGot);
return out;
free(c);
}
我的附加功能:
void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %d\n", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}
最后是测试:
void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}
解决方案
哦,你有一些错误:
- 在内部
text_new
分配内存以text *out
使用text *out = malloc(sizeGot);
whensizeGot = 24
是一个常量值。您应该为它分配sizeof(*out)
或sizeof(text)
字节的内存。 - 我不知道里面的
int sizeGot = 24; while (sizeNeeded > sizeGot)
循环是干什么用的。我猜目的是按照 24 的幂进行分配。而且看起来两个函数中的代码大多相同,看起来确实像代码重复,这是一件坏事。text_new
append
- 在里面
append
你传递一个指针t1
,而不是一个双指针,所以如果你修改 t1 指针本身,修改将在函数范围之外不可见。t1 = newText(stringy);
只是毫无意义并泄漏内存。你可以void append(text **t1, text *t2)
然后*t1 = newText(stringy)
。但是您可以使用更好的方法使用realloc
- 我希望 append 来“附加”字符串,而不是创建新对象。realloc
所以首先使用then调整缓冲区的大小strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1)
。 int sizeNeeded = (t1->capacity + t2->capacity);
已关闭。您以 24 的幂分配容量,这实际上与字符串长度无关。您需要strlen(t1->content) + strlen(t2->content) + 1
为字符串和空终止符都有字节。
尝试这个:
size_t text_newsize(size_t sizeNeeded)
{
// I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot *= 2;
}
return sizeGot;
}
text *newText(char *s) {
printf("new Text from %s\n", s);
if (s == NULL) return NULL;
int sizeNeeded = strlen(s) + 1;
int sizeGot = text_newsize(sizeNeeded);
text *out = malloc(sizeof(*out));
if (out == NULL) {
return NULL;
}
out->content = malloc(sizeGot);
if (out->content == NULL) {
free(out);
return NULL;
}
strcpy(out->content, s);
out->capacity = sizeGot;
printf("the capacity is %d\n", sizeGot);
return out;
}
和这个:
int append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %s\n", t1->content, t2->content);
int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;
if (t1->capacity < sizeNeeded) {
// this could a text_resize(text*, size_t) function
int sizeGot = text_newsize(sizeNeeded);
void *tmp = realloc(t1->content, sizeGot);
if (tmp == NULL) return -ENOMEM;
t1->content = tmp;
t1->capacity = sizeGot;
}
strcat(t1->content, t2->content);
return 0;
}
一些备注:
- 尝试处理库中的错误。如果你有一个像
void append(text *t1, text *t2)
let it be这样的函数int append(text *t1, text *t2)
,成功返回 0,错误返回负数*alloc
。 - 使用
size_t
类型存储所有内容的大小。它在中定义stddef.h
并且应该用于表示对象的大小。strlen
返回size_t
也sizeof
返回size_t
。 - 我喜欢把所有东西都放在一个单一的“命名空间”中,我通过在函数前面加上一个字符串来做到这一点
text_
。 - 我有一些空闲时间并决定实现你的库。
text
下面是一个存储字符串的简单对象的代码,我使用24
幻数作为分配块大小。
// text.h file
#ifndef TEXT_H_
#define TEXT_H_
#include <stddef.h>
#include <stdbool.h>
struct text;
typedef struct text text;
text *text_new(const char content[]);
void text_free(text *t);
int text_resize(text *t, size_t newsize);
int text_append(text *to, const text *from);
int text_append_mem(text *to, const void *from, size_t from_len);
const char *text_get(const text *t);
int text_append_str(text *to, const char *from);
char *text_get_nonconst(text *t);
size_t text_getCapacity(const text *t);
bool text_equal(const text *t1, const text *t2);
#endif // TEXT_H_
// text.c file
//#include "text.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
struct text {
size_t capacity;
char *content;
};
text *text_new(const char content[])
{
text * const t = malloc(sizeof(*t));
if (t == NULL) goto MALLOC_ERR;
const struct text zero = {
.capacity = 0,
.content = NULL,
};
*t = zero;
if (content != NULL) {
const int ret = text_append_str(t, content);
if (ret) {
goto TEXT_APPEND_ERR;
}
}
return t;
TEXT_APPEND_ERR:
free(t);
MALLOC_ERR:
return NULL;
}
void text_free(text *t)
{
assert(t != NULL);
free(t->content);
free(t);
}
int text_resize(text *t, size_t newcapacity)
{
// printf("%s %d -> %d\n", __func__, t->capacity, newcapacity);
// we resize in chunks
const size_t chunksize = 24;
// clap the capacity into multiple of 24
newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
void * const tmp = realloc(t->content, newcapacity);
if (tmp == NULL) return -ENOMEM;
t->content = tmp;
t->capacity = newcapacity;
return 0;
}
int text_append_mem(text *to, const void *from, size_t from_len)
{
if (to == NULL || from == NULL) return -EINVAL;
if (from_len == 0) return 0;
const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
const size_t newcapacity = oldcapacity + from_len + 1;
int ret = text_resize(to, newcapacity);
if (ret) return ret;
memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
to->content[newcapacity - 1] = '\0';
return 0;
}
int text_append_str(text *to, const char *from)
{
if (to == NULL || from == NULL) return -EINVAL;
return text_append_mem(to, from, strlen(from));
}
int text_append(text *to, const text *from)
{
if (to == NULL || from == NULL) return -EINVAL;
if (text_getCapacity(from) == 0) return 0;
return text_append_str(to, text_get(from));
}
const char *text_get(const text *t)
{
return t->content;
}
const size_t text_strlen(const text *t)
{
return t->capacity == 0 ? 0 : strlen(t->content);
}
size_t text_getCapacity(const text *t)
{
return t->capacity;
}
bool text_equal_str(const text *t, const char *str)
{
assert(t != NULL);
if (str == NULL && t->capacity == 0) return true;
const size_t strlength = strlen(str);
const size_t t_strlen = text_strlen(t);
if (t_strlen != strlength) return false;
if (memcmp(text_get(t), str, strlength) != 0) return false;
return true;
}
// main.c file
#include <stdio.h>
int text_testAppend(void) {
text *t = text_new("car");
if (t == NULL) return -1;
text *t2 = text_new("pet");
if (t2 == NULL) return -1;
if (text_append(t, t2)) return -1;
assert(text_equal_str(t, "carpet"));
assert(text_getCapacity(t) == 24);
text *t3 = text_new("789012345678901234");
if (t3 == NULL) return -1;
if (text_append(t, t3)) return -1;
assert(text_equal_str(t, "carpet789012345678901234"));
assert(text_getCapacity(t) == 48);
text_free(t);
text_free(t2);
text_free(t3);
return 0;
}
int main()
{
text *t1 = text_new("abc");
text_append_str(t1, "def");
printf("%s\n", text_get(t1));
text_free(t1);
printf("text_testAppend = %d\n", text_testAppend());
return 0;
}
推荐阅读
- mongodb - 如何检查集合的字段是否包含单个值或值数组
- java - 如何在java中同步异步操作
- antlr - 语义谓词影响范围
- intellij-idea - 有没有办法在 Intellij Scene Builder 中包含 JavaFX controlsFX?
- html - 在伪元素在 chrome 和 firefox 上出现不同之前
- swift - 带有 if 条件的 Swift 映射函数
- java - Spring hibernate CrudRepository 根据唯一约束更新保存方法
- java - 有扩展网址吗?
- php - 从另一个驱动器上的文档根目录连接到 XAMPP MySQL 数据库
- javascript - 如何在 Vue 中的组件之间共享一些代码