我知道在C,可申報一個字符串,像下面的字符數,用C確定字符的字符串的長度 - 如果用戶輸入字符串的內容
char mystring[50];
與「50」是字符數。
但是,如果用戶要輸入字符串的內容(通過scanf(「%s」,mystring);),什麼是正確的過程?我是否將其保留爲:
char mystring[0];
將它留爲'0',因爲我不知道用戶將輸入多少個字符?
還是我做的,
char mystring[400];
放棄以400個字符的用戶輸入?
我知道在C,可申報一個字符串,像下面的字符數,用C確定字符的字符串的長度 - 如果用戶輸入字符串的內容
char mystring[50];
與「50」是字符數。
但是,如果用戶要輸入字符串的內容(通過scanf(「%s」,mystring);),什麼是正確的過程?我是否將其保留爲:
char mystring[0];
將它留爲'0',因爲我不知道用戶將輸入多少個字符?
還是我做的,
char mystring[400];
放棄以400個字符的用戶輸入?
你遇到了scanf()和%s的確切問題 - 當你不知道有多少輸入時會發生什麼?
如果您嘗試運行char mystring[0];
,您的程序將編譯得很好。但是你總是會出現段錯誤。您正在創建一個大小爲0的數組,因此當您嘗試將某個放入該數組時,您將立即跳出您的字符串的界限(因爲沒有分配內存) - 這是段錯誤。
所以,第1點:你應該總是爲你的字符串分配一個大小。我可以想到很少的情況(好的,沒有)你想說char mystring[0]
而不是char *mystring
。
接下來,當您使用scanf時,您從不想使用「%s」說明符 - 因爲這不會執行任何邊界檢查字符串的大小。所以即使你有:
char mystring[512];
scanf("%s", mystring);
如果用戶輸入超過511個字符(因爲第512個是\ 0),你會去你的數組的邊界的。補救這個問題的方法是:
scanf("%511s", mystring);
這一切說C沒有一個工廠,如果有更多的投入比你期待的自動調整的字符串。這是你必須手動完成的事情。
解決此問題的一種方法是使用fgets()。
你可以說:
while (fgets(mystring, 512, stdin))
{
/* process input */
}
然後你可以使用的sscanf()來解析mystring
試試上面的代碼,長度爲5的字符串後4個字符已閱讀,代碼循環再次檢索其餘的輸入。 「處理」可能包括代碼重新分配一個字符串是一個更大的尺寸,然後附加fgets()的最新輸入。
上面的代碼並不完美 - 它會讓你的程序循環和處理任何無限長的字符串,所以你可能想對它有一些內部的硬性限制(例如循環最多10次)。
用戶將始終能夠輸入更多字符,從而使您的緩衝區溢出(一種常見的安全漏洞來源)。你可以,但是,指定「字段寬度」來scanf函數,像這樣:
scanf("%50s", mystring);
在這種情況下,您的緩衝區應該是51個字符,以佔50字符字段加空終止。或者讓你的緩衝區50個字符,並告訴scanf 49是寬度。
但是當聲明字符串時,我應該指定'0'還是一些大數字? – HollerTrain 2009-11-29 03:49:22
在本例中,您應該至少指定51。 (空終止符的長度+1。) – Thanatos 2009-11-29 03:50:30
好的。所以當聲明字符串不正確的編碼時,將它列爲'0'?我的問題是我不知道有多少用戶會輸入,但同時想要學習正確的方法... – HollerTrain 2009-11-29 03:51:58
有一個名爲ggets()的函數,它不是標準C庫的一部分。 這是一個相當簡單的功能。它使用malloc()初始化一個char數組。然後它每次從stdin中讀取一個字符的字符。它跟蹤有多少個字符被讀取,並在空間不足時使用realloc()擴展字符數組。
它可以在這裏找到:http://cbfalconer.home.att.net/download/index.htm
我建議你閱讀代碼,並重新實現自己。
C中的通常的做法是使用類似GNU readline或許NetBSD editline, aka libedit.(同樣的API,不同的實現和軟件許可。)
對於簡單或作業程序,理論上你可以給一個字段寬度SCANF ,但更常見的做法是將fgets()
固定寬度的陣列,然後在其上運行sscanf()
。這樣你就可以控制讀取的行數。
作爲一個例子,如果用戶輸入他們的名字,那麼你並不總是安全地將'mystring'的大小最大化爲35個字符,因爲有些人有很長的名字。您不希望觸及用戶無法完整輸入您請求的信息的情況。正確的做法是製作一個尺寸非常大的臨時緩衝區,以覆蓋用戶所有可能的輸入。一旦用戶輸入信息並將其存儲到緩衝區中,您就可以將緩衝區中的字符傳送到mystring,同時切斷緩衝區末尾的所有額外空間。你將能夠精確地告訴'mystring'所需的大小,並且你可以爲它分配足夠的空間並丟棄緩衝區。這樣你就不會在程序的其餘部分使用更多內存的字符串......你只會使用一個帶有你需要的內存量的字符串。
您仍然需要進行一些檢查,以確保用戶輸入的內容不會大於在極少數情況下分配的緩衝區或者某人正在嘗試利用您的程序時分配的緩衝區。 – 2009-11-29 04:16:33
這是cbfalconer的代碼(http://cbfalconer.home.att.net/download/index.htm)與一對夫婦細微的修改和編譯成一個文件:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ggets.h"
#define INITSIZE 112 /* power of 2 minus 16, helps malloc */
#define DELTASIZE (INITSIZE + 16)
enum {OK = 0, NOMEM};
int fggets(char* *ln, FILE *f)
{
int cursize, ch, ix;
char *buffer, *temp;
*ln = NULL; /* default */
if (NULL == (buffer = malloc(INITSIZE))) return NOMEM;
cursize = INITSIZE;
ix = 0;
while ((EOF != (ch = getc(f))) && ('\n' != ch)) {
if (ix >= (cursize - 1)) { /* extend buffer */
cursize += DELTASIZE;
if (NULL == (temp = realloc(buffer, (size_t)cursize))) {
/* ran out of memory, return partial line */
buffer[ix] = '\0';
*ln = buffer;
return NOMEM;
}
buffer = temp;
}
buffer[ix++] = ch;
}
if ((EOF == ch) && (0 == ix)) {
free(buffer);
return EOF;
}
buffer[ix] = '\0';
if (NULL == (temp = realloc(buffer, (size_t)ix + 1))) {
*ln = buffer; /* without reducing it */
}
else *ln = temp;
return OK;
} /* fggets */
/* End of ggets.c */
int main(int argc, char **argv)
{
FILE *infile;
char *line;
int cnt;
//if (argc == 2)
//if ((infile = fopen(argv[1], "r"))) {
cnt = 0;
while (0 == fggets(&line, stdin)) {
fprintf(stderr, "%4d %4d\n", ++cnt, (int)strlen(line));
(void)puts(line);
free(line);
}
return 0;
//}
//(void)puts("Usage: tggets filetodisplay");
//return EXIT_FAILURE;
} /* main */
/* END file tggets.c */
我測試了它,它總是會給你想要的東西。
基本上,要獲得他的原始代碼,您可以取消註釋並在fggets調用中用infile替換stdin。 – 2009-11-29 05:15:37
應該加上%s讀單詞,而不是整個字符串。因爲scanf格式字符串使用空格和換行符作爲分隔符。在這種情況下,請使用%c來代替(使用字段寬度),或者像上面提到的那樣使用fgets。在字段寬度爲%c的情況下,請記住將整個緩衝區字符串初始化爲零。 – 2009-11-29 04:55:09
程序不會總是出現段錯誤。事實上,可能不是大部分時間。你的程序可能會被無聲地破壞。 C不可愛嗎? :-) – 2009-11-29 17:57:43