[英]How do I deal with garbage values in manual string concatenation?
初學者 C 問題,為了我正在編寫的代碼,我最終不得不手動將字符連接到中間有中斷的字符串,但是在正確連接字符串后它會吐出垃圾。
我不知道它有多少nono,但它在replit 中正確編譯,但是在VS2019 中它給了我垃圾。
據我所知, malloc 分配了內存但給出了一個空字符串,打印時只會顯示已插入的內容。
用 stringOut[i] 替換 stringOut[strlen(stringOut)] 會正確地將它放在開頭但仍然打印垃圾? 我如何清空分配的字符串,以便我可以手動向其中添加字符,而不會在最后拋出訪問異常和打印垃圾?
有什么想法嗎?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 50
char* stringOut;
char* inString;
//========================== MAIN ============================
int main() {
stringOut = (char*)malloc(SIZE*sizeof(char));
inString = (char*)malloc(SIZE*sizeof(char));
printf("s: ");
scanf("%s", inString);
//inString = "quarry/beef/foot";
int i = 0;
printf("%lu ",strlen(inString));
printf("%d\n",i);
while(inString[i] != '/')
{
//printf("doingSTUFF");
stringOut[strlen(stringOut)] = inString[i];
printf("%s\n", stringOut);
i++;
}
stringOut[strlen(stringOut)] = ' ';
printf("%s", stringOut);
}
你有大量的小問題,但主要的問題是stringOut
分配,但未初始化這樣strlen(stringOut)
所調用未定義行為,因為在沒有NUL-termiating字符stringOut
標識字符串的結束,即使它是NUL -terminated ,您將在循環中的每次迭代中覆蓋空終止字符,分配stringOut[strlen(stringOut)] = inString[i];
(更不用說每次迭代調用strlen(stingOut)
效率低下。
一切都沒有丟失,您的想法也並非完全錯誤。 只要你戴上你的會計師帽子並考慮到你添加到stringOut
的字符數,並在你完成添加字符后添加nul 終止字符( '\\0'
,相當於普通的舊0
),所有這些都將工作這應該。
在我們到達那里之前,了解為什么在 C 中,不需要malloc
的返回我是否要malloc
的結果? 並了解sizeof (char)
定義為1
,因此在設置分配大小時無需將其作為大小乘數包含在內。 按照順序,您的分配將是:
stringOut = malloc (SIZE); /* allocate stringOut */
if (stringOut == NULL) { /* VALIDATE EVERY ALLOCATION */
perror ("malloc-stringOut");
return 1;
}
inString = malloc (SIZE); /* allocate inString */
if (!inString) { /* ditto */
perror ("malloc-inString");
return 1;
}
獲取用戶輸入時,建議使用fgets()
為什么? 在給定足夠大小的緩沖區的情況下,它一次消耗一整行輸入,因此stdin
中保留的內容不依賴於scanf
format-string 。 唯一需要注意的是所有面向行的輸入函數,例如fgets()
和 POSIX getline()
讀取並包含來自輸入的'\\n'
並將其包含在它們填充的緩沖區中。 所以你需要從inString
刪除換行符。 幸運的是, strcspn(string, reject)
提供了一種簡單的方法來做到這一點,因為它返回string
未包含在reject
列表中的字符數。 所以你可以簡單地做:
inString[strcspn (inString, "\n")] = 0;
這將用空終止字符覆蓋'\\n'
字符,只留下用戶輸入的文本。 (如果您需要保留stringOut
的長度,您可以通過將strcspn
的返回值strcspn
在一個變量中來同時進行,例如
inString[(inlen = strcspn (inString, "\n"))] = 0;
這樣做完全相同,但也節省了inString
中inlen
的長度。 您可以保留另一個保存stringOut
長度的stringOut
(例如,在開始時聲明size_t inlen = 0, outlen = 0;
),以便您現在可以通過程序跟蹤inString
和stringOut
的長度。
現在要從inString
復制到stringOut
,您只需進行一些更改就可以在很大程度上使用您的循環。 最重要的是,您需要確保不要嘗試讀取inString
的末尾,以防其中不包含'/'
( inString[i] != 0
,或者只是inString[i]
)只需添加作為while
循環中的附加條件,您可以執行以下操作:
/* loop while not at end and char not '/' */
while (inString[i] && inString[i] != '/') {
stringOut[outlen++] = inString[i]; /* copy from inString to stringOut */
i++;
}
(注意:使用計數器outlen
來跟蹤stringOut
的字符stringOut
而不是重復調用strlen()
)
現在outString
在這一點上不是空終止的。 您仍然打算根據您的代碼在末尾添加一個space
( ' '
),因此添加space
然后終止stringOut
,例如
stringOut[outlen++] = ' '; /* append space, increment outlen */
stringOut[outlen] = 0; /* nul-terminate stringOut */
現在你的代碼已經完成,你可以輸出stringOut
,如果你用單引號將輸出括起來,你可以確認你的空格在那里,例如
printf("'%s'\n", stringOut); /* output stringOut */
不要忘記釋放您分配的內容,例如
free (inString); /* don't forget to free the memory you allocate */
free (stringOut);
總而言之,您將擁有:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 50
int main (void) {
char *stringOut;
char *inString;
size_t inlen = 0, outlen = 0, i = 0; /* inlen, outlen, counter */
stringOut = malloc (SIZE); /* allocate stringOut */
if (stringOut == NULL) { /* VALIDATE EVERY ALLOCATION */
perror ("malloc-stringOut");
return 1;
}
inString = malloc (SIZE); /* allocate inString */
if (!inString) { /* ditto */
perror ("malloc-inString");
return 1;
}
printf("s: "); /* prompt for input */
if (fgets (inString, SIZE, stdin) == NULL) { /* read line of input */
fputs ("(user canceled input)\n", stdout);
return 0;
}
inString[(inlen = strcspn (inString, "\n"))] = 0; /* get inlen, trim \n */
/* loop while not at end and char not '/' */
while (inString[i] && inString[i] != '/') {
stringOut[outlen++] = inString[i]; /* copy from inString to stringOut */
i++;
}
stringOut[outlen++] = ' '; /* append space, increment outlen */
stringOut[outlen] = 0; /* nul-terminate stringOut */
printf("'%s'\n", stringOut); /* output stringOut */
free (inString); /* don't forget to free the memory you allocate */
free (stringOut);
}
示例使用/輸出
$ ./bin/stringout
s: quarry/beef/foot
'quarry '
內存使用/錯誤檢查
在你寫的,可動態分配內存的任何代碼,您有任何關於分配的內存任何塊2個職責:(1)始終保持一個指針的起始地址的存儲器中,以便塊,(2),當它是沒有它可以被釋放不再需要。
您必須使用內存錯誤檢查程序來確保您不會嘗試訪問內存或寫入超出/超出分配塊的范圍,嘗試讀取或基於未初始化值的條件跳轉,最后確認你釋放了你分配的所有內存。
對於 Linux valgrind
是正常的選擇。 每個平台都有類似的內存檢查器。 它們都易於使用,只需通過它運行您的程序即可。
$ valgrind ./bin/stringout
==9727== Memcheck, a memory error detector
==9727== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9727== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==9727== Command: ./bin/stringout
==9727==
s: quarry/beef/foot
'quarry '
==9727==
==9727== HEAP SUMMARY:
==9727== in use at exit: 0 bytes in 0 blocks
==9727== total heap usage: 4 allocs, 4 frees, 2,148 bytes allocated
==9727==
==9727== All heap blocks were freed -- no leaks are possible
==9727==
==9727== For counts of detected and suppressed errors, rerun with: -v
==9727== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始終確認您已釋放所有分配的內存並且沒有內存錯誤。
仔細檢查一下,如果您還有其他問題,請告訴我。
我看到的幾個問題:
據我所知, malloc 分配了內存但給出了一個空字符串,打印時只會顯示已插入的內容。
malloc()
返回包含垃圾的內存塊。 不是“空字符串”。 但是,如果您對該內存使用strcpy()
某些內容,則該字符串將被正確終止,並且不應打印未復制的字符。
inString = (char*)malloc(SIZE*sizeof(char));
inString = "采石場/牛肉/腳";
在這里,您分配了一塊內存,將其分配給inString
,然后將其他內容分配給inString
。 malloc()
返回的內存地址現在丟失了,您無法釋放它。 不要覆蓋您分配的內存地址。
stringOut[strlen(stringOut)] = inString[i];
這是個問題。 stringOut
包含未初始化的內存。 它可以包含任何東西。 所以strlen(stringOut)
將掃描第一個'\\0'
字符,它可能是第一個字節,也可能是第 10,000 個字節。
stringOut[strlen(stringOut)] = ' ';
這是要終止字符串嗎? 如果是這樣,請使用'\\0'
而不是' '
。
另外,不要忘記使用free()
釋放分配的內存。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.