异常
在写串算法时,有这么一段代码,添加之后下面的代码就无法正确执行了:
if (str->ch) {
free(str->ch);
str->ch=NULL;
}
错误代码
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char *ch;
int length;
} String;
int assign(String *str, char *ch) {
// 0.参数校验,如果 str 中已有字符,那么释放原串空间,因为我们会给它重新分配空间
if (str->ch) {
free(str->ch);
str->ch=NULL;
}
// 1.统计常量字符串 ch 中的字符个数,只有知道它的字符个数,我们才能清楚为 str 分配多少个字符空间
// 局部变量,存储常量字符串 ch 中的字符个数
int len = 0;
// 注意,我们不能直接操作 ch,因为是一个指针变量,在下面的操作后我们会移动指针,会修改掉 ch 原本的值,后面如果需要再使用就不是传入的参数值,所以要创建一个临时局部变量引用它的值来进行操作
char *c = ch;
// 从头到尾扫描常量字符串,以结束标记 '\0' 作为循环结束条件
while (*c != '\0') {
// 计数器加一
len++;
// 指针加一,继续下一个字符
c++;
}
// 2.为串 str 分配空间并赋值
// 2.1 如果常量字符串长度为 0,那么串 str 也该为一个空串
if (len == 0) {
str->ch = NULL;
str->length = 0;
return 1;
}
// 2.2 如果常量字符串长度不为 0,那么将常量字符串中所有字符赋给串 str
else {
// 给串分配 len+1 个存储空间,多分配一个空间是为了存放 '\0' 字符
str->ch = (char *) malloc(sizeof(char) * (len + 1));
// 判断是否分配空间成功
if (str->ch == NULL) {
// 如果分配空间失败,则返回 0
return 0;
} else {
// 如果分配空间成功,则遍历常量字符串中的每个字符,依次赋给串 str
c = ch;
for (int i = 0; i <= len; i++) {// 之所以在循环条件中使用 <=。是为例将常量字符串最后的 '\0' 字符也复制到新串中作为结束标记
str->ch[i] = *(c + i);// 其实也可以使用 str->ch[i]=c[i];
}
str->length = len;
return 1;
}
}
}
int compare(String s1, String s2) {
int i = 0;
while (i < s1.length && i < s2.length) {
if (s1.ch[i] != s2.ch[i]) {
return s1.ch[i] - s2.ch[i];
}
i++;
}
return s1.length - s2.length;
}
int main() {
String str;
// 赋值操作,将一个常量字符串赋给一个串
printf("\n赋值字符串:\n");
assign(&str, "hello world");
printf("%s\n", str.ch);
// 比较两个串
printf("\n比较两个串:\n");
String str2;
assign(&str2, "hellx world!");
int cmp;
cmp = compare(str, str2);
printf("%d\n", cmp);
}
执行结果是:
赋值字符串:
hello world
比较两个串:
Process finished with exit code -1073740940 (0xC0000374)
原因
代码没有任何问题。这段代码的意思是当传入的 str
串存在(不为 NULL
)时那么就释放其空间,然后将指针指向 NULL
。但是在 main
函数中我们是直接声明了一个 String str
就作为参数进行传入了,对于结构体 String
中的属性并没有初始化,需要将 str->ch
初始化为 NULL
,否则在经过这条语句时,就会出现打不开对应内存地址的报错。
解决
在 main 方法中声明 String
类型变量使用时,对其进行初始化:
int main(){
String str;
str.ch=NULL;
str.length=0;
}
我们可以写一个初始化函数 init
来初始化声明的串:
/**
* 初始化串
* @param str 未初始化的串
*/
void init(String *str) {
str->ch = NULL;
str->length = 0;
}
int main(){
String str;
init(&str);
}
正确代码
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char *ch;
int length;
} String;
/**
* 初始化串
* @param str 未初始化的串
*/
void init(String *str) {
str->ch = NULL;
str->length = 0;
}
int assign(String *str, char *ch) {
// 0.参数校验,如果 str 中已有字符,那么释放原串空间,因为我们会给它重新分配空间
if (str->ch) {
free(str->ch);
str->ch = NULL;
}
// 1.统计常量字符串 ch 中的字符个数,只有知道它的字符个数,我们才能清楚为 str 分配多少个字符空间
// 局部变量,存储常量字符串 ch 中的字符个数
int len = 0;
// 注意,我们不能直接操作 ch,因为是一个指针变量,在下面的操作后我们会移动指针,会修改掉 ch 原本的值,后面如果需要再使用就不是传入的参数值,所以要创建一个临时局部变量引用它的值来进行操作
char *c = ch;
// 从头到尾扫描常量字符串,以结束标记 '\0' 作为循环结束条件
while (*c != '\0') {
// 计数器加一
len++;
// 指针加一,继续下一个字符
c++;
}
// 2.为串 str 分配空间并赋值
// 2.1 如果常量字符串长度为 0,那么串 str 也该为一个空串
if (len == 0) {
str->ch = NULL;
str->length = 0;
return 1;
}
// 2.2 如果常量字符串长度不为 0,那么将常量字符串中所有字符赋给串 str
else {
// 给串分配 len+1 个存储空间,多分配一个空间是为了存放 '\0' 字符
str->ch = (char *) malloc(sizeof(char) * (len + 1));
// 判断是否分配空间成功
if (str->ch == NULL) {
// 如果分配空间失败,则返回 0
return 0;
} else {
// 如果分配空间成功,则遍历常量字符串中的每个字符,依次赋给串 str
c = ch;
for (int i = 0; i <= len; i++) {// 之所以在循环条件中使用 <=。是为例将常量字符串最后的 '\0' 字符也复制到新串中作为结束标记
str->ch[i] = *(c + i);// 其实也可以使用 str->ch[i]=c[i];
}
str->length = len;
return 1;
}
}
}
int compare(String s1, String s2) {
int i = 0;
while (i < s1.length && i < s2.length) {
if (s1.ch[i] != s2.ch[i]) {
return s1.ch[i] - s2.ch[i];
}
i++;
}
return s1.length - s2.length;
}
int main() {
String str;
init(&str);
// 赋值操作,将一个常量字符串赋给一个串
printf("\n赋值字符串:\n");
assign(&str, "hello world");
printf("%s\n", str.ch);
// 比较两个串
printf("\n比较两个串:\n");
String str2;
init(&str2);
assign(&str2, "hellx world!");
int cmp;
cmp = compare(str, str2);
printf("%d\n", cmp);
}
执行结果如下:
赋值字符串:
hello world
比较两个串:
-9
Process finished with exit code 0