这是一个简单的save_as()
功能:
gint save_as(GtkWidget *parent, struct buffers B)
{
GtkWidget *file_chooser = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_OK, NULL);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), "Untitled");
gint response = gtk_dialog_run(GTK_DIALOG(file_chooser));
switch(response)
{
case GTK_RESPONSE_OK:
GFile *file = g_file_new_for_path(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)));
GtkTextIter *start;
gtk_text_buffer_get_start(B.buffer0, &start);
GtkTextIter *end;
gtk_text_buffer_get_end(B.buffer0, &end);
// program abnormally terminates on the following line
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
g_file_replace_contents(file, contents, strlen(contents), NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
g_free(contents);
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_OK;
break;
case GTK_RESPONSE_CANCEL:
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
// user pressed X
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
这几乎是我们诊断和解决问题所需的全部内容。
这是我尝试单击文件->另存为... 然后单击保存时得到的完整消息:
(test:4478): Gtk-WARNING **: 11:56:20.184: Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixbufs/widgets in the buffer have been modified since the iterator was created.
You must use marks, character numbers, or line numbers to preserve a position across buffer modifications.
You can apply tags and insert marks without invalidating your iterators, but any mutation that affects 'indexable' buffer contents (contents that can be referred to by character offset) will invalidate all outstanding iterators
(test:4478): Gtk-CRITICAL **: 11:56:20.184: gtk_text_buffer_get_text: assertion 'gtk_text_iter_get_buffer (start) == buffer' failed
Segmentation fault (core dumped)
由于某种原因,迭代器start
和end
似乎不属于B.buffer0
。除了GTK+3 documentation外,我还按照this question的答案作为指导。
为什么会发生这种情况以及如何解决?
gtk_text_buffer_get_start_iter(B.buffer0, &start);
至gtk_text_buffer_get_start_iter(B.buffer0, start);
gtk_text_buffer_get_start_iter(B.buffer0, &end);
至gtk_text_buffer_get_start_iter(B.buffer0, end);
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
至gchar *contents = gtk_text_buffer_get_text(B.buffer0, start, end, FALSE);
我得到的唯一错误是:
Segmentation fault (core dumped)
这也应该是根据documentation发送参数的正确方法。
此外,我尝试将contents = gtk_text_buffer_get_text(B.buffer0, start, end, FALSE);
替换为contents = gtk_text_iter_get_text(start, end);
,但遇到相同的错误。
我还注意到,应用这些更改后,在编译期间会收到两个警告:
src/save.c:96:5: warning: ‘start’ may be used uninitialized in this function [-Wmaybe-uninitialized]
gtk_text_buffer_get_start_iter(B.buffer0, start);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/save.c:98:5: warning: ‘end’ may be used uninitialized in this function [-Wmaybe-uninitialized]
gtk_text_buffer_get_end_iter(B.buffer0, end);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这显然意味着gtk_text_buffer_get_start/end_iter()
无法将start
和end
与B.buffer0
相关联,但是为什么呢?它说start
和end
是未经初始化传递的,但是由于我现在正在初始化它们,这不是正常的吗?还是在那之前我应该做点什么?
buffer.h
的文件中。在这里(不是整个文件,只是结构定义):
struct buffers
{
GtkTextBuffer *buffer0;
GtkTextBuffer *buffer1;
GtkTextBuffer *buffer2;
};
我使用的实例是:
struct buffers Buffer;
这是一个全局变量,因此每个使用buffer.h
的文件都包含Buffer
。并且出于相同的目的(全局性),当然在与定义它的源文件相关联的头文件中将其声明为extern struct buffers Buffer;
。
此缓冲区像这样传递给函数save_as()
:
gint response = save_as(main_window, Buffer);
如果您想知道为什么我不使用全局变量而不是将其作为参数传递,那是因为我可以将任何类型为struct buffers
的变量传递给{{1 }}并让它完成工作,而不是为每个save_file()
变量都提供另一个功能。
struct buffers
所有内容编译时都不会发出任何警告。不幸的是,这不能解决问题。单击文件->另存为,然后单击保存,程序退出,我收到消息:
GtkTextIter *start = NULL;
GtkTextIter *end = NULL;
为什么至少在前两种情况下,我的(test:4081): Gtk-CRITICAL **: 19:48:29.843: gtk_text_buffer_get_start_iter: assertion 'iter != NULL' failed
(test:4081): Gtk-CRITICAL **: 19:48:29.844: gtk_text_buffer_get_end_iter: assertion 'iter != NULL' failed
(test:4081): Gtk-CRITICAL **: 19:48:29.844: gtk_text_buffer_get_text: assertion 'start != NULL' failed
和start
迭代器是end
才显得重要?这两个函数不是应该分别将NULL
和start
迭代器设置为文件的开头和结尾吗?因此,为什么在此之前设置哪些迭代器很重要?
答案 0 :(得分:0)
最后解决了。看来GtkTextIter
与大多数GTK类型有点不同。
这是应该怎么做:
gint save_as(GtkWidget *parent, struct buffers B)
{
GtkWidget *file_chooser = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_OK, NULL);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), "Untitled");
gint response = gtk_dialog_run(GTK_DIALOG(file_chooser));
switch(response)
{
case GTK_RESPONSE_OK:
GFile *file = g_file_new_for_path(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)));
GtkTextIter start;
gtk_text_buffer_get_start(B.buffer0, &start);
GtkTextIter end;
gtk_text_buffer_get_end(B.buffer0, &end);
// program abnormally terminates on the following line
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
g_file_replace_contents(file, contents, strlen(contents), NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
g_free(contents);
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_OK;
break;
case GTK_RESPONSE_CANCEL:
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
// user pressed X
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
如您所见,此代码与我的问题中的代码唯一的区别是以下两行:
错误
GtkTextIter *start;
GtkTextIter *end;
右
GtkTextIter start;
GtkTextIter end;
大多数GTK类型都希望您将它们作为指针,但这是一个例外。