Retrojen Forum
Dijital Sanat => Kodlama => Konuyu başlatan: wizofwor - 02 Kasım 2018, 20:50:38
-
cc65'le uğraştığım son mini projemde CBM SEQ dosyasından veri çekmek için cc65 e-mail grubunda denk geldiğim kodlardan devşirdiğim aşağıdaki fonksiyonu kullanıyorum. Ancak sonradan öğrendim ki while( !feof(ptr) ) kullanımı caiz değilmiş. Aşağıdaki gibi didaktik açıklamalar söz konusu.
EOF is the response you get from an attempted I/O operation. It means that you were trying to read or write something, but when doing so you failed to read or write any data, and instead the end of the input or output was encountered. As long as the I/O operations succeed, you simply cannot know whether further, future operations will succeed. You must always first try the operation and then respond to success or failure.
Okuma sırasında arada hata olursa kodun patlayacağından bahsedilmiş. while(!feof(ptr) && !ferror(ptr)) şeklindeki kullanım bunu çözmez mi? Yoksa multitasking sistemlerde başka bir programın dosyaya ilave yapması gibi durumlar mı sorun oluşturuyor?
void read_tree(uchar * id)
{
uchar *buffer;
uchar *file_name;
FILE *file;
uchar i;
buffer = malloc(1 * sizeof(uchar));
file_name = malloc(8 * sizeof(uchar));
// Dosya ismini hesapla
itoa(id, buffer, 2);
strcpy(file_name,"tdat");
strcat(file_name,buffer);
cprintf("\n\rVeri dosyasi aciliyor...");
_filetype = 's';
if(file = fopen(file_name, "r"))
{
cprintf("\n\rVeriler aliniyor...");
i=0;
while(!feof(file))
{
fread(&node[i], sizeof(struct Node), 1, file);
i++;
}
fclose(file);
cprintf("\n\rOk.");
}
-
Selamlar,
Benim bildiğim eof oluşması için read veya write yapılmış olması gerekiyor. Yani yeni açılan bir dosyada read veya write yapmadan dosya boş olsa bile eof oluşmuyor. Bu sebeple while içinde değil, işlem sonrasında bu kontrolü yapak doğru diye biliyorum.
Örneğin pascal olsa,
repeat
...
until eof
şeklinde yazardım.
NOT: Ben deliMawi yada ilker bu arada ...
-
C64 özelinde bu şekilde gayet çalışıyor aslında. Belki dosyanın boş olduğu durumda problem yaratabilir bir de okuma sırasında disket sürücüyü kapatırsan veya network kablosunu çekersen sonsuz döngüye girer denilmiş. Ancak okuma sırasında disket sürücü kapatıldığında veya disket çıkartıldığında programın kilitlenmesi bana gayet makul göründü.
// Ayrıca sen zaten foruma kayıtlı değil miydin? Kullanıcı kaydıyla ilgili bir sorun mu oluştu?
-
Uzun zamandır girmediğim için kaydım uçmuş.
Bu arada zamanında kullandığım gerçek nickim zaten eins idi. Ancak 5-6 sene önce, retro forumlara üye olmaya başladığımda, özellikle yabancı birçok foruma eins niki ile kayıt olamadım, bende o dönemde alternatif nick olarak deliMawi yi kullanmaya başlamıştım. Şimdi fırsat bu fırsat, tekrar üye olurken eins ile kayıt oldum...
Yukarıda ki konuya ilişkin olarak, çalışıp çalışmamasından öte, gerçek anlamda eof durumunun oluşabilmesi için bir i/o işlemi yapılması gerekliliği var. Benim burada mantık olarak yanlış olabileceğini düşündüğüm tek nokta, while döngüsünde kontrolün işlemden önce yapılıyor olması.
EK NOT: While loop öncesinde bir read ile başlanmış olsa bence caiz olurdu...
-
Bir ek not daha.
Yukarıda ki örnek kodda mesela, dosyada ki son veriyi de okudun. Ama son veri okununca henüz eof oluşmadı. Dolayısı ile while bir kez daha girdi döngüye. Read yaptın ve eof oluştu. Ama sen bu arada çoktan bir veri okudğunu düşünüp bunu işledin... Ama o okumaya çalıştığın son veri yoktu dosyada. Anlatabildim umarım.
Şu daha doğru:
i=0;
fread(&node[i], sizeof(struct Node), 1, file);
while(!feof(file))
{
i++;
fread(&node[i], sizeof(struct Node), 1, file);
}
Şöyle yazınca da daha rtistik oluyor: :)
i=0;
fread(&node[i], sizeof(struct Node), 1, file);
while(!feof(file))
{
fread(&node[++i], sizeof(struct Node), 1, file);
}
-
Öneri #1:
do {
...
} while (condition);
Öneri #2:
while (true) {
...
if (condition) break;
}
-
Bir yandan matahari'nin minimalist cevabı üzerine düşünürken, diğer taraftan da sana cevap vermeden duramadım.
İlk mesajında bahsettiğin read/write yapmadan eof oluşmuyor olması benim durumumda problem olmuyor. Zira okuma yapmadan çağırdığım ilk foef() True döndürüyor.
İkinci söylediğin fazladan bir kayıt okuma konusunda haklısın ancak kafamı kurcalayan dosyaya kullandığım dizinin boyutu kadar veri yazmıştım. Geri okurken fazla veri okuduğundan çakılması gerekmez miydi? Sanırım tamamen şansa çalıştı. Konuyu fazla uzatmadan şu koşulu sona alayım.
-
Çakılması gerekmez. node değişkeninden sonra ne olduğuna bağlı hafızada. Eğer tam dosya boyutu kadar ise node'a ayrılan bölüm, muhtemelen bir sonraki değişken için ayrılan yere birşeyler yazıyorsun...
-
Kodu where(!feof())'nin günah olduğuna inanarak okuyabiliyorsan oku anlamına gelecek şekilde aşağıdaki gibi değiştirmiştim. Bu da sorunsuz çalışıyor.
while(fread(&node[i], sizeof(struct Node), 1, file))
{
gotox(wherex()-3);
cprintf("%%%2d",(i++));
}
-
Evet, zaten doğru mantık da bu. Okuyabiliyorsan oku! Ha okuyamadıysan, yani sonuç 0 döndüyse iki seçenek kalmıştır. Ya feof, yada ferror... Bunu da gerekirse kontrol edip duruma göre davranabilirsin...
-
Cevaplar için teşekkürler. Sonuçta dosyanın başına kayıt sayısını da ekleyerek kodu aşağıdaki hale getirdim.
fread(&num_of_nodes, sizeof(uchar), 1, file);
for(i=0; i<num_of_nodes; i++)
{
fread(&node[i], sizeof(struct Node), 1, file);
gotox(wherex()-3);
cprintf("%%%2d",(100*(i+1)/num_of_nodes));
if(feof(file)) break;
}
-
O değil de gördüğüm kadarıyla read_tree() fonksiyonun (oradaki itoa bildiğimiz standard library itoa'sı ise) bildiğin memory trashing yapıyor sanki. Şöyle ki:
fonksiyonun bir uchar * id alıyor. Pointer olduğuna göre bunun değeri bir memory address tutuyor C64 olduğuna göre 16 bit bir adres olsa gerek.
sonra bir buffer tahsis etmişsin:
uchar *buffer;
buffer = malloc(1 * sizeof(uchar));
uchar (bildiğimiz unsigned char ise bu) sizeof(uchar) zaten 1 byte. 1 x 1 = 1 byte'lık buffer allocate etmiş oldun.
ama sonra şu var:
itoa(id, buffer, 2);
bu fonksiyon id'nin değerini bir binary string'e çevirip buffer'a yazar (son argümana 2 demişsin çünkü). Şimdi bu id memory adress olduğuna göre büyükçe bir sayı. E bunu bir de binary'ye çevirdin, (bir değer sallıyorum) diyelim oldu sana "1111 1010 0100" gibi bir string. itoa() bunu yazdı buffer'a. Ama buffer 1 byte'cık (salladığım sıralıda var 12 karakter - yani 12 byte - 1 byte'ı buffer'a yazdı ama ardından gelen 11 byte'ı trash etti).
Yanlışım varsa söyleyin lütfen. :)
-
@Alpyre;
Bahsettiğin probleme rastlamadım. Tahminim compiler bunu kendisi hallediyor ancak "Converting pointer to integer without a cast" şeklinde bir warning veriyordu. Bu uyarıyı iki yerde daha alıyordum. Çözümünü sonraya bırakmıştım.
Senin uyarından sonra biraz araştırdım ve kodu itoa((uchar)id, buffer, 10) şeklinde değiştirerek warning'den kurtuldum.
-
@Alpyre;
Bahsettiğin probleme rastlamadım. Tahminim compiler bunu kendisi hallediyor ancak "Converting pointer to integer without a cast" şeklinde bir warning veriyordu. Bu uyarıyı iki yerde daha alıyordum. Çözümünü sonraya bırakmıştım.
Senin uyarından sonra biraz araştırdım ve kodu itoa((uchar)id, buffer, 10) şeklinde değiştirerek warning'den kurtuldum.
Şimdi warning'den kurtulman güzel de... hala aynı memory trashing olayı söz konusu.
Kod'un o kısmı tam olarak ne iş görüyor? Sanki orada itoa() değil de atoi() kullanılması gerekiyor gibi geliyor bana ama...
-
integer'ı daha sonra dosya adına eklemek üzere char'a döndürüyorum.
atoi() bunun tam tersini yapmıyor mu?
-
Hmm. Şimdi anladım. Ama senin kod integer'ı char'a döndürmüyor ki, bir pointer'ı string'e döndürüyor.
Çünkü fonksiyonun uchar* id alıyor. Yani bir pointer alıyor.
Neden pointer alıyor ki? Sanırım id'yi pass by reference yapmak istiyorsun...
Bence buna gerek yok. Yani fonksiyonun uchar id alabilir.
Ha ille de pass by reference olması gerekiyorsa (yani fonksiyonun uchar* id alacaksa), o halde itoa() çağırırken id'yi dereference etmelisin:
itoa(*id, buffer, 10);
...id'nin değerinin de 9'dan büyük olmayacağı garanti edilmeli. Büyük olursa yine memory trashing olur.
Hem ayrıca buffer ve filename'i de malloc()'la tahsis etmene gerek yok. Doğrudan stack üzerinde tahsis edebilirsin:
uchar buffer[1];
uchar filename[8];
-
Pointer kullanmamın sebebi pass by value / pass by reference muhabbeti değil. 8-bit mimaride çalışacak program için ezbere optimizasyon yapmaya çalışmam. Ezbere dememin sebebi şu:
6502 CPU'da fonksiyona değer göndermenin maliyeti çok yüksek. C ile kod yazarken mümkünse global değişkenler kullanın veya pointer gönderin deniyor. Ancak senin sorun üzerine düşününce benim göndermekten imtina ettiğim değişken (unsigned char) 1 byte yerine gönderdiğim pointer 2 byte.
Dereference konusuna gelince. Kodu aşağıdaki şekilde denediğimde sorunsuz şekilde derleniyordu.
itoa((uchar)id, buffer, 10);
Uyarın üzerine uchar'ı uchar* yaptım. Bu şekilde "Converting pointer to integer without a cast" uyarısı alıyorum. Ama çalışıyor.
itoa((uchar*)id, buffer, 10);
Son olarak id'nin önüne yıdız koydum. Bu şekilde uyarı vermiyor ama dosyayı da bulamıyor.
itoa((uchar)*id, buffer, 10);
-
Selam wizofwor,
Söylemekten kendimi alamadığım bir nokta olacak:
C ile ilgili çalışmalarını neden cc65 üzerinde yapıyorsun?
Her ne kadar C64'ün kalbimdeki yeri bütün platformlardan öte olsa da, C'yi öğrenmek ve kendini geliştirmek için C64'ün doğru platform olmadığını düşünüyorum. C'nin bellek, stack, değişken yönetimi zaten yeterince tuzak içeriyor. C64 gibi 8 bitlik bir mimarinin limitleri için düşünülmüş cc65 gibi bir derleyici, dil ve standart kütüphane desteği bakımından ne noktadadır ben şüpheye düşerdim. Hatta dünyadaki bütün c derleyici ve kütüphane implementasyonlarını "bütünlük ve destek" yönünden sıraya dizmeye kalksak, ve cc65 bu sıralamada sonlarda çıksa şaşırmam. İlaveten elinde bir debugger olması ve kodun içinde debugger ile satır satır ilerleyebilmek, veri yapılarını inceleyebilmek vs, öğrenmeyi hızlandırıyor bence.
Elbette bu iş zevk işi ve sen iyi vakit geçiriyorsan sorun yok. Ancak ben olsam, önce pc'de en iyi desteklenen, en iyi debugger'ı olan 2-3 aday derleyiciden biri ile önce oturur C'ye yüklenirdim. Bu hakimiyeti kazandıktan sonra cc65'in C64'e has inceliklerini vs öğrenip öyle kullanırdım.
Umarım yol tıkayıcı bir mesaj olarak algılamazsın. Sevgiler.
-
Tavsiyen için çok teşekkür ederim. Ben programlamaya hobi olarak yaklaşıyorum ve olaya biraz daha farklı açıdan bakıyorum. C64 haricindeki işler beni heyecanlandırmıyor. 6502'nin C için ciddi handikapları olduğunun farkındayım. Bu yüzden belli limitler dahilinde kodlamaya çalışıyorum. Amacım C öğrenmekten çok C64 üzerinde bir şeyler geliştirmek.
-
Tekrar selam wizofwor
Pass by pointer ve dereference konularında bir tekrar okuma yapman gerekiyor diye düşünüyorum. Çünkü:
itoa((uchar*)id, buffer, 10);
...bu yazdığın dereference değil, bir type cast.
Ha bak bu olur:
Son olarak id'nin önüne yıdız koydum. Bu şekilde uyarı vermiyor ama dosyayı da bulamıyor.
itoa((uchar)*id, buffer, 10);
...ama (uchar) yazmana gerek yok. Çünkü zaten bir uchar pointer'ı dereference ettiğinde uchar bir değer elde edersin. Yani yazman gereken ilk yanıtımda yazdığım gibi sadece: *id
Eğer dosyayı bulamıyorsa, pass ettiğin *id değerinde bir yanlışlık olabilir (gerçi buna neden olacak başka yanlışlar da var).
Bu arada böyle mikro optimizasyon yapacaksak, pass by value maliyetinden daha büyük itoa() maliyeti var orada.
Sana tek basamaklı bir değeri 1 ascii değerine çevirecek bir işlem lazım o kadar. Bunu id'nin değerine 48 ekleyerek halledebilirsin (C64'te string'ler ascii'dir herhalde).
Yanlış anlamadıysam sen şöyle dosya adları istiyorsun:
id = 0 ise "tdat0"
id = 1 ise "tdat1"
...
id = 9 ise "tdat9"
ve "tdat10" kesinlikle yok, 9'a kadar (1 uchar'lık buffer tahsis etmiş olmandan çıkarıyorum bunu).
Eğer bu çıkarımlarım doğruysa... sen yine id'yi pointer'ı ile pass et...
...fakat file_name'i şöyle oluştur:
uchar file_name[6] = "tdat0"; // 6 çünkü NULL terminator için byte daha lazım.
file_name[4] = *id + 48; // 48 çünkü ascii 48 '0' demek.
Al sana sade çok optimize (itoa, strcpy ve strcat'den kurtuldun) hem de doğru çalışacak bir kod. ;)
-
Program boyutunu düşürmek için kütüphaneleri kaldırmaya başladım. string.h ve stdlib.h kütüphanelerini kaldırınca itoa ve strcopy'de gitti. id'yi decimal yerine, read_three(0) yerine read_three("0") şeklinde, ASCII olarak gönderiyorum. Bu şekilde yapınca herhangi bir modifikasyon yapmadan doğrudan string'in sonuna ekleyebildim.
uchar file_name[8];
char_cpy(file_name,"tdat");
concat(file_name, id);
...
void char_cpy(char * dest, char * src)
{
while(*src)
*dest++ = *src++;
*dest = '\0';
}
void concat(char * dest, char * src)
{
char i;
while(*dest)
*dest++;
while(*src)
*dest++ = *src++;
*dest = '\0';
}
Tahminin doğru
read_three("0") , tdat0 dosyasını,
read_three("1"), tdat1 dosyasını okuyor.
-
Değer olarak göndersen concat'ten de kurtulutdun.
Ayrıca for döngüsünde falan kullanırdın id'yi gerekirse, ama yine sen bilirsin:)
-
@wizofwor abi CC65 ile GEOS üzerinde de uygulamalar bekliyoruz senden.