C语言 当使用scanf从user读取字符串时,读取位置异常



我试图从用户读取字符串输入,并使用指针将其存储在两个dim数组中。当试图使用这些字符串时,我得到访问违反读取位置异常。首先,我声明了char***,它将存储两个dim数组的指针,然后我使用for循环为每个单元格初始化两个dim数组。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define WORDLEN 80
#define DEFLEN 200
#define KEYVALUE 2
char*** MallocDic(int dictionarySize);
char** MallocDicElement(char* word, char* definition);
void PrintDictionary(char*** dictionary, int dictionarySize);
int main()
{
int dictionarySize;
printf("Please enter dictionary sizen");
scanf("%d", &dictionarySize);
char*** dictionary = MallocDic(dictionarySize);
int i;
for (i = 0; i < dictionarySize; i++) {
char* inputWord = (char*)malloc(WORDLEN * sizeof(char));
char* inputDef = (char*)malloc(DEFLEN * sizeof(char));
if (inputWord == NULL || inputDef == NULL)
{
printf("Failed to allocate memory!n");
exit(1);
}
printf("enter word : n");
scanf("%s", inputWord);
printf("enter definition : n");
scanf("%s", inputDef);
printf("word : %s ,def : %sn", inputWord, inputDef);
//dictionary[i] = MallocDicElement(inputWord, inputDef);
//free(inputDef);
free(inputWord);
}
printf("Print Dictionary : n");
//PrintDictionary(dictionary, dictionarySize);
}
char*** MallocDic(int dictionarySize) {
char*** p;
p = (char***)malloc(dictionarySize * sizeof(char**));
return p;
}
char** MallocDicElement(char* word, char* definition) {
char** p = (char**)malloc(KEYVALUE * sizeof(char*));
int i;
for (i = 0; i < KEYVALUE; i++) {
if (i == 0) {
p[i] = (char*)malloc(WORDLEN * sizeof(char));
p[i] = word;
}
else {
p[i] = (char*)malloc(DEFLEN * sizeof(char));
p[i] = definition;
}
}
return p;
}
void PrintDictionary(char*** dictionary, int dictionarySize) {
int i = 0, j = 0;
for (i = 0; i < dictionarySize; i++) {
for (j = 0; j < KEYVALUE; j++) {
printf("word : %sn", dictionary[i][0]);
printf("definition : %sn", dictionary[i][1]);
}
}
}

在尝试打印第一个字符串时逻辑中断。我错过了什么?

谢谢你的帮助。

至少这些问题。

代码分配内存并将指向该分配的指针保存到p[i],然后在下一行中将指针word复制到p[i]。这将丢失从malloc()返回的指针。

p[i] = (char*)malloc(WORDLEN * sizeof(char));
p[i] = word; // ???

OP更有可能想要复制word指向的字符串到p[i]指向的内存。

p[i] = malloc(WORDLEN);
strcpy(p[i], word);

通常只分配需要的资源。

p[i] = malloc(strlen(word) + 1);
strcpy(p[i], word);

研究strdup().
为了简洁,省略了错误检查。

不要在*scanf()中使用没有宽度"%s","%[]"

限制可接受的输入小于目标数组的大小。

"%s"不读取和保存空间

下面的命令不能读取包含空格的定义。

printf("enter definition : n");
scanf("%s", inputDef);  // Stops after first word

读取非空白后,扫描将在第一个空白处停止。

也许:

scanf(" %199[^n]", inputDef);

检查输入函数的返回值

if (scanf(" %199[^n]", inputDef) != 1) {
Handle_input_error();
}
其他:

避免难以阅读&维护配置

与其强制转换(不需要)和size为类型(在其他地方定义),不如分配为被引用对象的大小——不需要类型出错。

// p = (char***)malloc(dictionarySize * sizeof(char**));
p = malloc(sizeof p[0] * dictionarySize);

更容易正确编码,检查和维护。

冒着这里没有答案的风险(不与您现有的代码一起工作),我想建议您花时间更好地结构您的数据。即使是简单的:

// A `dictionary` is an array of `capacity` entries, `size` of which are in use.
// Elements are kept in lexicographical order.
struct dictionary
{
struct entry
{
const char * word;
const char * definition;
};
struct entry * entries;
size_t         size;
size_t         capacity;
};
typedef struct dictionary dictionary;

这使得处理事情变得简单了无数倍。现在可以创建两个有用的函数:

dictionary * new_dictionary( size_t capacity );
void free_dictionary( dictionary * dict );

这种结构化的特性使得管理单个部件更加容易。特别是,您的用户可以将指针传递给字典,而不必担心它会改变。例如,假设您想要更新字典的容量:

void set_dicitionary_capacity( dictionary * dict, size_t new_capacity )
{
if (new_capacity < dict->size) return;
struct entry * new_entries = realloc( dict->entries, new_capacity * sizeof dict->entries[0] );
if (!new_entries) return;
dict->capacity = new_capacity;
dict->entries  = new_entries;
}

使用函数与不透明字典对象进行接口的想法是基本数据封装的基础。这样做使使用代码更容易:

dictionary * words = new_dictionary( 1000 );
if (!words) fooey();
update_dictionary( words, "hello", "a greeting" );
update_dictionary( words, "world", "the Earth; a planet; any organism’s collective society" );
printf( "There are %zu words in the dictionary.n", dictionary_size( words ) );
const char * desc = find_word( words, "there" );
printf( "Obi Wan can%s use this dictionary.n", desc ? "" : "not" );
free_dictionary( words );

希望我们已经看到在每个层面上事情是如何更容易理解的。换句话说,以这样一种方式编写代码,使意义和结构尽可能清晰。这有助于减少我们在编写代码时糊涂的头脑可能产生的失败数量。

最新更新