LINUX.ORG.RU

Язык C, ошибка сегментирования.

 ,


3

3

Суть задания в том, что нужно написать программу, которая будет выводить текущее время римскими символами. Попытался проверить работоспособность того, что уже написал - код компилируется нормально, но при запуске программы выдает сообщение «Ошибка сегментирования (сделан дамп памяти)». Ошибка, если я правильно понял, происходит в функции «convert_dec_to_roman», но в чем конкретно я ошибся, понять не могу, поэтому прошу помощи. Заранее спасибо.

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/** convert_dec_to_roman -- преобразует целое число в строковое представление
 * 
 * Параметры:
 *   roman - строка для записи результата преобразования
 *   decimal - исходное целое
 *   n - ограничение на длину результата
 * Возвращаемое значение:
 *   функция возвращает -1 при любой ошибке
 */
int convert_dec_to_roman (char *roman, unsigned int decimal, size_t n);

/** get_roman_date -- преобразует дату в строковое представление
 * 
 * Параметры:
 *   romandate - строка для записи даты римскими числами
 *   now - структура времени
 *   n - ограничение на длину результата
 * Возвращаемое значение:
 *   функция возвращает -1 при любой ошибке
 */
int get_roman_date (char *romandate, struct tm *now, size_t n);

int main (int argc, char *argv[])
{
    struct tm *date;
    int error_check = 0;
    char romandate [100];
    int size = sizeof (romandate);

    error_check = get_roman_date (romandate, date, size);

    if (error_check == 0) {
        printf("%s\n", romandate);
    } else {
        exit (EXIT_FAILURE);
    }

    return EXIT_SUCCESS;
}

int convert_dec_to_roman (char *roman, unsigned int decimal, size_t n)
{
    const int arabar[]  = {  1,   4,    5,   9,    10,  40,  50,   90,  100, 400,  500, 900,  1000};
    const char *romanar[] = { "I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M"};

    int m = sizeof(arabar)/sizeof(int)-1;
    int i, z;

    i = 0;
    z = m;
    while(decimal > 0) {
        if(decimal >= arabar[z]) {
	    
            roman[i++] = *romanar[z];
            decimal -= arabar[z];

        } else {
            z--;
        }
    }

    if(i > n) {
        return -1;
    } else {
        return 0;
    }
}



int get_roman_date (char *romandate, struct tm *now, size_t n)
{
    int error_check = 0;

    error_check = convert_dec_to_roman (romandate, now->tm_wday, n);








    return error_check;
}




Ответ на: комментарий от powerguy

Вот что выдает перед тем как упасть:

#0 0x00000000004007fa in convert_dec_to_roman (roman=0x7fffffffe440 'M' <repeats 200 times>..., decimal=4157714112, n=100) at romandate.c:68

#1 0x0000000000400878 in get_roman_date (romandate=0x7fffffffe440 'M' <repeats 200 times>..., now=0x7fffffffe450, n=100) at romandate.c:89

#2 0x0000000000400648 in main (argc=1, argv=0x7fffffffe5a8) at romandate.c:44

DBSR ()

Начни с вывода значений z и i в цикле while(decimal ...).

invy ★★★★★ ()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от RisuX3

тоже вариант. мне обычно через gdb проще посмотреть простой код

powerguy ★★★ ()

GCC ругается, что переменная data не инициализирована.

Может вместо

struct tm *date;
ты на самом деле хотел
    time_t temp = time(NULL);
    struct tm *date=localtime(&temp);

RisuX3 ()

И кто будет инициализировать date? Просто везуха, что раньше не падает.

int main (int argc, char *argv[])
{
    struct tm *date;
    int error_check = 0;
    char romandate [100];
    int size = sizeof (romandate);

    error_check = get_roman_date (romandate, date, size);

И какова размера должен быть буфер если на вход decimal бред в пару миллиардов может быть?

int convert_dec_to_roman (char *roman, unsigned int decimal, size_t n)
io ★★ ()
Ответ на: комментарий от Harald

До этого про backtrace не знал. Сейчас вывел, вроде бы сам нашел, где у меня ошибка.

#0 0x00000000004007fa in convert_dec_to_roman (roman=0x7fffffffe440 'M' <repeats 200 times>..., decimal=4157714112, n=100) at romandate.c:68 arabar = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000}

romanar = {0x400904 «I», 0x400906 «IV», 0x400909 «V», 0x40090b «IX», 0x40090e «X», 0x400910 «XL», 0x400913 «L», 0x400915 «XC», 0x400918 «C», 0x40091a «CD», 0x40091d «D», 0x40091f «CM», 0x400922 «M»}

m = 12 i = 3009 z = 12

#1 0x0000000000400878 in get_roman_date (romandate=0x7fffffffe440 'M' <repeats 200 times>..., now=0x7fffffffe450, n=100) at romandate.c:89 error_check = 0

#2 0x0000000000400648 in main (argc=1, argv=0x7fffffffe5a8) at romandate.c:44 date = 0x7fffffffe450 error_check = 0 romandate = 'M' <repeats 100 times> size = 100

Т.е., как уже сказали выше, идет переполнение массива roman. Происходит это из-за того, что decimal имеет значение 4157714112, что заставляет цикл выполняться слишком больше количество раз. А из-за чего такое значение появляется?

DBSR ()
-while(decimal >0)
+while(decimal >0 && i< 100)
dron@gnu:~$./a.out
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
dron@gnu:~$ 
Dron ★★★★★ ()
Ответ на: комментарий от DBSR

Используй флаги -Wall -Werror -Wextra при компиляции, и больше не будет таких вопросов.

RisuX3 ()

А с каких пор в сишечке, можно строки присваивать, да ещё и символам?

//...
roman[i++] = *romanar[z]; //или это какой то мего трюк?
//...

В конце массива, нигде не записывается нулевой символ...

Ну и да, нет проверки за границы выхода массива, но падает оно конкретно в это случае не тут.

pon4ik ★★★★★ ()
$ clang -fsanitize=address -O1 -g a.cpp 
$ ./a.out 
=================================================================
==912==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff2e732444 at pc 0x0000004b839f bp 0x7fff2e732390 sp 0x7fff2e732388
WRITE of size 1 at 0x7fff2e732444 thread T0
    #0 0x4b839e in convert_dec_to_roman(char*, unsigned int, unsigned long) /tmp/a.cpp:59:29
    #1 0x4b81a2 in main /tmp/a.cpp:35:21
    #2 0x7fb41c790a64 in __libc_start_main (/lib64/libc.so.6+0x21a64)
    #3 0x4b805c in _start (/tmp/a.out+0x4b805c)

Address 0x7fff2e732444 is located in stack of thread T0 at offset 132 in frame
    #0 0x4b812f in main /tmp/a.cpp:29

  This frame has 1 object(s):
    [32, 132) 'romandate' <== Memory access at offset 132 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /tmp/a.cpp:59 convert_dec_to_roman(char*, unsigned int, unsigned long)
Shadow bytes around the buggy address:
  0x100065cde430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100065cde440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100065cde450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100065cde460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100065cde470: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00
=>0x100065cde480: 00 00 00 00 00 00 00 00[04]f3 f3 f3 f3 f3 f3 f3
  0x100065cde490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100065cde4a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100065cde4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100065cde4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100065cde4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  ASan internal:           fe
==912==ABORTING
fluorite ★★★★★ ()
Ответ на: комментарий от fluorite

да прост улыбнуло:) энтож форум. Мало ли ты не в курсе, указал на твои ошибки. Учитывая что твой пост был «топик не читай, сразу бесполезной простынёй отвечай».

pon4ik ★★★★★ ()

напиши потом рабочий вариант, для истории

Novell-ch ★★★★★ ()

уверен, ты быстро минуешь стадию кодера и будешь рм.

начнём по порядку

у тебя не инициализирован указатель на дату.

правь.


struct tm *date;
error_check = get_roman_date (romandate, date, size);
int get_roman_date (char *romandate, struct tm *now, size_t n)
error_check = convert_dec_to_roman (romandate, now->tm_wday, n);
qulinxao ★★☆ ()
Последнее исправление: qulinxao (всего исправлений: 2)
Ответ на: комментарий от fluorite

Ну сказала она что там переполнение, про которое анон ещё в начале топика сказал, ну функу показала, единственную, где в буффер что то пишется.

В данном, конкретном случае - код нагляднее, и движений сделать, что бы найти косяк надо меньше.

А то что шланг ништяк и так все кому надо знают, или это и был профит?;)

pon4ik ★★★★★ ()
Ответ на: комментарий от beastie

KISS

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
getweek()
{
	time_t t;
	struct tm *tm;

	t = time(NULL);
	tm = localtime(&t);

	return tm->tm_wday;
}

struct conv {
	int	arab;
	char	*roman;
} *cp, convert[] = {
	{ 1000, "M"  },
	{  900, "CM" },
	{  500, "D"  },
	{  400, "CD" },
	{  100, "C"  },
	{   90, "XC" },
	{   50, "L"  },
	{   40, "XL" },
	{   10, "X"  },
	{    9, "IX" },
	{    5, "V"  },
	{    4, "IV" },
	{    1, "I"  },
	{    0, NULL },
};

void
roman(char *roman, unsigned int decimal, size_t n)
{
	*roman = '\0';

	while (decimal > 0) {
		for (cp = convert; cp->arab != 0 && decimal < cp->arab; cp++)
			;
		decimal -= cp->arab;
		strncat(roman, cp->roman, n);
	}
}

int
main(int argc, char **argv)
{
	int week = getweek();
	char romandate[80] = {};

	roman(romandate, week, sizeof(romandate));

	printf("Arab  week: %d\n", week);
	printf("Roman week: %s\n", romandate);
	
	return 0;
}
beastie ★★★★★ ()
Ответ на: комментарий от DBSR

начни с более простых заданий.

то что у тебя сейчас - надежда.

и и можно возвращать структуры(указатели на них) а не гонять табуны аргументов.

структуры даже не обязательно обьявлять. а чисто большая строка где пред самой строкой ответом несколько «статусных символов» которые конвертиш в нужные .

qulinxao ★★☆ ()
Ответ на: комментарий от qulinxao

Потому что я не могу больше видеть быдлокод. Набивать свои шишки конечно полезно, но ... либо человек вдохновится правильным подходом, либо так и будет всегда писать быдлокод.

PS: учителю ещё и объяснить надо будет, что оно делает. Т.ч. или поймёт или в таком виде всё равно сдавать не будет.

beastie ★★★★★ ()
Ответ на: KISS от beastie

Надо больше конста:)

pon4ik ★★★★★ ()
Ответ на: комментарий от beastie

ты показал ему, что так писать можно ,

ибо ты устранил синтаксические(будь C более стрикт) но косяки с кривыми интерфейсами функций оставил :(

qulinxao ★★☆ ()
Ответ на: комментарий от qulinxao

Ну, строго говоря, не самый плохой интерфейс, ибо эта штука так то может и в контексте более чем одного потока исполнения вызываться, либо я не правильно понял твою задумку про

и можно возвращать структуры(указатели на них) а не гонять табуны аргументов

pon4ik ★★★★★ ()
Ответ на: KISS от beastie

Ммм, а выход за границы массива то проверить не? По факту в твоём небыдлокоде, таки есть к чему придраться, и тот же баг что у ТС?

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

всёж С(именно не об С++) переносимый ассемблер

и для духа ( наряду с листанием стандартов) полезно посмотреть как С эволюционировал

например из Лионовых коментариев(http://v6.cuzuco.com/)

/* structures to access integers : */
/* single integer */
struct { int  integ;};

/* in bytes */
struct { char lobyte; char hibyte; };

/* as a sequence */
struct { int r[]; };
/* -------------------------   */

и затем к любому валидному(т.е указывающего на память ибо пространство имён полей общее ) p можно

было p->integ или p->lobyte или даже p->r[k]

это ещё до различений struct union чисто для духа.

так и здесь можно возвращать строку(если не указатель на) в которой s[0] это статусный байт -ошибок и прочего a s[1...] сам ответ если вообще есть.

ну или культурно и бюрократично создавать struct ( public class ) и ими оперировать.

qulinxao ★★☆ ()
Последнее исправление: qulinxao (всего исправлений: 2)
Ответ на: комментарий от pon4ik

а иначе видим листинг ТС в ритуальных целях argc argv и прочие *

qulinxao ★★☆ ()
Ответ на: комментарий от qulinxao

зачем ты ему ускоряешь карьеру в прожектменеджмент?

жжошь :)

Harald ★★★★★ ()
Ответ на: комментарий от qulinxao

Для тех кто в танке, можно эталонный вариант? Скрепы это хорошо, но чем плох интерфейс конкретно roman()?

Хотя это таки больше праздное любопытство ибо тыкаю я обычно на крестах, материи, видимо, более высокие.

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

Придирайся, на то оно и опубликовано.

В общем, всё, что я хотел показать, так то, что если правильно переставить данные (структура вместо двух массивов и индексной переменной в данном случае), то всё остальное само красиво вытекает из этого.

По поводу «выхода за границы» — то да, там надо было бы заменить на

strncat(roman, cp->roman, n - strlen(roman) - 1);

beastie ★★★★★ ()
Ответ на: комментарий от pon4ik

алгоритмы(итераторы)структуры данных

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/** convert_dec_to_roman -- преобразует целое число в строковое представление
 * 
 * Параметры:
 *   decimal - исходное целое
 * Возвращаемое значение:
 *   функция возвращает область со статусом и строкой
*/
char* dec2rom (unsigned int decimal);

void 
ParseErrorExit(char* answer){
	//бла бла
	switch(answer[0]){
		default:printf("%i:%s\n",(int)answer[0],answer+1);
	}
	free(answer);//соблюдай ритуал памяти.
	exit(EXIT_FAILURE);
}

int 
main (int nenujnoechisloargumentovVstrokeZapuska, char *NenujnieArgumenty[]){
	char* answer;
	unsigned int a[]={1,2,3,4,5,6,7,8,9,11,349,5498,1,1<<10,2<<11,2<<13,4<<14,2<<19,0},*tst=a;
	do{
		if(*(answer=dec2rom(*tst))){
			ParseErrorExit(answer);
		}
		printf("%s\n", answer+1);
		free(answer);//блюди
	}while(*tst++);

	return EXIT_SUCCESS;
}

char* 
dec2rom(unsigned int inp){
	const char *symb = "IVXLCDM";//добавляйте по паре букв для больших
	//для теста можно ограничить symb="IVX" и дать inp=65534 :)
	//для теста можно ограничить symb="I" ...
	int max=1,pw=1,end;
	while(symb[2*pw-1]){max*=10;pw++;}
	int nbuf=inp/max+4*pw+1;
	char *buf=calloc(nbuf,1);
	if(!buf){
		buf=calloc(32,1);
		*buf='\x01';
		strcat(buf,"not enough memory\n");
		return buf;

	}
	char *p=buf+1;end=2*pw-1;
	while(1){
		while(inp>=max){
			*p++=symb[end-1];
			inp-=max;
		}
	if(!inp)break;
		max/=10;
		if(inp>=9*max){
			*p++=symb[end-3];
			*p++=symb[end-1];
			inp-=9*max;
		}
		if(inp>=5*max){
			*p++=symb[end-2];
			inp-=5*max;
		}
		if(inp>=4*max){
			*p++=symb[end-3];
			*p++=symb[end-2];
			inp-=4*max;
		}
		end-=2;
	}
	return buf;
}
qulinxao ★★☆ ()
Ответ на: комментарий от qulinxao

будь это код эдак на 1000 строк с частыми

...
symb[2*pw-1]
...
*buf='\x01';
....
int nbuf=inp/max+4*pw+1;
...
unsigned int a[]={1,2,3,4,5,6,7,8,9,11,349,5498,1,1<<10,2<<11,2<<13,4<<14,2<<19,0},*tst=a;

про всех не говорю, но я бы там ноги себе сломал, магия чисел как минимум.

Dron ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.