我正在Linux的终端上学习C,并且我在文本上做了一个小游戏,所以一切正常(特别是我的大循环,如果玩家想重启游戏),我只是添加了一个转储“在开头输入您的伪”。
输入后它不起作用,它说
Segmentation Error , core dumped
这里是所有代码,最重要的是前几行,因为其余所有代码均运行正常:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
int main (void) {
// déclaration des variables
bool essais8 = false ;
bool essais10 = false ;
bool essais12 = false;
bool essais14 = false;
bool condition = true;
bool condition_diff = false;
bool condition_recommencer = false;
bool condition_bonus1 = true;
bool condition_bonus2 = true;
bool condition_bonus3 = true;
char* buffer;
char* recommencer;
char* pseudo ;
size_t bufsize = 32;
size_t characters;
int difficulte;
int sheitan = 0;
int sheitan2 = 0;
int a;
int bonus1;
int bonus2;
int bonus3;
int ecart;
int essais;
int nb_bonus2;
srand(time(NULL));
// on récupère le pseudo du joueur
printf("Salut ! Quel est ton nom ? ");
characters = getline(&buffer,&bufsize,stdin);
pseudo = buffer;
// after this I have the segmentation errror
while (condition == true) { // Boucle de recommencement du Jeu
// here is the big game that was functioning perfectly before I add the pseudo option on top.
}
printf("Fin du programme \n");
return(0);
}
答案 0 :(得分:2)
您忘记初始化buffer
。而且,您最好将printf(3)格式的控制字符串以\n
结尾,或者在任何输入之前调用fflush(3)(因为stdio(3)被缓冲了)。
未初始化的指针包含垃圾。使用undefined behavior。成为scared!
所以我的建议是编码
size_t bufsiz = 64;
char *buffer = malloc(bufsiz);
if (!buffer) { perror("malloc buffer"); exit(EXIT_FAILURE); };
及以后的
printf("Salut ! Quel est ton nom ?\n");
characters = getline(&buffer,&bufsize,stdin);
下一次在编译时启用所有警告和调试信息,因此如果使用with,则编译代码GCC gcc -Wall -Wextra -g
。您会得到一些警告。
当然,请阅读How to debug small programs和debugging with GDB
在Linux上,根据您的特定情况,您可能会对使用readline(3)而不是getline(3)感兴趣。
请不要忘记阅读所使用的每个函数的文档(例如,here,如果它是标准的C语言)。
答案 1 :(得分:2)
getline()
可以以两种不同方式使用:
要使用1.初始化传递的指针以指向有效内存,并传递有效内存的大小。
#define INITIAL_SIZE (42)
int main(void)
{
char * buffer = malloc(INITIAL_SIZE * sizeof *buffer);
size_t bufsize = INITIAL_SIZE;
ssize_t characters = getline(&buffer, &bufsize, stdin);
if (-1 == characters)
{
/* handle error. */
}
else
{
/* use buffer */
}
free(buffer); /* Free buffer. */
}
要使用2.初始化传递给NULL
的指针,并传递0
作为大小。
int main(void)
{
char * buffer = NULL;
size_t bufsize = 0;
ssize_t characters = getline(&buffer, &bufsize, stdin);
if (-1 == characters)
{
/* handle error. */
}
else
{
/* use buffer */
}
free(buffer); /* Free buffer. */
}
注意:getline()
返回ssize_t
不是 size_t
。
答案 2 :(得分:1)
'buffer'变量指向一些垃圾内存地址。您需要先使用“ malloc”函数分配必要的内存,或者将“缓冲区”设置为静态数组而不是指针。
答案 3 :(得分:0)
getline()
是POSIX.1 function,它将行读入动态分配的缓冲区,从而允许任何长度的行(仅受可用内存量限制)。它返回读取的字符数,如果没有更多输入或发生错误,则返回-1。
以下是一种使用模式示例:
char *line = NULL;
size_t size = 0;
ssize_t len;
while (1) {
len = getline(&line, &size, stdin);
if (len < 1)
break;
/* Do something with the input line */
}
您随时可以使用以下方式释放动态分配的缓冲区
free(line);
line = NULL;
size = 0;
您想要清除指向NULL且大小为零的指针的原因是那样,您不会意外尝试访问已释放的内存,但是您可以调用getline(&line, &size, handle)
来读取更多行会简单地识别出它没有缓冲区,并会分配一个新的缓冲区。
如果您小心的话,可以以任何希望的方式操作动态数据。例如:
while (1) {
char *line = NULL;
size_t size = 0;
ssize_t len;
len = getline(&line, &size, stdin);
if (len < 1) {
free(line);
break;
}
/* Do something with the contents of the line */
free(line);
}
可以工作,但是速度会很慢,因为C库将对每次读取的行至少执行一次malloc()
调用,并可能根据行的长度进行额外的realloc()
调用。 / p>
按原样编写getline()
的原因是,它允许对任意数量的行重用同一缓冲区。如果顺序读取文件,则可以重复使用相同的缓冲区。让我们看一个更复杂的示例:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
int main(int argc, char *argv[])
{
unsigned long linenum;
char *line = NULL, *in, *out, *end;
size_t size = 0, n;
ssize_t len;
FILE *src;
int arg;
if (!setlocale(LC_ALL, ""))
fprintf(stderr, "Warning: Your C library does not support your current locale.\n");
if (argc < 2) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s FILENAME [ FILENAME ... ]\n", argv[0]);
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
for (arg = 1; arg < argc; arg++) {
src = fopen(argv[arg], "r");
if (!src) {
fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
free(line);
exit(EXIT_FAILURE);
}
linenum = 0;
while (1) {
len = getline(&line, &size, src);
if (len < 1)
break;
linenum++;
/* First character in the line read: */
in = line;
out = line;
/* Pointer to the end-of-string character on the line: */
end = line + len;
/* Skip all leading whitespace characters. */
while (in < end && isspace((unsigned char)(*in)))
in++;
/* Character copy loop. */
while (in < end)
if (isspace((unsigned char)(*in))) {
/* Replace consecutive whitespace characters with spaces. */
*(out++) = ' ';
do {
in++;
} while (in < end && isspace((unsigned char)(*in)));
} else {
/* Copy all other characters as-is. */
*(out++) = *(in++);
}
/* There may be a single space before out. Backtrack it, if so. */
if (out > line && out[-1] == ' ')
out--;
/* Mark the end of the string at out. */
*out = '\0';
/* Calculate the new length, just for output purposes. */
n = (size_t)(out - line);
/* Print the line. */
printf("%s: Line %lu: '%s' (%zu of %zd characters)\n",
argv[arg], linenum, line, n, len);
}
if (!feof(src) || ferror(src)) {
fprintf(stderr, "%s: Read error.\n", argv[arg]);
fclose(src);
free(line);
exit(EXIT_FAILURE);
}
if (fclose(src)) {
fprintf(stderr, "%s: Error closing file: %s.\n",
argv[arg], strerror(errno));
free(line);
exit(EXIT_FAILURE);
}
}
free(line);
line = NULL;
size = 0;
return EXIT_SUCCESS;
}
如果我们将上述内容另存为 example.c ,然后使用例如gcc -Wall -O2 example.c -o example
,然后运行提供文本文件名称作为参数的程序,例如./example example.c
,它将输出类似
example.c: Line 1: '#define _POSIX_C_SOURCE 200809L' (31 of 33 characters)
example.c: Line 2: '#include <stdlib.h>' (19 of 20 characters)
example.c: Line 3: '#include <locale.h>' (19 of 20 characters)
example.c: Line 4: '#include <string.h>' (19 of 20 characters)
example.c: Line 5: '#include <stdio.h>' (18 of 19 characters)
example.c: Line 6: '#include <ctype.h>' (18 of 19 characters)
example.c: Line 7: '#include <errno.h>' (18 of 19 characters)
example.c: Line 8: '' (0 of 1 characters)
example.c: Line 9: 'int main(int argc, char *argv[])' (32 of 33 characters)
该程序的作用是简单地逐行读取每个指定的文件,删除每行上的任何前导和尾随空格,并将所有连续的空格合并为一个空格。较小的字符数是剩余(和显示)的字符数,较大的数字是从文件读取的原始字符数。
示例程序的其他注释(如果您感兴趣的话)
setlocale(LC_ALL, "")
调用告诉您的C库使用用户区域设置(通常在LANG
或LC_ALL
环境变量中定义)。该程序仅对当前语言环境使用的字符集使用字符类型定义(以确定哪些代码为“空白”),因此也可以通过setlocale(LC_CTYPE, "")
将其限制于此。如果C库不支持当前语言环境,则调用将返回NULL。通常这是由于用户配置错误所致,因此,程序随后发出警告会很有用。
isspace()
(以及is*()
中定义的所有其他<ctype.h>
函数)采用未签名的字符代码(或EOF)。由于char
类型可以是有符号的或无符号的,因此在提供给函数之前,我们将字符显式转换为(unsigned char)
。考虑一下我们只需要用这种方式处理的这个愚蠢的历史包.。
由于line
指向动态分配的内存缓冲区的开头,因此我们不能修改它(通过realloc()
或free()
除外,然后将其设置为{{1 }})。如果我们确实对其进行修改,则随后使用该指针进行的任何NULL
或getline()
调用都可能会吓坏并导致程序崩溃,因为它们确实需要指针指向缓冲区的开头,而不仅仅是里面的某个地方。
我喜欢使用指针(free()
)而不是索引。在这里,char *in, *out, *end
从in
开始,一直上升到但不包括line
,在此处line+len
将字符串末尾nul getline()
放入指示行尾。这就是为什么我也经常使用名为\0
的指针来指向它的原因。 end
也从out
开始,但仅当字符排成一行时才增加。
如果您想到一排字母拼字游戏,例如在拼字游戏中,line
指向您将放置下一个瓷砖的位置,而out
则指向您获得的下一个瓷砖。 / p>
当in
或getline()
返回零或负值(或getdelim()
返回NULL)时,这意味着没有更多数据要读取,或者操作因其他原因失败。
循环后,fgets()
检查输入流是否被完全读取而没有错误。恰恰相反,相反:只有在发生错误或未读取整个文件时,表达式才为真。
如果我已将数据写入某个文件,例如(!feof(src) || ferror(src))
,则通常在此测试之前进行FILE *dst
测试。如果在将C库缓冲的最后一个数据写入文件时出错,则为true。
if (fflush(dst))
关闭文件。我个人更喜欢验证其返回值,因为即使当前只能在非常特殊的情况下失败,但作为用户,我绝对希望知道OS在写入数据方面是否存在问题!测试基本上不花费任何费用,但对用户而言可能至关重要。在处理数据时,我不希望任何程序“忘记”告诉我有问题。我的数据对我很重要。
fclose(src)
是安全的,什么也不做。 (此外,free(NULL)
等效于realloc(NULL, size)
,因此,如果您初始化指向NULL的指针,则不需要初始的malloc,您可以将malloc(size)
始终设置为所需的大小。)我建议您使用上面的代码。您甚至可以在ltrace(realloc()
)下运行它,以查看实际执行了哪些标准C库调用及其结果。或在strace(ltrace ./example example.c
下)查看系统调用(从进程到操作系统内核)。
例如,您可以添加“说”
strace ./example example.c
在 if (linenum == 7) {
/* We skip line 7, and even destroy it! Muahhahah! */
free(line);
line = NULL;
size = 0;
continue;
}
行之后,查看文本文件的第七行会发生什么。 (它们被跳过,即使释放了缓冲区,也不会发生任何不良情况(因为linenum++
开始了while循环主体的下一次迭代),因为下一个continue
会动态分配新行
如果您决定要保留行的一部分副本,只需计算需要为字符串末尾nul(getline()
添加一个的长度,为副本重复分配那么多字符( \0
始终在C中;因此,malloc()等人确实获取要分配的字符数),sizeof (char) == 1
数据,并添加终止nul。例如,
memcpy()
如果需要完整的字符串(直到字符串nul的结尾),则可以使用POSIX.1-2008 strdup()
。