nagy változónál segfault

Fórumok

Sziasztok!

Adott az alábbi program. Ha n-et 11 millióra állítom, a program nem fut le, szegmentációs hibát jelent. 10 millió környékén még stabilan lefut. A határ a programban beállított érték környékén van; van amikor lefut, van amikor nem... Különböző hardvereken is ezt tapasztaltam, mindegyiken Ubuntu 10.10 volt, a tárolókban található gcc-vel.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
/*Addig dobalunk a kockaval, amig meg nem dobjuk mind a hat lehetoseget*/
void main()
{
unsigned long n,x;
float v,d;
n=10475400;
unsigned char a,e,q,w[6],s[n];
srand(time(NULL));
for(x=0; x<n; x++)
{
for(a=0; a<6; a++){w[a]=0;}
s[x]=0;
e=0;
while (e<6)
{
s[x]++;
q=rand()%6;
switch(q)
{
case 1:
w[1]=1;
break;
case 2:
w[2]=1;
break;
case 3:
w[3]=1;
break;
case 4:
w[4]=1;
break;
case 5:
w[5]=1;
break;
default:
w[0]=1;
break;
}
e=0;
for(a=0; a<6; a++){e+=w[a];}
}
}
v=0;
for(x=0; x<n; x++){v+=s[x];}
v=v/n;
d=0;
for(x=0; x<n; x++){d+=s[x]*s[x];}
d=sqrtf(d/n-v*v);
printf("%f\n%f\n", v,d);
}

Mit tehetnék, hogy nagyobb n-ekre is lefusson a program?

Hozzászólások

Ennyi memóriát dinamikusan kell foglalni.

gondoltam, hogy lesz aki belekot...
A warning lenyege, hogy nem hiba hanem gyanithatolag problema forrasa. Esetleg nem az, akkor false positive - na es? Ertem en, hogy elofordulhat hogy valaki direkt ekkora stacket akar, az majd figyelmen kivul hogyja a warningot. Kerdes a gyakorlatban ennek az aranya a veletlenul igy elkovetett hiba aranyahoz.. Forditasi idoben ott volt a gcc-nek az orult nagy stack, nyugodtan warningolhatott volna ha akart volna.

Kicsit elterve a szaltol: a melohelyen is az az elgondolas hogy a warning az error es az jo ha -Werror -al forditunk.. De ha ez igy lenne mert kell a forditokban eleve megkulonboztetni a warningot az error-tol?

Meg annyi offot, hogy imho az a legrosszabb mikor a warning esetleg megrosszabb lint vagy hasonlo kodelemzo gyomlalas kedvert nekiallsz eleve jo kodot modositani..

Nem belekötöttem, megjegyeztem. De ha már ilyen mélységig esünk, akkor nosza.
Lehet kódot írni -Wall -al, úgy, hogy az soha az életben nem fog sikítozni hogy baj lesz belőle, de ha te úgy döntesz hogy valamit rosszul csinálsz, azért nem a gcc a felelős. (Lehet úgy is shared objecteket kezelni, hogy kézzel, fixen, behaxolod az entrypointokat ami teljesen szabályos, lévén fordított esetben ezt nevezik hook-nak, a gcc nem fog szolni érte, de az, hogy valamit nem, esetleg szar módon kezelsz le, az már a te problémád.)
A warning-ok között is figyelmen kívül lehet hagyni párat, pont ezért warning, hogy rákérdez, biztos ezt akarod-e, mert nem feltétlen szabályos. A nem szabályos != nem jó -val,
Bár ha melóhelyen ezzel foglalkozol, akkor nem is értem miért nekem kéne meggyőznöm téged arról, hogy ha valamit favágó módszerrel csinálsz az nem a fordító hibája.

Szerintem a warning egy jófejség a fordító részéről, hogy fejlesztés közben a szar kódot is lehessen futtatni, ettől függetlenül majdnem ugyanannyira gáz, mint az error. Ha az ember biztos benne, hogy egy warning false positive, akkor #pragma warning disable vagy ilyesmi, de figyelmen kívül hagyni semmiképp se nyerő.

--
joco voltam szevasz

Igen, azt írtam, hogy nem létezik rá megoldás, valóban.

Igen, én is tudom, hogy lehetséges megoldani, csak felvetettem, hogy ilyen esetben például a warning nem jelez semmiféle hibát, pontosan tudom, hogy mit csinálok, és hogy előbb telik meg a memória, minthogy a signed-unsigned összehasonlítás miatt gond legyen. De okoskodj még légyszíves.
----
Hülye pelikán

Mivel -Werror-rol volt szo, es ezt irtad ra:

> És akkor mi van, ha a ciklusváltozó int (nem véletlenül mondjuk), és valami tároló méretéig akarok menni, ami ugye valami unsigned típus, ekkor warning lesz, de mégsem hiba, sőt.

Felteteleztem, hogy az a gondod, hogy warningol (ill errort csinal belole), pedig te tudod mit csinalsz, es tudod, hogy nem lehet overflow. Erre gyogyir a cast.

Ha az a gondod, hogy int i = (unsigned)j -re nem warningol a fordito, arra is van -W kapcsolo: -Wsign-conversion, es maris sirni fog.

--
|8]

> És akkor mi van, ha a ciklusváltozó int (nem véletlenül mondjuk)

Nálam ilyenkor az van, hogy elgondolkodok: akármennyire okénak tűnik, hogy int, biztos minden szempontból jó-e ez nekem. Nem kell-e valamit magasabb szinten újragondolni.

Ha tuti, akkor jöhet a cast, ahogy mondták.

Gondolom azért, mert a linker számolgatja a stack-et...
Mint lejjebb kiderült állítani is lehet amiről a gcc nem is tud, tehát nehezen tudna warningot adni...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

A lényeg az, hogy a gcc számára nincs olyan, hogy túl nagy a stack (a címzési maximumot leszámítva), max azt mondhatná a warningban, hogy "figyelj, mert nagyobb stack kell, mint a default". Kb ezt mondja egyébként a valgrind is...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

+1

valgrind kimenete:


==5093== Warning: client switching stacks?  SP change: 0xBE8E46A8 --> 0xBDEE6ED0
==5093==          to suppress, use: --max-stackframe=10475480 or greater
==5093== Invalid write of size 4
==5093==    at 0x80484EE: main (kocka.c:14)
==5093==  Address 0xbdee6ed0 is on thread 1's stack

Bocs, hogy off vagyok, de elegge indentalatlan a kod. Amugy miert akarod eltarolni az osszes dobas eredmenyet?

Ilyen indentalatlan kodot at nem nezek, inkabb tippet adok: nezd meg gdb-vel.

--
|8]

- Megnézem a dinamikus memóriakezelést. Nem vagyok c programozó (semmilyen se), akkor írok meg pár soros dolgokat c-ben, ha számít a sebesség.

- Geditben írtam és átrakva elvész az indendálást, pedig valahol azt olvastam, hogy nem kéne neki. Aztán gondoltam, ennél a pár sornál nem nagy gond. Ezek szerint mégis. Egyébként most először írtam ide programkódot. Olvasgattam a belinkelt formázási útmutatót, de abból nekem nem jött le, hogy lehetne szépen megoldani. Pedig több mindent kipróbáltam. Sajnálom, én már csak ilyen gyenge képességű vagyok.

- Azért tároltam a dobások eredményét, mert így egyszerűbb volt szórást számolni. Nem gondoltam, hogy probléma lesz vele. Igen, ez lesz a legegyszerűbb, az egyes eredmények tárolása nélkül is ki lehet számolni a szórást és feltehetőleg ezzel meg is oldódott a mizéria.

- A változónevekről: v: várható érték, d:szórás, s: összeg, a többi jellegtelen. Egy ilyen rövid programnál nem gondoltam, hogy lesz valaki, akinek ez nem tetszik.

Köszönöm a hozzászólásokat!

Arra vigyázz, hogy amit számítógépen kimutatsz, az nem feltétlenül a véletlenről szól, hanem sokkal inkább a véletlen-generátorról, amit használsz.
Például ebben a programban - ha elegendően nagy n-re futtatod, akkor egy idő után periodikus lesz a "véletlen" forrásod, mert az alap véletlen generátorok periodikusak.

Technikai segítség: forráskódot pastebin-ben szoktak posztolni. Megtartja az indentálást, sőt még színezi is a kódot, ha megadot a nyelvet. http://pastebin.com

Igen, ezért választottam ezt az időtől függő generálást, de valóban, így sem tudhatom, hogy mennyire véletlen így a véletlen.

Végül is így módosítottam, így nem kellett a memóriával izélni, menet közben számolok. A változó típusokat így már nagyobbra kellett állítani itt-ott. http://pastebin.com/X7kSGGVx

Köszönöm a segítséget!

Amikor a sztatisztikat szamolod, tobb gond is van szerintem

s=q;

kell s++; helyett. Gyakorlatilag az s valtozo kihagyhato.
Valamint n helyett kesobb epp azt az s-et kellene hasznalnod ami eddig volt, mert a while ciklus nem megy vegig. Sot, ezt a for ciklust is jo lenne lecserelni egy while-ra.
Nehany modositas:
Az elejen "int my_n = 0;"
A cikluson belulre


my_n ++;
v+=q;
d+=q*q;

es a legvegen


v=v/my_n;
d=sqrt( d/(my_n*my_n) - v*v );
// de nekem ugy remlik, hogy szigma = szum ( q_i*q_i - q_atlag*q_atlag) / (my_n - 1) kell! ,
// vagyis

v = v/(double)my_n;
q = sqrt( ( d + v*v*(double)my_n )/( (double)my_n -1.0) ));

// vigyazni kell, ha integert hasznalsz a kepletben, mert a fordito ugy dont neha, hogy kerekiti a szamot

Félreérted, nem a dobásokból számolok statisztikát, hanem a dobások számából ezért kell egy számláló változó, ez az s. Nem mértem miket írsz.

A másik, hogy Te korrigált tapasztalati szórást javasolsz tapasztalati szórás helyett, egyrészt nem tudom miért, másrészt nagy n-ekre ezek aszimptotikusan egyenlők, sokkal kiesebb n-ekre sincs jelentősége, hogy melyikkel számolunk.

"vigyazni kell, ha integert hasznalsz a kepletben, mert a fordito ugy dont neha, hogy kerekiti a szamot" Ezt hogy érted?

Hát meg nem mondom már milyen két típus volt egy kifejezésben
(talán valóban float és int), ami az egyik gcc-vel a számomra
várt módon működött, a másikkal pedig nem. Valamelyik tagot meg
kellett cast-olni, hogy jó legyen.

> Sol omnibus lucet.

No, megtaláltam:

(int)=(int)/(unsigned int)

A két (int) előjeles kellett, hogy legyen, az (unsigned int)
pedig egy számláló volt, ami természetesen előjeltelen. Az egyik
fordító jól csinálta a dolgot, a másik (az újabb) nem. Az unsigned
adatot castolni kellett (int)-re, és akkor jó lett.

Azért van gond itt a motorral is. Pl:

Ennek a kódnak: [index]
Ez az eredménye:

Vagy ennek: <index>
Ez:


Valljuk be, hogy nem kényelmes így. Szerencsésebb lenne, ha code html elemek között nem oldaná fel a BBCode-ot és a html kódot sem (kivéve nyilván a lezáró </code>-ot). Szerintem.
--
http://www.naszta.hu

Pont a double osztast nem lehet megtoszojni hatulrol. A 2-t egyszeruen muszaly double-ra castolni, mert a double int-re castolasaval informacioveszteseg jonne letre. Raadasul az osztas pont nem atrendezheto.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

- A fordító nem csinálhat olyan átrendezést, ami megváltoztatja az eredményt.
- Ha van double a képletben, akkor az addigi _részeredmény_ is double-re castolódik, mert fordított esetben információvesztés van.

Ami meglepő lehet az az, hogy:

(1/2)/2.0 == 0.0

De ez nem a fordító (vagy a nyelv) hülyesége.

1/2

az ugye két egész, a művelet a maradékos osztás, az eredmény egész, azaz 0.

x/2.0

az egy egész és egy "valós", a művelet az osztás (két valós), konverzió kell egész->valós. (Maradékos osztás most nem játszik, mert ahhoz egész számok kellenek, és a valós -> egész konverzió információvesztéssel járhat, így implicit nem megengedett.)

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

Az egesz switch-case helyett eleg lenne ennyi: w[q]=1;
Ha 255 dobasbol nem jon ki a 6 kulonbozo ertek, tulcsordul a szamlalo es 0rol ujrakezdi.

Ezt en is akartam irni, meg tegnap este, de surgosebb dolgom lett. ;)
A w[] tombot fel lehet erre hasznalni, w[q] = 1 -ek helyett w[q] ++ kell, utana pedig mar egyszeru a statisztikat is szamolni es nem kell vegigporgetni az osszes esemenyen, mint az eredeti kodban. (de azota mar van egy masik, mukodo megoldas)

Egy kis adalek, azon kivul, hogy termeszesetesen feloldottak a problemad forrasat.
Linkernek atadhato az informacio, ha nagyobb stack meretet szeretnel.

gcc -Wl,-stack_size,0x10000000 foo.c -o foo
gcc -Wl,-stack,0x10000000 foo.c -o foo

Ld-tol fuggoen.

Már elhangzott, hogy az s tömböt jobb dinamikusan foglalni, és ez meg is szüntette a segfaultot.

A segfaultot dinamikusmemória-foglalás nélkül is meg lehet szüntetni a stack limit megnövelésével, pl.

ulimit -S 12345

paranccsal kb. 12.345 MB-ra növelheted a főszál vermének maximális méretét -- tehát ha ennyi adatmennyiség alatt van az összes aktív függvény lokális változóinak és argumentumainak összmérete, akkor még nem segfaultol. A méretnövelés az aktuális shellben és a belőle indított programokban jut érvényre.

Az indentalas ott van, jelold ki, nezd meg a forrast.

<code> taget hasznaltal, na az ezt csinalja.


1
2

mig peldaul a [code] ezt csinalja:


1
    2

Meg tudod editalni (szerkeszteni) atopicindito hozzaszolast, ott csereld ki a <> jeleket [] -re, es mindenki jol jar.

Ha program által elfoglalt statikus terület elér egy bizonyos
méretet (64 MB?), akkor segfault. Valahol állítható ez a limit,
de nem tudom hol.

Fanyalgóknak: sajnos valóban előfordulnak olyan esetek, amikor
nagy-nagy statikusan foglalt memóriára van szükség. Sok statikus
memóriát lehet azonban spórolni ügyes algoritmussal, feladat
párhuzamosítással, megosztott memória használattal, overlay-
technikákkal, de ezek haladó témák.

> Sol omnibus lucet.

Ha jól értelmezem a kérdés az:

Hányat kell dobni a dobókockával ahhoz, hogy mind a hat szám előforduljon.

Ehhez viszont nem kell programozni:

http://en.wikipedia.org/wiki/Coupon_collector's_problem

szerk: Ebben az esetben
várható érték: 6*H(6) = 147/10 azaz pontosan 14.7
variancia: 6*Pi^2= 59.2176 szórás:7.69

Ha jól számoltam...

Ha a fordító tud C++-t is, akkor így definiáld a nagy tömbödet:


#include <vector>

{
...
std::vector<unsigned char> tul_nagy_tomb(10475400);
...
unsigned char ertek = tul_nagy_tomb[index];
...
unsigned char * cim_kell = &tul_nagy_tomb[0];
...
}

Ja, ez az alak azért is jó (szerencsés is hozzászokni), mert bárhol lépsz is ki a programból/függvényből stb., fel lesz szabadítva. Míg ha magad allokálód, akkor az nem biztos.

Azért lett at a függvény, mert nem találtam a szögletes zárójel kódját. (Az oldal egyébként benyeli.)
--
http://www.naszta.hu

ebben csak kicsikre kell kiszámolni :)
http://www.spoj.pl/problems/FAVDICE/

én mint nemprogramozó: ilyen egyszerű feladatoknál (1 fájlos...) először a nagy tömböket kirakom globálisba. tesztelgetés fejvakargatás után ha minden rendben - beburkolom egy struct-ba, amiben már dinamikusan van foglalva - felszabaditva a mem., és
lokális lesz a név. biztos van hátránya ennek de a konyhában beválthat.
üdv,ncs

kedves szemet,
1. nem mondta senki hogy érdektelen 1 billió oldalú kockára a kérdés, a fenti feladatot azért írtam be mert a topicnyitót láthatóan érdeklik a hasonló problémák.

2. ha az Euler konstans megfelelő sok jegye rendelkezésre áll akkor nem gond kiszámolni 1 billió-ra sem :), de ekkor a feladat egy gyorsan konvergáló gamma közelítés keresése lesz,
az eredeti feladat pedig középiskolás szintű...

3. "leellenőrizheted az okos tömbös módszereddel" ezt nem értem mire írtad. a globális tömbök használatát én általában javasoltam a topicnyitónak, ahelyett hogy a nyelvi eszközökkel foglalkozik (malloc,new,vector), főleg ha nem nyelvi problémák is vannak a megoldásában.

Tul nagy tombot hoztal letre a stacken.

Meg mindig csunyan, de legalabb heapen hozod letre igy: (nem inicilalizalt data .bss, gyakran nullaval toltodik ki de erre tilos epiteni)

/*Addig dobalunk a kockaval, amig meg nem dobjuk mind a hat lehetoseget*/

#define n 10475400
unsigned long x;
unsigned char a,e,q,w[6],s[n];
void main() // a main elott van tomb letre hozas , vagyis fugvenyen kivuli , "globalis" valtozo
{
float v,d;
srand(time(NULL));

FYI:
$ ulimit -a val megnezheted a max stack meretet

mondjuk:
$ ulimit -s 16000 # -al megnovelheted a max stacket.

Nem szokas megnovelni a stacket, mar az alap ertek is normalis esetben nagyon nagy.

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

A nagy egy relativ fogalom. Nekem van itthon egy hati taskam, amit en "nagy"-kent definialok, tekintve, hogy tenyleg nagy, de egy elefant szallitasara momentan alkalmatlan. Mert az meg nagyon-nagyon nagy.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

Jó nagy zavar van C szabvánnyal kapcsolatban, pedig jelenleg már kb. minden fordító elég tisztességesen követi. Valahogy mindig ellentétesen kezelik: használnak olyan funkciót ami szabvány szerint nem feltétlen úgy van, és kerülnek olyat amit pedig a szabvány rögzít...

"gyakran nullaval toltodik ki de erre tilos epiteni"

A szabvány pedig kimondja:

"If an object that has static storage duration is not initialized explicitly,
then:
- if it has pointer type, it is initialized to a null pointer;
- if it has arithmetic type, it is initialized to (positive or unsigned) zero;
- if it is an aggregate, every member is initialized (recursively) according to these rules;
- if it is a union, the first named member is initialized (recursively) according to these rules."