我的代码中有以下函数来提取名称:
void student_init(struct student *s, char *info) {
char *name;
char *name2;
char a[] = { *info };
name = strtok(a, "/");
strcpy(s->first_name, name);
name2 = strtok(NULL, "/");
strcpy(s->last_name, name2);
}
当我运行时,我看到:
Please enter a student information or enter "Q" to quit.
Daisy/Lee
A student information is read.: Daisy/Lee
Please enter a row number where the student wants to sit.
1
Please enter a column number where the student wants to sit.
2
The seat at row 1 and column 2 is assigned to the student: D . �
?.?. ?.?. ?.?.
?.?. ?.?. D.�.
?.?. ?.?. ?.?.
我正在尝试在c程序中使用strtok函数来分割带有“/”的字符串以分隔名字和姓氏,并将它们存储在学生结构的first_name和last_name变量中。我可以将第一个名称存储在相应的变量中,但正如您从上面链接中的图像中看到的那样,我得到了一个?输出中的符号应该是姓氏的第一个首字母。
答案 0 :(得分:0)
char a[] = { *info };
这是你的问题。这创建的是一个单字节字符数组,其中包含info
的第一个字符,而不包含任何其他内容。
由于strtok
需要字符串,它可能会在该单字节数组的末尾运行并使用内存中发生的任何事情。这就是为什么你看到第一个角色没问题而且没有其他的东西(尽管从技术上来说,作为未定义的行为,字面意思任何是允许发生的。)
不应该构造一个单字节数组,而应该只使用传入的字符串:
name = strtok(info, "/");
您制作本地副本的唯一原因是,不允许更改您要标记的字符串(例如,如果它是字符串文字,或者您希望保留它以供以后使用)。由于您的示例运行显示您正在将读入此字符串,因此它不能是字符串文字。
并且,如果你想保留它以供日后使用,这可能是调用者而不是函数所带来的最佳成本(当关于是否需要所需的信息时,该函数总是复制它是浪费的/ em>或者只有来电者才知道。
为了制作令牌化的副本,它就像:
一样简单char originalString[] = "pax/diablo";
scratchString = strdup(originalString);
if (scratchString != NULL) {
student_init (struct student *s, scratchString);
free (scratchString);
} else {
handleOutOfMemoryIssue();
}
useSafely (originalString);
如果您的实施 strdup
(它是POSIX而不是ISO),请参阅here。
在我看来,“更清洁”的实施将遵循:
void student_init (struct student *s, char *info) {
// Default both to empty strings.
*(s->first_name) = '\0';
*(s->last_name) = '\0';
// Try for first name, exit if none, otherwise save.
char *field = strtok (info, "/");
if (field == NULL) return;
strcpy (s->first_name, field);
// Try for second name, exit if none, otherwise save.
if ((field = strtok (NULL, "/")) == NULL) return;
strcpy (s->last_name, field);
}