Saya mencoba mengembalikan string C dari suatu fungsi, tetapi tidak berfungsi. Ini kode saya.
char myFunction()
{
return "My String";
}
Dalam main
saya menyebutnya seperti ini:
int main()
{
printf("%s", myFunction());
}
Saya juga telah mencoba beberapa cara lain myFunction
, tetapi tidak berhasil. Sebagai contoh:
char myFunction()
{
char array[] = "my string";
return array;
}
Catatan: Saya tidak diperbolehkan menggunakan pointer!
Sedikit latar belakang tentang masalah ini:
Ada fungsi yang mencari tahu bulan apa ini. Misalnya, jika 1 maka itu mengembalikan Januari, dll.
Jadi ketika akan mencetak, itu melakukan seperti ini: printf("Month: %s",calculateMonth(month));
. Sekarang masalahnya adalah bagaimana mengembalikan string itu dari calculateMonth
fungsi.
return 0
tersirat secara default hanya di C99 (dan C ++) tetapi tidak di C90.Jawaban:
Tanda tangan fungsi Anda harus:
Latar Belakang:
Ini sangat mendasar untuk C & C ++, tetapi sedikit lebih banyak diskusi harus dilakukan.
Dalam C (& C ++ dalam hal ini), string hanyalah array byte yang diakhiri dengan byte nol - maka istilah "string-nol" digunakan untuk mewakili rasa string tertentu ini. Ada jenis string lain, tetapi di C (& C ++), rasa ini secara inheren dipahami oleh bahasa itu sendiri. Bahasa lain (Java, Pascal, dll.) Menggunakan metodologi berbeda untuk memahami "string saya".
Jika Anda pernah menggunakan Windows API (yang ada di C ++), Anda akan melihat parameter fungsi yang cukup teratur seperti: "LPCSTR lpszName". Bagian 'sz' merepresentasikan gagasan 'string-zero' ini: array byte dengan terminator null (/ nol).
Klarifikasi:
Demi 'intro' ini, saya menggunakan kata 'bytes' dan 'characters' secara bergantian, karena cara ini lebih mudah dipelajari. Ketahuilah bahwa ada metode lain (karakter lebar, dan sistem karakter multi-byte ( mbcs )) yang digunakan untuk menangani karakter internasional. UTF-8 adalah contoh mbcs. Demi intro, saya diam-diam 'melewatkan' semua ini.
Penyimpanan:
Ini berarti bahwa string seperti "string saya" sebenarnya menggunakan 9 + 1 (= 10!) Byte. Hal ini penting untuk diketahui saat Anda akhirnya bisa mengalokasikan string secara dinamis.
Jadi, tanpa 'penghentian nol' ini, Anda tidak memiliki string. Anda memiliki serangkaian karakter (juga disebut buffer) yang berkeliaran di memori.
Data umur panjang:
Penggunaan fungsinya seperti ini:
... umumnya akan membuat Anda mendapatkan kesalahan acak tidak tertangani / segmen dan sejenisnya, terutama 'di jalan'.
Singkatnya, meskipun jawaban saya benar - 9 kali dari 10 Anda akan mendapatkan program yang macet jika Anda menggunakannya seperti itu, terutama jika menurut Anda 'praktik yang baik' untuk melakukannya dengan cara itu. Singkatnya: Biasanya tidak.
Misalnya, bayangkan suatu saat nanti, string sekarang perlu dimanipulasi dengan cara tertentu. Umumnya, pembuat kode akan 'mengambil jalan yang mudah' dan (mencoba) menulis kode seperti ini:
Artinya, program Anda akan macet karena kompiler (mungkin / mungkin tidak) telah melepaskan memori yang digunakan
szBuffer
pada saatprintf()
inmain()
dipanggil. (Kompiler Anda juga harus memperingatkan Anda tentang masalah seperti itu sebelumnya.)Ada dua cara untuk mengembalikan string yang tidak akan mudah muntah.
std::string
) untuk menangani umur panjang data (yang memerlukan perubahan nilai kembalian fungsi), atauPerhatikan bahwa tidak mungkin menggunakan string tanpa menggunakan pointer di C. Seperti yang telah saya tunjukkan, string itu sama. Bahkan di C ++ dengan kelas template, selalu ada buffer (yaitu pointer) yang digunakan di latar belakang.
Jadi, untuk menjawab dengan lebih baik (pertanyaan yang sekarang dimodifikasi). (Pasti ada berbagai 'jawaban lain' yang dapat diberikan.)
Jawaban yang Lebih Aman:
Contoh 1, menggunakan string yang dialokasikan secara statis:
Apa yang dilakukan 'statis' di sini (banyak pemrogram tidak menyukai jenis 'alokasi' ini) adalah string dimasukkan ke dalam segmen data program. Artinya, dialokasikan secara permanen.
Jika Anda pindah ke C ++, Anda akan menggunakan strategi serupa:
... tapi mungkin lebih mudah menggunakan kelas helper, seperti
std::string
, jika Anda menulis kode untuk Anda gunakan sendiri (dan bukan bagian dari pustaka untuk dibagikan dengan orang lain).Contoh 2, menggunakan buffer yang ditentukan pemanggil:
Ini adalah cara yang lebih 'sangat mudah' untuk menyebarkan string. Data yang dikembalikan tidak tunduk pada manipulasi oleh pihak yang menelepon. Artinya, contoh 1 dapat dengan mudah disalahgunakan oleh pihak yang menelepon dan membuat Anda terkena kesalahan aplikasi. Dengan cara ini, jauh lebih aman (meskipun menggunakan lebih banyak baris kode):
Ada banyak alasan mengapa metode kedua lebih baik, terutama jika Anda menulis perpustakaan untuk digunakan oleh orang lain (Anda tidak perlu mengunci skema alokasi / deallokasi tertentu, pihak ketiga tidak dapat memecahkan kode Anda, dan Anda tidak perlu menautkan ke pustaka manajemen memori tertentu), tetapi seperti semua kode, terserah Anda tentang apa yang paling Anda sukai. Oleh karena itu, kebanyakan orang memilih misalnya 1 sampai mereka terbakar berkali-kali sehingga mereka menolak untuk menulis seperti itu lagi;)
Penolakan:
Saya pensiun beberapa tahun yang lalu dan nilai C saya agak berkarat sekarang. Kode demo ini harus dikompilasi dengan baik dengan C (meskipun demikian, tidak masalah untuk kompiler C ++).
sumber
char *
, karena string literal di C memiliki tipechar[]
. Namun, mereka tidak boleh dimodifikasi dengan cara apa pun, jadiconst char*
lebih disukai mengembalikan (lihat securecoding.cert.org/confluence/x/mwAV ). Pengembalianchar *
mungkin diperlukan jika string akan digunakan dalam fungsi pustaka warisan atau eksternal yang (sayangnya) mengharapkanchar*
argumen sebagai, bahkan meskipun itu hanya akan membacanya. C ++, sebaliknya, memiliki literal stringconst char[]
bertipe (dan, karena C ++ 11, Anda juga dapat memilikistd::string
literal).fraught with problems
di bagian "Umur panjang data" sebenarnya benar-benar valid. Literal string memiliki masa pakai statis di C / C ++. Lihat tautan yang disebutkan Giorgi di atas.String AC didefinisikan sebagai penunjuk ke larik karakter.
Jika Anda tidak dapat memiliki pointer, menurut definisi Anda tidak dapat memiliki string.
sumber
void foo( char array[], int length)
. Tentu saja,array
ini adalah penunjuk di bawah tenda, tetapi ini bukan penunjuk "secara eksplisit", dan oleh karena itu mungkin lebih intuitif bagi seseorang yang mempelajari array tetapi yang belum terlalu mempelajari penunjuk.Perhatikan fungsi baru ini:
Saya mendefinisikan "array" sebagai statis. Jika tidak, saat fungsi berakhir, variabel (dan penunjuk yang Anda kembalikan) keluar dari ruang lingkup. Karena memori itu dialokasikan pada tumpukan, dan itu akan rusak. Kelemahan dari implementasi ini adalah kode tersebut tidak reentrant dan tidak threadsafe.
Alternatif lain adalah menggunakan malloc untuk mengalokasikan string di heap, lalu membebaskan di lokasi yang benar dari kode Anda. Kode ini akan menjadi reentrant dan threadsafe.
Seperti disebutkan dalam komentar, ini adalah praktik yang sangat buruk, karena penyerang kemudian dapat menyuntikkan kode ke aplikasi Anda (dia perlu membuka kode menggunakan GDB, lalu membuat breakpoint dan mengubah nilai variabel yang dikembalikan menjadi overflow dan kesenangan baru saja dimulai).
Jauh lebih disarankan untuk membiarkan pemanggil menangani tentang alokasi memori. Lihat contoh baru ini:
Perhatikan bahwa satu-satunya konten yang dapat dimodifikasi adalah konten pengguna. Efek samping lain - kode ini sekarang threadsafe, setidaknya dari sudut pandang perpustakaan. Pemrogram yang memanggil metode ini harus memverifikasi bahwa bagian memori yang digunakan aman untuk thread.
sumber
Masalah Anda adalah dengan jenis fungsi yang dikembalikan - itu harus:
... dan kemudian formulasi asli Anda akan berhasil.
Perhatikan bahwa Anda tidak dapat memiliki string C tanpa melibatkan pointer, di suatu tempat di sepanjang baris.
Juga: Nyalakan peringatan kompiler Anda. Ini harus telah memperingatkan Anda tentang itu jalur kembali mengkonversi
char *
kechar
tanpa cast yang eksplisit.sumber
Berdasarkan backstory yang baru Anda tambahkan dengan pertanyaan, mengapa tidak mengembalikan integer dari 1 hingga 12 untuk bulan tersebut, dan biarkan fungsi main () menggunakan pernyataan switch atau tangga if-else untuk memutuskan apa yang akan dicetak? Ini tentu bukan cara terbaik untuk pergi - char * akan - tetapi dalam konteks kelas seperti ini, saya membayangkan itu mungkin yang paling elegan.
sumber
Anda bisa membuat larik di pemanggil, yang merupakan fungsi utama, dan meneruskan larik ke callee yang merupakan myFunction () Anda. Dengan demikian myFunction dapat mengisi string ke dalam array. Namun, Anda perlu mendeklarasikan myFunction () sebagai
Dan di fungsi utama, myFunction harus dipanggil dengan cara ini:
Namun, penunjuk masih digunakan.
sumber
Jenis pengembalian fungsi Anda adalah karakter tunggal (
char
). Anda harus mengembalikan pointer ke elemen pertama dari larik karakter. Jika Anda tidak dapat menggunakan pointer, maka Anda sedang kacau. :(sumber
Atau bagaimana dengan yang ini:
Dan sebut itu dengan bulan Anda menghitung di tempat lain.
sumber
A
char
hanya satu karakter satu byte. Itu tidak dapat menyimpan string karakter, juga bukan penunjuk (yang tampaknya tidak dapat Anda miliki). Oleh karena itu, Anda tidak dapat menyelesaikan masalah Anda tanpa menggunakan pointer (yangchar[]
merupakan gula sintaksis).sumber
Jika Anda benar-benar tidak dapat menggunakan pointer, lakukan sesuatu seperti ini:
Angka ajaib 9 itu buruk, dan ini bukan contoh pemrograman yang bagus. Tapi Anda mengerti maksudnya. Perhatikan bahwa pointer dan array adalah hal yang sama (sejenis), jadi ini sedikit curang.
sumber
Nah, dalam kode Anda, Anda mencoba untuk mengembalikan
String
(dalam C yang tidak lain adalah array karakter yang diakhiri dengan null), tetapi jenis kembalian dari fungsi Andachar
yang menyebabkan semua masalah bagi Anda. Sebaliknya Anda harus menulis seperti ini:Dan selalu bagus untuk memenuhi syarat tipe Anda
const
saat menetapkan literal dalam C ke pointer karena literal dalam C tidak dapat dimodifikasi.sumber
Prototipe fungsi Anda menyatakan bahwa fungsi Anda akan mengembalikan karakter. Jadi, Anda tidak bisa mengembalikan string dalam fungsi Anda.
sumber
Di C, literal string adalah array dengan kelas memori konstan statis, jadi mengembalikan pointer ke array ini aman. Lebih detail ada di pertanyaan Stack Overflow "Life-time" dari string literal di C
sumber
Kembalikan string dari fungsi
sumber