在C语言String结构体,模仿Java的String类(一)中,我为大家介绍了头文件的内容,在这里,我会介绍String结构体、以及如何创建String结构体、String结构体在头文件中函数是如何实现的。
首先我们来看看String结构体包含哪些成员:
从(一)中的结构定义来看,我们做了2件事情,首先使用结构体的不完整定义,先定义my_string这个结构体,然后通过typedef为my_string定义了一个类型别名String,方便在函数调用、函数声明中使用my_string的简单名,也就是:struct my_string可以用String替代。
struct my_string; // 使用结构体的不完整定义
typedef struct my_string String; // 定义字符串结构体的类型别名
接下来,就完整的定义my_string结构体,这样做的原因是我们需要在结构体的函数定义中使用String来作为参数。
我们在结构体中定义,保存字符串长度的length结构体成员,然后定义字符串内容的结构体成员char* value
int length; // 字符串的长度, 不包含末尾的\0
char* value; // 字符串的内容
接着,我们会模仿Java的String类代码,为my_string添加一些函数,这里用到了面向对象的设计。由于是c语言并没有Java语言类、this等特性,因此就用结构体来模仿了。主要思路是将一些函数指针定义为结构体的成员,函数指针如何定义呢?
可以这样看,假设需要一个获得字符串中某个索引的字符,那么可以定义一个函数:
char char_at(String* thisptr, int index);
根据c语言的语法规则,函数指针的定义,就应该是:
char (*char_at)(String* thisptr, int index);
对比一下,只是需要将普通的函数定义中,将函数名放到括号中,并在函数名的前面加一个星即可。如图:
参考上图,这就是函数指针的定义,它表示,这是一个名为char_at的函数指针,它可以指向一个函数,这个函数的返回值必须是char,需要有两个参数,一个是String结构体的指针,一个是int即可。
按照以上的介绍,我们就可以模仿Java的String中的相应方法,来定义我们的String结构体了。
具体的String结构体是如何定义的,请参见(一)的源代码,其中注释的很详细,就不在此占用篇幅过多介绍了。
如何创建String结构体,我设计了一个函数 String* build_string(const char* str),这个函数的目的是为了返回一个已创建好的String结构体指针。
创建结构体的代码以及结构体中函数的实现,我们都在my_string.c中完成,首先会引入必要的头文件。
/**
* 引入C语言标准输入输出头文件stdio.h
* 它包含了用于执行输入和输出操作的函数声明和宏定义,
* 比如常见的printf()(用于输出)、scanf()(用于输入)等函数。
* 通过在 C 程序中包含这个头文件,就可以使用这些与输入输出相关
* 的函数来与标准输入输出设备(如屏幕、键盘等)进行交互。
*/
#include <stdio.h>
/**
* 引入C语言标准库头文件stdlib.h
* 它包含了用于执行内存管理、动态内存分配、随机数生成、环境变量和系统相关操作、数值转换函数等函数。
* 内存管理:包含了如 malloc()、calloc()、realloc() 和 free() 等函数,用于管理内存。
* 类型转换函数:包含了如 atoi()、atof()、atol() 等函数,用于将字符串转换为数值。
*
*/
#include <stdlib.h>
/**
* 引入C语言标准字符串头文件string.h
* 字符串处理:包含了如 strcpy()、strncpy()、strcat()、strncat()、strcmp()、
* strncmp()、strchr()、strrchr() 等函数,用于处理字符串。
* 字符串操作:包含了如 strlen()、strstr()、strtok() 等函数,用于操作字符串。
*/
#include <string.h>
/**
* 引入C语言标准类型头文件ctype.h
* 它包含了用于判断字符类型、大小写转换等函数。
* 字符类型判断:包含了如 isalnum()、isalpha()、isdigit()、isspace() 等函数,用于判断字符类型。
* 大小写转换:包含了如 tolower()、toupper() 等函数,用于转换字符大小写。
*/
#include <ctype.h>
/**
* 引入C语言标准布尔类型头文件stdbool.h
* 它包含了用于定义布尔类型 bool 的宏定义。
* 布尔类型:bool 类型,用于表示真或假。
*/
#include <stdbool.h>
/**
* 引入errno头文件
* 它包含了用于获取系统错误信息的宏定义。
*/
#include <errno.h>
/**
* 引入common_util.h头文件
* 包含了一些自动以的工具函数声明和宏定义
*/
#include "common_util.h"
/**
* 引入my_string.h头文件
* 它包含了String结构体和相关函数声明
*/
#include "my_string.h"
接下来,我们编写String* build_string(const char* str)函数,该函数的主要作用是根据传入的char* str构建一个String结构体的指针,并返回给调用方。一个初步的实现如下:
/**
* 创建一个String结构体
* @param str 字符串
* @return 新的String结构体指针
* 结构体中的字符串指针指向字符串会trim到合适的大小
*/
String* build_string(const char* str){
if(str == NULL){
printf(">>> str参数的值为NULL!\n");
return NULL;
}
String* new_str = (String*)malloc(sizeof(String));
if(new_str == NULL){
printf(">>> String结构体创建失败: %s\n", strerror(errno));
}else{
const char* tmppos = str; // 先保存str的地址
if(*str == '\0'){
new_str->value = (char*)malloc(sizeof(char) * 1);
new_str->value[0] = '\0';
new_str->length = 0;
}else{
// 先计算str的长度
int i = 0;
while(*str != '\0'){
// printf(">>> str(%d): %c\n", i, *str);
i++;
str++;
}
new_str->length = i; // 保存字符的长度
new_str->value = (char*)malloc(i+1); // 为value在堆里申请内存
char* tmpval = new_str->value;
// 复制字符串到value
while(*tmppos != '\0'){
*tmpval = *tmppos;
tmppos++;
tmpval++;
}
*tmpval = '\0'; // 字符串末尾加上'\0'
}
// 下边将绑定字符串结构体中定义的各种函数
// 我们会实现一个函数就绑定一个,逐步完善。
}
return new_str;
}
首先,如果str是NULL那么,直接返回NULL,如果不是NULL, 我们就定义一个String结构体指针String* new_str, 并调用malloc方法为其在对上申请内存空间。
如果new_str为NULL,说明申请内存空间失败,那么调用strerro函数,输出错误原因。
如果new_str不为NULL,说明申请内存空间成功,那么先保存一下str,然后判断其是否是一个空字符串,也就是第一个字符是否是\0.
如果第一个字符是\0,那么我们就将new_str结构体的value在堆上分配一个字节的空间,将\0赋给value,将长度设置为0.
如果str的第一个字符不是\0,那么我们就先通过循环计算一下str的长度,然后调用malloc函数为new_str的value申请内存,接下来将str的每个字符赋值给value。
这样就完成了结构体的创建,我们返回new_str即可。
后续我们会逐步实现定义在String结构体中的函数,然后在String* build_string(const char* str)函数中将实现的函数绑定到String结构体中。
敬请期待。