2016-02-12 243 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