Saya mencoba mengkompilasi dan menjalankan program C di bawah ini pada mesin Ubuntu & Windows saya dengan GCC & VC9. Namun, saya menghadapi masalah di bawah ini:
Di mesin Ubuntu:
GCC mengkompilasi dengan baik, tetapi ketika dijalankan, saya ditunjukkan prompt ini:
Segmentation Fault (Core Dump).
Di mesin Windows:
VC9 Mengkompilasi & berjalan dengan baik. GCC mengkompilasi dengan baik, tetapi proses berakhir ketika program dijalankan.
Perlu bantuan ahli Anda di sini. Ini kode saya:
#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;
}
Memperbarui:
Penghargaan diberikan kepada Eliah karena tidak hanya membantu saya melacak kesalahan, tetapi juga memperkenalkan saya ke gdb
dan alat penelusuran baliknya ( bt
) yang sangat membantu dalam men-debug program yang disusun gcc. Ini adalah versi yang dimodifikasi, saya berhasil setelah beberapa percobaan dan kesalahan:
#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;
}
Jawaban:
Kesalahan segmentasi terjadi ketika suatu program mencoba mengakses memori di luar area yang telah dialokasikan untuk itu.
Dalam hal ini, seorang programmer C yang berpengalaman dapat melihat bahwa masalahnya terjadi pada baris di mana
sprintf
disebut. Tetapi jika Anda tidak dapat mengetahui di mana kesalahan segmentasi Anda terjadi, atau jika Anda tidak ingin repot membaca kode untuk mencoba mencari tahu, maka Anda dapat membangun program Anda dengan simbol debug (dengangcc
,-g
flag melakukan ini ) dan kemudian jalankan melalui debugger.Saya menyalin kode sumber Anda dan menempelkannya ke file yang saya beri nama
slope.c
. Lalu saya membangunnya seperti ini:(Ini
-Wall
opsional. Hanya untuk membuatnya menghasilkan peringatan untuk lebih banyak situasi. Ini dapat membantu dalam mencari tahu apa yang mungkin salah, juga.)Kemudian saya menjalankan program di debugger
gdb
dengan menjalankan terlebih dahulugdb ./slope
untuk memulaigdb
dengan program, dan kemudian, sekali di debugger, memberikanrun
perintah kepada debugger:(Jangan khawatir tentang
you have broken Linux kernel i386 NX
...support
pesan saya ; itu tidak mencegahgdb
dari digunakan secara efektif untuk men-debug program ini.)Informasi itu sangat samar ... dan jika Anda tidak memiliki simbol debug diinstal untuk libc, maka Anda akan mendapatkan pesan lebih samar yang memiliki alamat heksadesimal alih-alih nama fungsi simbolik
_IO_default_xsputn
. Untungnya, itu tidak masalah, karena apa yang benar-benar ingin kami ketahui adalah di mana dalam program Anda masalahnya terjadi.Jadi, solusinya adalah melihat ke belakang, untuk melihat pemanggilan fungsi apa yang terjadi yang mengarah ke pemanggilan fungsi tertentu di pustaka sistem tempat
SIGSEGV
sinyal akhirnya dipicu.gdb
(dan debugger apa pun) memiliki fitur ini bawaan: ini disebut jejak stack atau backtrace . Saya menggunakanbt
perintah debugger untuk menghasilkan backtrace digdb
:Anda dapat melihat bahwa
main
fungsi Anda memanggilcalc_slope
fungsi (yang Anda maksudkan), dan kemudiancalc_slope
panggilansprintf
, yang (pada sistem ini) diimplementasikan dengan panggilan ke beberapa fungsi pustaka terkait lainnya.Apa yang secara umum Anda minati adalah pemanggilan fungsi dalam program Anda yang memanggil fungsi di luar program Anda . Kecuali jika ada bug di perpustakaan / perpustakaan itu sendiri yang Anda gunakan (dalam hal ini, perpustakaan C standar yang
libc
disediakan oleh file perpustakaanlibc.so.6
), bug yang menyebabkan crash ada di program Anda dan sering akan berada di atau di dekat panggilan terakhir dalam program Anda.Dalam hal ini, itu adalah:
Di situlah panggilan program Anda
sprintf
. Kami tahu ini karenasprintf
merupakan langkah selanjutnya. Tetapi bahkan tanpa itu menyatakan itu, Anda tahu ini karena itulah yang terjadi pada baris 26 , dan ia mengatakan:Dalam program Anda, baris 26 berisi:
(Anda harus selalu menggunakan editor teks yang secara otomatis menampilkan nomor baris, setidaknya untuk baris yang sedang Anda gunakan. Ini sangat membantu dalam menafsirkan kesalahan waktu kompilasi, dan masalah runtime yang terungkap saat menggunakan debugger.)
Sebagaimana dibahas dalam jawaban Dennis Kaarsemaker ,
s
adalah array satu byte. (Bukan nol, karena nilai yang Anda tetapkan""
, adalah panjang satu byte, yaitu sama dengan{ '\0' }
, dengan cara yang"Hello, world!\n"
sama dengan{ 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }
.)Jadi, mengapa ini masih bisa bekerja pada beberapa platform (dan ternyata tidak ketika dikompilasi dengan VC9 untuk Windows)?
Orang sering mengatakan bahwa ketika Anda mengalokasikan memori dan kemudian mencoba mengakses memori di luarnya, itu menghasilkan kesalahan. Tapi itu tidak sepenuhnya benar. Menurut standar teknis C dan C ++, apa yang sebenarnya dihasilkan ini adalah perilaku yang tidak terdefinisi.
Dengan kata lain, apa pun bisa terjadi!
Namun, beberapa hal lebih mungkin daripada yang lain. Mengapa array kecil pada stack akan, pada beberapa implementasi, tampak berfungsi seperti array yang lebih besar pada stack?
Ini bermuara pada bagaimana alokasi tumpukan diimplementasikan, yang diizinkan bervariasi dari platform ke platform. Eksekusi Anda dapat mengalokasikan lebih banyak memori ke tumpukan daripada yang sebenarnya dimaksudkan untuk digunakan pada satu waktu. Kadang-kadang ini memungkinkan Anda untuk menulis ke lokasi memori yang belum Anda klaim secara eksplisit dalam kode Anda. Sangat mungkin inilah yang terjadi ketika Anda membangun program Anda di VC9.
Namun, Anda tidak harus mengandalkan perilaku ini bahkan di VC9. Ini berpotensi bergantung pada versi pustaka yang berbeda yang bisa ada pada sistem Windows yang berbeda. Tetapi yang lebih mungkin adalah masalah bahwa ruang stack tambahan dialokasikan dengan maksud bahwa itu akan benar-benar digunakan, dan karenanya sebenarnya dapat digunakan.Kemudian Anda mengalami mimpi buruk penuh "perilaku tidak terdefinisi," di mana, dalam hal ini, lebih dari satu variabel dapat disimpan di tempat yang sama, di mana menulis ke satu menimpa yang lain ... tetapi tidak selalu, karena kadang-kadang menulis ke variabel di-cache dalam register dan tidak benar-benar dilakukan segera (atau membaca ke variabel dapat di-cache, atau suatu variabel dapat diasumsikan sama dengan sebelumnya karena memori yang dialokasikan untuk itu diketahui oleh kompiler tidak dituliskan melalui variabel itu sendiri).
Dan itu membawa saya ke kemungkinan lain mengapa program bekerja ketika dibangun dengan VC9. Mungkin, dan agak mungkin, beberapa array atau variabel lain sebenarnya dialokasikan oleh program Anda (yang dapat mencakup dialokasikan oleh perpustakaan yang digunakan program Anda) untuk menggunakan ruang setelah array satu byte
s
. Jadi, memperlakukans
sebagai array lebih dari satu byte akan memiliki efek mengakses konten dari variabel / array tersebut, yang juga bisa menjadi buruk.Kesimpulannya, ketika Anda memiliki kesalahan seperti ini, untungnya mendapatkan kesalahan seperti "kesalahan segmentasi" atau "kesalahan perlindungan umum." Ketika Anda tidak memilikinya, Anda mungkin tidak tahu sampai sudah terlambat bahwa program Anda memiliki perilaku yang tidak jelas.
sumber
Halo buffer overflow!
Anda mengalokasikan satu byte untuk sebuah string pada stack dan kemudian melanjutkan untuk menulis lebih dari satu byte untuknya. Dan to top it off, Anda membaca di luar akhir array itu. Silakan baca manual C dan terutama bagian tentang string dan mengalokasikan memori untuknya.
sumber