当我运行后验函数时,函数中间的 Segfault

Segfault in the middle of a function when I run a posterior function

提问人:Fernando Loula 提问时间:9/3/2023 最后编辑:bartekczrnFernando Loula 更新时间:9/5/2023 访问量:48

问:

我的练习是将字符串的解析器写成英文数字的书面形式,我可以毫无问题地得到这个数字,并以我们选择存储的格式将其写回解析。但是,如果在打印后我调用另一个函数,则在打印过程中会出现段错误。 这

int     main(int argc, char *argv[])
{
    char *digits; 
    char *dictionary;

    read_input(&digits, &dictionary, argc, argv);
    check_stored_numbers();
}

打印此

c2r6p4% ./a.out 24234

exp 0  units 234
exp 1  units 24% 

int     main(int argc, char *argv[])
{
    char *digits; 
    char *dictionary;

    read_input(&digits, &dictionary, argc, argv);
    check_stored_numbers();
    load_dict(dictionary);
}

打印此内容:

c2r6p4% ./a.out 24234 
exp 0  units 234 
zsh: segmentation fault (core dumped)  ./a.out 24234

具体来说,解析后的数字将转到全局变量数组,该数组由全局指针标记。我试图以逐步的方式隐藏许多特定部分,但最终归结为这个问题。我将尝试将代码放在下一个会话中,我知道有很多糟糕的代码、人为的解决方案和需要改进的一般内容。但是我需要特别了解为什么未来的函数调用能够对以前运行的程序进行隔离。 此时我正在运行 Ubuntu,使用 cc * -Wall -Werror -Wextra 进行编译。

#include "functions.h"

int char_to_digit(char c)
{
    return (c - '0');
}



#include "functions.h"

void check_only_digits(char *digits)
{
    while (*digits != '\0')
    {
        if ((*digits >= '0') && (*digits <= '9'))
        {
            digits++;
        }
        else
        {
            ft_putstr("error");
            exit (1) ;
        }
    }
    return ;
}

#include "functions.h"

void free_mem(void *pointer)
{
    if (pointer != NULL)
        {
            free(pointer);
        }
}

#include "functions.h"

void    ft_openfile()
{
    char *conteudo = NULL;
    int arquivo;
    ssize_t tamanho;
    
    arquivo = open ("numbers.dict", O_RDONLY);
    if (arquivo == -1)
    {
        perror("Erro ao abrir arquivo");
        exit (1) ;
    }
    tamanho = lseek(arquivo, 0 , SEEK_END);
    conteudo = (char *)malloc(tamanho + 1);
    if (conteudo == NULL)
    {
        perror("Erro ao alocar memoria");
        close(arquivo);
        exit(1);
    }
    lseek(arquivo, 0, SEEK_SET);
    ssize_t bytes = read(arquivo, conteudo, tamanho);
    if (bytes == -1)
    {
        perror("Erro ao alocar memoria");
        free(conteudo);
        close(arquivo);
        exit (1) ;
    }
    conteudo[tamanho] = '\0';
    printf("arquivo:\n%s", conteudo);
    free(conteudo);
    close(arquivo);
}

#include "functions.h"

void    ft_putchar(char c)
{
    write(1, &c, 1);
}

void ft_putnbr(int nb)
{
    long    number;
    
    number = (long)nb;
    if (number < 0)
    {   ft_putchar('-');
        number = number * -1;
    }

    if (number < 10)
    {
        ft_putchar(number + '0');
    }

    if (number >= 10)
    {
        ft_putnbr(number / 10);
        ft_putnbr(number % 10);
    }
}

#include "functions.h"

void    ft_putstr(char (*str))
{
    int i;

    i = 0;
    while (str[i] != 0)
    {
        write(1, &str[i], 1);
        i++;
    }
}

#include "functions.h"

char    *ft_strcpy(char *dest, char *src)
{   
    int count;

    count = 0;
    while (src[count] != '\0')
    {       
        dest[count] = src[count];
        count++;
    }
    dest[count] = '\0';
    return (dest);
}


#include "functions.h"

int ft_strlen(char (*str))
{
    int i;

    i = 0;
    while (str[i] != '\0')
    {
        i++;
    }
    return (i);
}

#ifndef FUNCTIONS_H
#define FUNCTIONS_H

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "types.h"


void    ft_putstr(char (*str));
void    ft_putchar(char c);
int     ft_strlen(char (*str));
void    ft_putnbr(int nb);
char    *ft_strcpy(char *dest, char *src);
void    check_only_digits(char *digits);
void    read_input_dict(char **dictionary, char *argv);
void    read_input_digits(char **digits, char *argv);
void    read_input(char **digits, char **dictionary, int argc, char **argv);
void    free_mem(void *pointer);
int     parse_blocks(char *digits);
void    str_end(char **str);
int     char_to_digit(char c);
void    store_user_numbers(char *digits);
int     parse_number(char **read_digits, char *digits);
void    load_dict(char *dict);

#endif



#include "types.h"

dict_num    *g_dict;
parsed_num *g_user_numbers;
int g_blocks = 1;

#include "types.h"

#ifndef GLOBALS_H
#define GLOBALS_H

extern dict_num    *g_dict;
extern parsed_num *g_user_numbers;
extern int g_blocks;

#endif



#include "functions.h"
#include "globals.h"

void    load_dict(char *dict)
{
    ft_strlen(dict);
    g_dict = malloc(sizeof(dict_num) * 41);

    g_dict[0].number.exponent = 0;
    g_dict[0].number.units = 0;
    g_dict[0].name = "zero";

    // a large number of lines

    g_dict[40].number.exponent = 36;
    g_dict[40].number.units = 1;
    g_dict[40].name = "undecillion";
}



#include "functions.h"
#include "globals.h"

void    check_stored_numbers()
{
    for(int i = 0; i < g_blocks; i++)
    {
        printf("\nexp %i  units %i", g_user_numbers[i].exponent, g_user_numbers[i].units);
    }
}

void    check_stored_dict()
{
    for(int i = 0; i < 41; i++)
    {
        printf("exp %i  units %i  name %s\n", g_dict[i].number.exponent, g_dict[i].number.units, g_dict[i].name );
    }
}

int     main(int argc, char *argv[])
{
    char *digits; 
    char *dictionary;

    read_input(&digits, &dictionary, argc, argv);
    check_stored_numbers();
    //load_dict(dictionary);
    
    //check_stored_dict();
    //free(dictionary);
    //free(digits);
    //free(g_user_numbers);
}




#include "functions.h"

int parse_blocks(char *digits)
{
    int number_of_digits;
    int number_of_blocks;

    number_of_digits = ft_strlen(digits);
    number_of_blocks = number_of_digits / 3;
    if ((number_of_digits % 3) != 0)
        number_of_blocks++;
    return (number_of_blocks);
}



#include "functions.h"

int power(int base, int exp)
{
    if (exp == 0)
        return (1);
    if (exp == 1)
        return (base);
    return (base * power(base, exp - 1));
}

int parse_number(char **read_digits, char *digits)
{
    int return_value;
    int count;

    count = 3;
    return_value = 0;
    while ((*read_digits != digits - 1) && (count >= 1))
    {
        return_value = return_value +(power(10, 3 - count) * (char_to_digit(**read_digits)));
        (*read_digits)--;
        count--;
    }
    return (return_value);
}



#include "functions.h"

void read_input(char **digits, char **dictionary, int argc, char **argv)
{
        if (argc < 2 || argc > 3)
    {
        ft_putstr("error");
        exit (1) ;
    }

    if (argc == 2)
    {
        read_input_digits(digits, argv[1]);
    }
    else
    {
        read_input_dict(dictionary, argv[1]);
        read_input_digits(digits, argv[2]);
    }
}



#include "functions.h"

void read_input_dict(char **dictionary, char *argv)
{
    *dictionary = malloc ((ft_strlen(argv)+1) * sizeof(char));
    *dictionary = ft_strcpy(*dictionary, argv);
    //ft_putstr(*dictionary);
}



#include "functions.h"

void read_input_digits(char **digits, char *argv)
    {
        *digits = malloc((ft_strlen(argv)+1) * sizeof(char));
        *digits = ft_strcpy(*digits, argv);
        check_only_digits(*digits);
        store_user_numbers(*digits);
        //ft_putstr(*digits);
        //ft_putchar('\n');
    }
    


#include "functions.h"
#include "globals.h"

void store_user_numbers(char *digits)
{
    int count_blocks, count_digits, user_number;
    char *read_digits;

    read_digits = digits;
    count_digits = 0;
    count_blocks = 0;
    g_blocks = parse_blocks(digits);
    g_user_numbers = malloc(sizeof(parsed_num) * g_blocks);
    str_end(&read_digits);
    while (count_blocks < g_blocks)
    {
        g_user_numbers[count_blocks].exponent = count_blocks;
        g_user_numbers[count_blocks].units = parse_number(&read_digits, digits);
        count_blocks++;
    }
}



#include "functions.h"

void str_end(char **str)
{
    while(**str != '\0')
    {
        (*str)++;
    }
    (*str)--;
}



#ifndef TYPES_H
#define TYPES_H

typedef struct{
    int exponent;
    int units;
}parsed_num;

typedef struct{
    parsed_num number;
    char *name;
}dict_num;

#endif     
c 解析 分段故障 全局变量

评论

1赞 Craig Estey 9/3/2023
旁注:在顶部只做一次#include "functions.h"
1赞 Craig Estey 9/3/2023
创建一个拥有一切(并且没有的单曲。它应该干净地编译。编辑您的问题并将其发布在底部的代码块中。除了问题之外,底部的代码块是否已经存在?如果我们下载了该块并按照我的描述构建并运行它,它会像您描述的那样出错吗?.c#include "functions.h"#include
1赞 Craig Estey 9/3/2023
另一种调试方法是正常编译并重新运行。这可能有助于找到程序出错的确切位置-fsanitize=address
1赞 Fe2O3 9/3/2023
请原谅我这么说,但这是将函数荒谬地分散到单个文件中......看。。。没有读者愿意去寻找执行这种基本操作的另一个函数。标准库头文件(间接)包含在每个源文件中。你为什么要写自己的版本?例如,不会从此代码中的任何位置调用。 做的不仅仅是打开一个文件...char_to_digit()free_mem()ft_openfile()
2赞 Fe2O3 9/3/2023
"是玩具问题“当然是!使用roll-y'r-own替换标准库函数简直是愚蠢的(不应该教,imo)。对参数计数或行数 # 的规定限制也是愚蠢的。优先级 #1 应该是干净正确的代码。如果一个函数可以用 31-32 行干净利落地写出来,那么 30 行的限制意味着代码将不得不不那么清晰。PS:在英语中,=>“四十二”。在德语中,==> “Zwei (two) UND Vierzig (forty)”......很难使一个算法真正国际化(使用外部词典)......(印度的数字系统走得更远!4242

答:

2赞 NicknEma 9/5/2023 #1

printf其余的标准 I/O 函数是缓冲的,这意味着当您调用它们时,它们实际上不会立即打印所有内容。由于 I/O 操作非常慢,因此它们会将内容累积到内部缓冲区中,并且仅在它们有足够的数据(或明确告诉它们时)才打印。 我不知道这是否完全是你的问题(你调用特定于 Linux 的函数而我在 Windows 上),但你可以尝试两件事:

  • 转换为 ,因为 stderr 未缓冲。printf(...)fprintf(stderr, ...)
  • 之后调用(这是您明确告诉它立即打印的方式)fflush(stdout)printf

如果我的假设是正确的,那么这两件事中的任何一件都应该使程序在实际崩溃的地方崩溃,所以在.ft_strlen

P.S. 我不认为编写自己的标准函数版本是愚蠢的,除非你很着急。这是一个很好的练习,即使他们的功能变得更糟。我记得这个缓冲功能的主要原因是,上次我编写自己的打印函数来单独打印每个字符时,我体验到了它令人难以置信的缓慢 - 打印一条简单的消息比整个软件渲染例程慢!经常重写东西,我的朋友。不一定是要重新发明轮子,而是要理解为什么正方形不起作用。

评论

0赞 Fernando Loula 10/23/2023
这个答案真的很神奇,也帮助我理解了我陷入的那种问题。非常感谢,真的很有帮助!