Ubuntu 12.04'te GCC ile derleme yapılamıyor


9

Aşağıdaki C programını Ubuntu ve Windows makinelerimde hem GCC hem de VC9 ile derlemeye ve çalıştırmaya çalışıyorum. Ancak, aşağıdaki sorunlarla karşı karşıyayım:

Ubuntu makinesinde:

GCC iyi derler, ancak çalıştırıldığında şu istemi gösteririm:

Segmentation Fault (Core Dump).

Windows makinede:

VC9 İyi derler ve çalışır. GCC iyi derler, ancak program çalıştırıldığında işlem sona erer.

Burada uzman yardımına ihtiyacınız var. İşte benim kod:

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

int calc_slope(int input1,int input2)
{
    int sum=0;
    int start=input1;
    int end=input2;
    int curr=start;

    //some validation:
    if (input1>input2)
        return -1;


    while(curr<=end)
    {
        if (curr>100)
        {
            char *s="";
            int length;
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            length=strlen(s);
            s++;
            do
            {
                //printf("curr=%d char=%c pointer=%d length=%d \n",curr,*s,s,length);
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                //printf("curr=%d l=%d c=%d r=%d\n",curr,left,cent,right);
                if ( (cent>left && cent>right) || (cent<left && cent<right) )
                {
                    sum+=1; //we have either a maxima or a minima.
                }

                s++;
            } while (*(s+1)!='\0');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}

Güncelleme:

Kredi Eliah'a sadece hatayı izlememe yardımcı olmakla kalmadı, aynı zamanda bir gcc derlenmiş programında hata ayıklamada çok yardımcı olan beni gdbve geri izleme aracını ( bt) tanıttı . İşte değiştirilmiş sürüm, bazı deneme yanılma sonra çalıştı:

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

int calc_slope(int input1,int input2)
{
    int sum=0;
    int start=input1;
    int end=input2;
    int curr=start;

    //some validation:
    if (input1>input2)
        return -1;


    while(curr<=end)
    {
        if (curr>100)
        {
            int size=10;
            char *s=(char*)malloc((size+1) * sizeof(char));
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            s++;
            do
            {
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                if ( (cent>left && cent>right) || (cent<left && cent<right) )
                {
                    sum+=1; //we have either a maxima or a minima.
                }

                s++;
            } while (*(s+1)!='\0');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}

3
Bence bu bir derleme sorunu değil, daha çok bir çalışma zamanı sorunu. StackOverflow'dan daha fazla yardım alabilirsiniz .
oaskamay

VC9 ile derlendikten sonra gerçekten iyi çalıştığından emin misiniz ?
Eliah Kagan

Evet,% 100. ama gcc ile değil.
Prahlad Yeri

@PrahladYeri Havalı! Bunun nedenlerini açıkladım cevabım . (Bu aynı zamanda muhtemelen bu konuyu konu üzerinde düşünmemiz gerektiği anlamına gelir, çünkü Ubuntu * -specific * davranışı ile ilgilidir. Windows'daki GCC benzer davranış gösterir, ancak hata mesajı yoktur ve orada ne olduğunu tam olarak bilmek zor - ayrıca, ne zaman Ubuntu ve Microsoft Visual C ++ çalışmaları GCC farklı, ben Ubuntu Ubuntu üzerine GCC öyle gibi çalışır neden sormak için makul bir yerdir sor düşünüyorum konusunda başka sorum dedi. doğru yapmak Yığın taşması aittir).
Eliah Kagan

C'de bir dize değişmezini değiştirmek tanımsız bir davranıştır. Lütfen bunu hatırla.
jn1kk

Yanıtlar:


15

Bir program, kendisine ayrılan alanın dışındaki belleğe erişmeye çalıştığında bir segmentasyon hatası oluşur.

Bu durumda, deneyimli bir C programcısı, sorunun sprintfçağrılan satırda meydana geldiğini görebilir . Ancak, segmentasyon hatasının nerede meydana geldiğini söyleyemiyorsanız veya anlamaya çalışmak için kodu okumak istemiyorsanız , programınızı hata ayıklama sembolleri ile oluşturabilirsiniz (ile gcc, -gbayrak bunu yapar ) ve ardından bir hata ayıklayıcı üzerinden çalıştırın.

Kaynak kodunuzu kopyaladım ve adını verdiğim bir dosyaya yapıştırdım slope.c. Sonra böyle inşa ettim:

gcc -Wall -g -o slope slope.c

(Bu -Wallisteğe bağlıdır. Yalnızca daha fazla durum için uyarı üretmesini sağlamaktır. Bu, neyin yanlış olabileceğini anlamaya da yardımcı olabilir.)

Daha sonra gdbilk olarak programla gdb ./slopebaşlamak için gdbçalışarak hata ayıklayıcıda programı çalıştırdım ve sonra hata ayıklayıcıda bir kez hata ayıklayıcıya runkomut verdim:

ek@Kip:~/source$ gdb ./slope
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/ek/source/slope...done.
(gdb) run
Starting program: /home/ek/source/slope 
warning: Cannot call inferior functions, you have broken Linux kernel i386 NX (non-executable pages) support!

Program received signal SIGSEGV, Segmentation fault.
0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6

(Do benim dert you have broken Linux kernel i386 NX... supportmesajın, aynı engellemez gdbbu programı hata ayıklamak için etkin bir kullanılmaktan.)

Bu bilgiler son derece şifreli ... ve libc için hata ayıklama sembolleri yüklü değilse, sembolik işlev adı yerine onaltılık bir adrese sahip daha da şifreli bir mesaj alırsınız _IO_default_xsputn. Neyse ki, önemli değil, çünkü gerçekten bilmek istediğimiz şey, programınızda sorunun nerede olduğu .

Bu nedenle, çözüm, geriye doğru bakmak, SIGSEGVsinyalin nihayet tetiklendiği bir sistem kütüphanesinde o belirli işlev çağrısına giden işlev çağrılarını görmek .

gdb(ve herhangi bir hata ayıklayıcı) yerleşik bu özelliğe sahiptir: buna yığın izleme veya geri izleme denir . btHata ayıklama komutunu bir geri izleme oluşturmak için kullanın gdb:

(gdb) bt
#0  0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
#1  0x00178e04 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2  0x0019b234 in vsprintf () from /lib/i386-linux-gnu/libc.so.6
#3  0x0017ff7b in sprintf () from /lib/i386-linux-gnu/libc.so.6
#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
#5  0x08048578 in main () at slope.c:52
(gdb)

Sen görebilirsiniz mainişlevi çağırır calc_slope(amaçladığınız) işlevini ve sonra calc_slopeçağırır sprintfdiğer ilgili kütüphane fonksiyonları bir çift için çağrıları ile uygulanan (bu sistemde) olan.

Genel olarak ilgilendiğiniz şey, programınızdaki , programınızın dışındaki bir işlevi çağıran işlev çağrısıdır . Kullandığınız bir kütüphanede hata / kütüphaneler kendilerini olmadığı sürece (bu durumda, standart C kütüphanesi libckütüphane dosyası tarafından sağlanan libc.so.6), çarpışma programınızda ve neden hata genellikle ya da yakınında olacak programınızdaki son çağrı .

Bu durumda, bu:

#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26

Programınız buradan çağırır sprintf. Bunu biliyoruz çünkü sprintfbir sonraki adım. Ama bunu belirtmeden bile, bunu biliyorsunuz çünkü 26. satırda olan budur ve şöyle diyor:

... at slope.c:26

Programınızda, satır 26 şunları içerir:

            sprintf(s,"%d",curr);

(Her zaman en azından şu anda bulunduğunuz satır için satır numaralarını otomatik olarak gösteren bir metin düzenleyici kullanmalısınız. Bu, hem derleme zamanı hatalarını hem de hata ayıklayıcı kullanırken ortaya çıkan çalışma zamanı sorunlarını yorumlamak için çok yararlıdır.)

Dennis Kaarsemaker'ın cevabında tartışıldığı gibi , stek baytlık bir dizidir. (Sıfır değil, çünkü atadığınız değer ""bir bayt uzunluğunda, yani eşittir { '\0' }, aynı şekilde "Hello, world!\n"eşittir { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }.)

Peki, neden olabilir bazı platformda bu hala iş (ve görünüşe göre Windows için VC9 ile derlenmiş ne zaman)?

İnsanlar genellikle bellek ayırdığınızda ve sonra belleğe erişmeye çalıştığınızda bunun bir hata oluşturduğunu söylerler. Ama bu gerçekten doğru değil. C ve C ++ teknik standartlarına göre, bunun gerçekten ürettiği tanımlanmamış davranıştır.

Başka bir deyişle, her şey olabilir!

Yine de, bazı şeyler diğerlerinden daha olasıdır. Neden yığındaki küçük bir dizi, bazı uygulamalarda, yığındaki daha büyük bir dizi gibi görünür?

Bu, platformdan platforma değişmesine izin verilen yığın tahsisinin nasıl uygulandığı ile ilgilidir. Yürütülebilir dosya, yığınına gerçekte herhangi bir zamanda kullanılması amaçlanandan daha fazla bellek ayırabilir. Bazen bu, kodunuzda açıkça hak talebinde bulunmadığınız bellek konumlarına yazmanıza izin verebilir . VC9'da programınızı oluştururken olan şey budur.

Ancak, VC9'da bile bu davranışa güvenmemelisiniz. Farklı Windows sistemlerinde bulunabilecek kitaplıkların farklı sürümlerine bağlı olabilir. Ancak, daha fazla yığın alanının gerçekte kullanılması amacıyla tahsis edilmesi sorunu daha da olasıdır ve bu yüzden de kullanılabilir.Sonra "tanımsız davranış" tam kabusu yaşarsınız, burada, bu durumda, birden fazla değişken aynı yerde depolanabilir, burada birine yazma üzerine diğerinin üzerine yazılır ... ama her zaman değil, çünkü bazen değişkenlere yazar kayıtlarda önbelleğe alınır ve gerçekte hemen gerçekleştirilmez (veya değişkenlere yapılan okumalar önbelleğe alınabilir veya bir değişkenin öncekiyle aynı olduğu varsayılabilir, çünkü derleyiciye ayrılan belleğin yazılmadığı bilinir değişkenin kendisi).

Ve bu da beni VC9 ile oluşturulduğunda programın neden çalıştığına dair diğer olası olasılığa getiriyor. Tek baytlık diziden sonraki alanı kullanmak için, programınız tarafından bazı dizilerin veya başka bir değişkenin gerçekte (programınızın kullandığı bir kütüphane tarafından tahsis edilmesini de içerebilir) tahsis edilmesi mümkündür s. Bu nedenle, sbir bayttan daha uzun bir dizi olarak işlem yapmak , o / bu değişkenlerin / dizilerin içeriğine erişim etkisi de yaratacaktır, bu da kötü olabilir.

Sonuç olarak, böyle bir hatayla karşılaşırsanız, "Segmentasyon hatası" veya "Genel koruma hatası" gibi bir hata almak şanslı . Ne zaman yok buna sahip çok geç program olmasıdır kadar, dışarı bulamayabilir tanımsız davranış.


1
Böyle açıklayıcı açıklama için teşekkürler. Bu tam olarak ne gerekli .. !!
Prahlad Yeri

9

Merhaba arabellek taşması!

char *s="";
sprintf(s,"%d",curr);
length=strlen(s);

Yığında bir dize için bir bayt ayırır ve sonra ona bir bayttan fazlasını yazmaya devam edersiniz. Üstüne üstlük, bu dizinin sonunun ötesinde okursunuz. Lütfen bir C kılavuzu ve özellikle dizeler ve bunlar için bellek ayırma bölümünü okuyun.


Evet, daha sonra bunu öğrendim. Ama bunu yazdığımda, VC9 derleyicisine sadece izin vermekle kalmadı, aynı zamanda sonuçları düzgün bir şekilde gösterdi. Ben strlen (ler) printf-ed ve bana 1 değil, 4 gösterdi !!
Prahlad Yeri

Bunu düzeltmek için nasıl bir yol izlemeliyim? Koddan tahmin etmiş olmanız gerektiği gibi, önceden sabit boyutlara * ayırmaya imkanım yok. Uzunluğu, bir dizgeye dönüştürene kadar bilinemeyen curr değişkenindeki basamak sayısıdır! ?
Prahlad Yeri

Yapabilirim, ama programlama tavsiyesi için gerçekten Stack Overflow'a gitmelisiniz, çünkü burada oldukça offtopic.
Dennis Kaarsemaker

1
O görünüşte Ubuntu ve başka bir platform arasındaki farklılık (ve bunun için en olası nedeni açıkladım davranışı içerir beri burada orijinal soru konu dışı olmayabilir @DennisKaarsemaker cevabım ). C düzgün dizeleri nasıl tahsis ile ilgili soruların burada değil Overflow üzerine ait olduğunu kabul ediyorum .
Eliah Kagan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.