İşlev imzanız şu şekilde olmalıdır:
const char * myFunction()
{
return "My String";
}
Arka fon:
C & C ++ için çok önemlidir, ancak biraz daha fazla tartışma sırayla yapılmalıdır.
C'de (& C ++ bu konuda), bir dize sadece bir sıfır bayt ile sonlandırılmış bir bayt dizisidir - bu nedenle "dize-sıfır" terimi, bu özel dizi çeşidini temsil etmek için kullanılır. Başka tür dizgiler de vardır, ancak C (& C ++) 'da, bu tat doğal olarak dilin kendisi tarafından anlaşılır. Diğer diller (Java, Pascal, vb.) "Dizimi" anlamak için farklı metodolojiler kullanır.
Windows API'yi (C ++ içinde) kullanırsanız, "LPCSTR lpszName" gibi oldukça düzenli olarak işlev parametreleri görürsünüz. 'Sz' bölümü, bu 'dizge-sıfır' kavramını temsil eder: boş (/ sıfır) sonlandırıcıya sahip bir bayt dizisi.
Açıklama:
Bu 'giriş' uğruna, 'bayt' ve 'karakterler' kelimelerini birbirinin yerine kullanıyorum, çünkü bu şekilde öğrenmek daha kolay. Uluslararası karakterlerle başa çıkmak için kullanılan başka yöntemler (geniş karakterler ve çok baytlı karakter sistemleri ( mbcs )) olduğunu unutmayın. UTF-8 bir mbcs örneğidir. Giriş uğruna, tüm bunları sessizce 'atlıyorum'.
Hafıza:
Bu, "dizem" gibi bir dizenin aslında 9 + 1 (= 10!) Bayt kullandığı anlamına gelir. Bu, dizeleri dinamik olarak ayırmaya ne zaman başladığını bilmek önemlidir.
Yani, bu 'sonlandırıcı sıfır' olmadan, bir dizeniz olmaz. Hafızada asılı duran bir dizi karakteriniz (tampon da denir) var.
Verilerin uzun ömürlü olması:
İşlevin bu şekilde kullanılması:
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // Fraught with problems
printf("%s", szSomeString);
}
... sizi genellikle rastgele ele alınmayan istisnalar / segment hataları ve benzerleriyle, özellikle de 'yolun aşağısında' yönlendirir.
Kısacası, cevabım doğru olsa da - 10'da 9'u, bu şekilde kullanırsanız, özellikle de bu şekilde yapmanın 'iyi bir uygulama' olduğunu düşünüyorsanız, çöken bir programla karşılaşacaksınız. Kısaca: Genelde değil.
Örneğin, gelecekte bir zaman düşünün, dizginin artık bir şekilde manipüle edilmesi gerekiyor. Genel olarak, bir kodlayıcı 'kolay yolu seçer' ve şu şekilde kod yazmaya (dener):
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
Derleyici (/ veya olmayabilir) tarafından kullanılan bellek serbest etmelerinden Yani, programınız çökmesine olacak szBufferzaman printf()içinde main()denir. (Derleyiciniz sizi bu tür sorunlar konusunda önceden uyarmalıdır.)
Bu kadar kolay kusmayacak dizeleri döndürmenin iki yolu vardır.
- bir süre yaşayan tamponları (statik veya dinamik olarak ayrılmış) döndürür. C ++ '
std::stringda verilerin uzun ömürlülüğünü işlemek için' yardımcı sınıfları '(örneğin ) kullanın (bu, işlevin dönüş değerini değiştirmeyi gerektirir) veya
- bilgiyle doldurulan işleve bir tampon iletir.
C'de işaretçiler kullanmadan dizeleri kullanmanın imkansız olduğuna dikkat edin. Gösterdiğim gibi, bunlar eşanlamlıdır. C ++ şablon sınıflarında bile arka planda her zaman kullanılan tamponlar (yani işaretçiler) vardır.
Yani, (şimdi değiştirilmiş soruya) daha iyi cevap vermek için. (Verilebilecek çeşitli 'diğer yanıtlar' olduğundan emin olabilirsiniz.)
Daha Güvenli Cevaplar:
Örnek 1, statik olarak ayrılmış dizeleri kullanarak:
const char* calculateMonth(int month)
{
static char* months[] = {"Jan", "Feb", "Mar" .... };
static char badFood[] = "Unknown";
if (month<1 || month>12)
return badFood; // Choose whatever is appropriate for bad input. Crashing is never appropriate however.
else
return months[month-1];
}
int main()
{
printf("%s", calculateMonth(2)); // Prints "Feb"
}
Burada 'durağan' olan şey (birçok programcı bu tür 'ayırma'yı sevmez), dizelerin programın veri segmentine yerleştirilmesidir. Yani kalıcı olarak tahsis edilmiştir.
C ++ 'ya geçerseniz, benzer stratejiler kullanırsınız:
class Foo
{
char _someData[12];
public:
const char* someFunction() const
{ // The final 'const' is to let the compiler know that nothing is changed in the class when this function is called.
return _someData;
}
}
... ancak std::string, kodu kendi kullanımınız için yazıyorsanız (ve başkalarıyla paylaşılacak bir kitaplığın parçası değil) , yardımcı sınıfları kullanmak muhtemelen daha kolaydır .
Örnek 2, arayan tanımlı arabellekleri kullanarak:
Bu, ipleri etrafta dolaştırmanın daha 'kusursuz' yoludur. Döndürülen veriler, arayan tarafın müdahalesine tabi değildir. Yani, örnek 1 arayan taraf tarafından kolaylıkla kötüye kullanılabilir ve sizi uygulama hatalarına maruz bırakabilir. Bu şekilde, çok daha güvenlidir (daha fazla kod satırı kullansa da):
void calculateMonth(int month, char* pszMonth, int buffersize)
{
const char* months[] = {"Jan", "Feb", "Mar" .... }; // Allocated dynamically during the function call. (Can be inefficient with a bad compiler)
if (!pszMonth || buffersize<1)
return; // Bad input. Let junk deal with junk data.
if (month<1 || month>12)
{
*pszMonth = '\0'; // Return an 'empty' string
// OR: strncpy(pszMonth, "Bad Month", buffersize-1);
}
else
{
strncpy(pszMonth, months[month-1], buffersize-1);
}
pszMonth[buffersize-1] = '\0'; // Ensure a valid terminating zero! Many people forget this!
}
int main()
{
char month[16]; // 16 bytes allocated here on the stack.
calculateMonth(3, month, sizeof(month));
printf("%s", month); // Prints "Mar"
}
İkinci yöntemin daha iyi olmasının birçok nedeni vardır, özellikle başkaları tarafından kullanılmak üzere bir kitaplık yazıyorsanız (belirli bir tahsis / serbest bırakma şemasına kilitlenmeniz gerekmez, üçüncü taraflar kodunuzu kıramazlar, ve belirli bir bellek yönetimi kitaplığına bağlanmanıza gerek yoktur), ancak tüm kodlar gibi, en çok neyi seveceğiniz size bağlıdır. Bu nedenle, çoğu insan örneğin 1'i o kadar çok kez yanana kadar seçer ki artık o şekilde yazmayı reddederler;)
Yasal Uyarı:
Birkaç yıl önce emekli oldum ve şimdi C'm biraz paslanmış. Bu demo kodunun tümü C ile düzgün bir şekilde derlenmelidir (yine de herhangi bir C ++ derleyicisi için uygundur).