2016-02-12 242 views
0

我一直在寫一個非常小的C程序,我遇到了很多問題。通過我可以找到的所有堆棧溢出文章來看,但沒有取得太大的成功。該程序假設對純文本字符串使用非常簡單的異或「加密」。輸入字符串和鍵都是6個字符長。我是C和指針的新手。我認爲我沒有掌握一些語言的基礎知識。在C函數中返回char數組

#include <stdio.h> 
#include <string.h> 

#define LENGTH 7 
#define KEY "secret" 

char * encryptDecrypt(char *plaintext); 

int main(int argc, char **argv) 
{ 
    if(argc > 1) 
    { 
     char *plainText = argv[1]; 
     printf("Encrypting plaintext: %s (%i)\n", plainText, strlen(plainText)); 
     char *cipherText = encryptDecrypt(plainText); 
     printf("Encrypted: %s (%i)\n", cipherText, strlen(cipherText)); 
     char *decryptedText = encryptDecrypt(cipherText); 
     printf("Decrypted: %s (%i)\n", decryptedText, strlen(decryptedText)); 
    } 
    return 0; 
} 

char * encryptDecrypt(char *text) 
{ 
    char result[LENGTH]; 
    for (int i = 0; i < LENGTH-1; i++) 
    { 
     result[i] = (char)(text[i]^KEY[i]); 
    } 
    char *resultPtr = &result; 
    return resultPtr; 
} 

用Arg 「foobar的」 輸出運行程序:

加密明文:foobar的(6)

加密:╠╠╠╠╠╠╠╠T¨(19)

解密的:╠╠╠╠╠╠╠╠T¨(19)

問題:

  1. 打印指向結果數組的指針在encryptDecrypt函數中使用並返回後,會有所不同
  2. 在密文上使用XOR不會將其還原爲原始純文本(儘管因爲無論我的' m打印錯誤,這部分可能沒有問題)
  3. 加密/解密文本的字符串長度是19個字符長嗎?如果原始字符串是6個字符,這怎麼可能?
+1

'char result [LENGTH]; ... char * resultPtr =&result;返回resultPtr;'。不可以。您不能返回本地變量的地址。這是未定義的行爲。 –

+1

[指向本地變量的指針]的可能重複(http://stackoverflow.com/questions/4570366/pointer-to-local-variable) – 2501

+0

安全注意事項:1)在使用它們後清理純文本緩衝區memset(decryptedText ,0,its_size)'2)使用'strdup()'複製plaint文本數據是針對#1的一個問題。最好只在處理純文本數據時使用自己的代碼,而不是在內存中放置它的潛在副本。 – chux

回答

4
char *resultPtr = &result; 
return resultPtr; 

你不能這樣做。 result函數結束時不存在,則不能返回result

修改你的函數是這樣的:

void encryptDecrypt(char *text, char *result) 
{ 
    for (int i = 0; i < LENGTH - 1; i++) 
    { 
    result[i] = (char)(text[i]^KEY[i]); 
    } 
} 

創建在調用者網站的數組,然後將其作爲result。例如

char result[LENGTH] = {0}; 
encryptDecrypt(plainText, result); 

注意:實際使用%s打印加密的數據是不是最好的主意,因爲例如作爲XOR的結果,你可能會在文本之間的空字節,這將被視爲您的字符串爲空終止符,並且printf不會顯示其餘字符串。考慮用於打印密文的東西如this

您的格式說明符strlen也是錯誤的,請使用%zu insted %i,否則您將觸發undefined behaviour

+0

會使用'char ** result'作爲指針處理的更多保存方式嗎?我不太確定,如果'char * result'將起作用。順便說一句:使用'malloc'將解決問題。 – LittleByBlue

+1

@LittleByBlue它會以它的方式工作,你可以測試。沒有malloc,我已經避免了內存管理問題,因爲OP假定了固定大小。 –

+0

'char * resultPtr =&result;'是無效的,因爲'&result'的類型是'char **'。 – LittleByBlue

3

你不能返回一個指向局部變量的指針。你必須要麼

  • 傳遞一個緩衝區的地址
  • malloc的空間,並返回它(然後調用者必須釋放它)
  • 的strdup局部變量和返回(這實際上就是的malloc結束了你)

你不能返回一個指向局部變量的原因是因爲當你退出功能,他們被摧毀

+0

'strdup()'不會在'result'上工作。 'strdup()'依賴空字符表示結束。 'result [i] =(char)(text [i]^KEY [i]);'打破合同。需要malloc /複製'LENGTH'或'LENGTH + 1'。 – chux

3

encryptDecrypt是返回一個局部變量的地址。一旦函數返回,該內存無效,因此試圖使用它是undefined behavior

您應該使用malloc爲該功能的結果動態分配內存。此外,您應該傳遞字符串的大小,以便知道需要分配多少空間。

將加密的字符串打印爲字符串也是未定義的行爲,因爲您擁有的不是一個字符串,而是一個不以null結尾的字符數組。

另外,打印sizeof的結果時,請務必使用%zu格式說明符。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define LENGTH 7 
#define KEY "secret" 

char * encryptDecrypt(char *text, int len); 

int main(int argc, char **argv) 
{ 
    if(argc > 1) 
    { 
     char *plainText = argv[1]; 
     // use the string length + 1 to include the null terminator 
     int len = strlen(plainText)+1; 
     printf("Encrypting plaintext: %s (%zu)\n", plainText, strlen(plainText)); 
     char *cipherText = encryptDecrypt(plainText,len); 
     //printf("Encrypted: %s (%zu)\n", cipherText, strlen(cipherText)); 
     char *decryptedText = encryptDecrypt(cipherText,len); 
     printf("Decrypted: %s (%zu)\n", decryptedText, strlen(decryptedText)); 
     // clean up the allocated memory 
     free(cipherText); 
     free(decryptedText); 
    } 
    return 0; 
} 

char * encryptDecrypt(char *text, int len) 
{ 
    char *result = malloc(len); // allocate memory for the result 
    for (int i = 0; i < len; i++) 
    { 
     // if the text is longer that the key, wrap around on the key 
     result[i] = (char)(text[i]^KEY[i%LENGTH]); 
    } 
    return result; // return the allocated buffer 
} 

編輯:

修正了KEY索引,以防止overruning它。

+0

沒有downvote,但我認爲它是空終止,檢查,他只加密前6個字母 –

+0

@GiorgiMoniava我在'strlen(plainText)+ 1'傳遞的加密/解密長度,以便空終止符將被拾取向上。這樣,它不依賴於字符串的長度,並且它不需要在解密後手動將null終止。 – dbush

+0

我沒有批評你的代碼 - 我說他的空終止,即使加密我猜 –

2

encryptDecrypt函數中的結果字符數組僅是此函數的局部變量,只要encryptDecrypt函數正在執行,它的內容將保留在內存中。一旦函數執行完成,數組的結果內容可能會或可能不會出現在內存中(如果內容被破壞,那麼它將包含一些垃圾值)。

數組名稱本身指向數組的第一個元素。
因此char *resultPtr = &result;正在發送指向第一個元素的指針地址。 相反,你應該寫char *resultPtr = &result[0];char *resultPtr = result;

爲了使您的代碼的工作,取代
char result[LENGTH];

char * result = (char*)malloc(sizeof(LENGTH)); 
result[LENGTH-1] = '\0';  // To make result string null terminated 

malloc函數用於動態內存分配,現在出現在結果數組中的內容將保持在內存中(甚至在函數執行結束後),直到使用自由函數從內存中明確釋放。

注:即使加密後的結果字符串爲空終止,但我們不能用printf("%s",result);打印,因爲加密方法使用XOR甚至可以使字符串作爲空terrminator的其他字符。要打印這樣的字符串,您必須創建自己的函數來打印結果數組的前6個字節。

+0

'(char *)'cast不是必要的,因爲'malloc'返回'void *'。但很好的答案。 – LittleByBlue

+0

只能用malloc替換不會有幫助,請參閱dbush的回答。在OPs情況下,第7個字節不是空的。 –

+0

「數組結果競賽被銷燬(它會包含一些垃圾值)」--->確信C沒有指定這個。在函數結束後使用'&result'就是UB。數據可能在那裏,可能不是。 – chux