/*
* code.c
*
* TASK
* Reverse a string by reversing pointers. Function should use return
* type char* and use a char* parameter as input.
*/
#include <stdio.h>
#include <string.h>
#define STRMAX 51
char* reverse(char* sPhrase[]);
int main() {
char sPhrase[STRMAX];
char sReverse[STRMAX];
printf("Enter string (max. 50 chars): ");
gets(sPhrase);
sReverse = reverse(sPhrase);
return 0;
}
char* reverse(char* sPhrase[]) {
char* sOutput[STRMAX];
int iCnt = 0, iCntRev;
for (iCntRev = strlen(*sPhrase)-2; iCntRev >= 0; iCntRev--) {
sOutput[iCnt] = sPhrase[iCntRev];
iCnt++;
}
*sOutput[iCnt] = '\0'; // Don't forget to close the string
return sOutput;
}
此代码有一些怪癖:
sReverse = reverse(sPhrase);
return sOutput;
这些警告意味着什么?我该如何修补错误?该函数应该将char *作为返回类型和参数保留,因为我将这个小程序作为C培训课程的一部分。
答案 0 :(得分:6)
我看到一些问题。首先,char* sOutput[STRMAX]
是一个char*
的数组 - 大概是你的意思char sOutput[STRMAX]
?
其次,更重要的是,当你以这种方式在函数中声明一个数组(char sOutput[STRMAX]
)时,它会在堆栈上分配,并在函数返回时解除分配。因此,如果你试图返回它,你会得到未定义的结果,因为它在技术上不再存在了!
解决方案是将缓冲区传递给函数以供其使用:
char* reverse(char const sPhrase[], char sOutput[])
(我添加了const
,因此您不会意外覆盖sPhrase)。然后像这样拨打reverse
:
reverse(sPhrase, sReverse);
现在你的算法是否有效......
答案 1 :(得分:3)
让一些事情摆脱困境:
永远不会使用gets()
; 将在您的程序中引入故障点。如果你传递的是一个大小为10个字符的缓冲区,并且用户输入100个字符,那么gets()
会很快将这些额外的90个字符写入紧跟缓冲区后的内存中,从而导致各种混乱。缓冲区溢出是一种简单且常见的恶意软件攻击,使用gets()
的任何代码都是设计不安全的 。请改用fgets()
。
您不能像在行sReverse = reverse(sPhrase);
中那样分配数组对象。如果要复制reverse
返回的字符串的内容,则需要使用strcpy()
或类似内容:strcpy(sReverse, reverse(sPhrase));
“赋值中的不兼容类型”诊断来自于您尝试将指针值(char *
)分配给数组对象(char [STRMAX]
)的事实。正如我上面提到的,无论如何都无法分配给数组对象。
“来自不兼容类型的传递参数”警告来自这样的事实:您的函数定义未被类型化以期望指向char的指针,而是指向指向char的指针。将功能定义更改为
char *reverse(char *sPhrase) {...}
为什么*sPhrase
代替sPhrase[]
(或*sPhrase[]
)?首先,当数组表达式出现在大多数上下文中时,其类型被隐式地从“N元素数组T”转换为“指向T”(表达式衰减到指针类型)并且其值设置为数组中第一个元素的地址;此规则的例外情况是,当数组表达式是sizeof
或&
(地址)运算符的操作数时,或者数组表达式是用于初始化数组的字符串文字时声明中的char。其次,在函数参数定义的上下文中,T a[]
与T *a
相同;两种形式都将a
声明为指向T.的指针。
当您在reverse(sPPhrase);
中调用main
时,表达式sPPhrase
的类型从“STRMAX元素数组char”衰减到“指向char”,因此形式参数在该函数需要输入为char *
。
您仍然可以将下标运算符应用于sPhrase,因为下标是根据指针算法定义的,但请记住,sPhrase是指针值,而不是数组。
正如您目前所写的那样,reverse
需要一个char *[]
类型的参数,它与char **
相同,就好像您将一个指针数组传递给char,而不是一个char数组。同样,sOutput
中的reverse
变量需要声明char sOutput[STRMAX];
,而不是char *sOutput[STRMAX];
(后者声明sOutput是指向char的指针数组,这不是你想要的这里;这是“从不兼容类型返回”警告的来源。
“局部变量的返回地址”警告来自于您尝试返回函数本地变量的地址并具有自动范围的事实。函数退出后,该变量不再存在,并且存储在该位置的值可能不再有效。有几种方法可以解决这个问题:
将sOutput声明为静态:static char sOutput[STRMAX];
。这将导致sOutput的内存在程序启动时分配并保持分配直到程序退出,因此数组的内容将在调用reverse
之间保持不变。该变量仍然是函数的本地变量(它不能通过该函数之外的名称访问)。但是,这意味着该功能不再是线程安全的,而且这是一个丑陋的解决方案。
在reverse
内动态分配缓冲区并返回该地址。这样做的好处是您可以根据需要调整缓冲区大小,而不必担心线程安全。缓冲区将持续存在,直到您显式释放它(或直到程序退出)。缺点是调用者现在负责在内存完成后释放该内存。避免头痛与内存管理的最好方法是首先避免内存管理,这个问题并没有真正要求它。
执行反向操作(即在输入数组中执行反向操作),并返回输入数组的地址。这意味着你正在破坏你的输入,这可能不是你想要的。
将目标数组作为第二个输入传递给函数,不要打扰返回值(或者,如果 返回某些内容,则返回目标数组的地址):
char *reverse(char *src, char *dst) { // write contents of src in reverse order to dst return dst; } ... reverse(sPPhrase, sPReverse);
这就是像strcpy()
这样的函数的工作原理,所以这是先例,这是所有选项中最不痛苦的。
请记住,C中的字符串处理是非常原语(我们在这里谈论石刀和熊),以及许多其他语言中有意义的概念(比如使用{{1}分配字符串内容)并不真正适用于C.
答案 2 :(得分:0)
类型char * sPhrase []实际上与char *不兼容,[]具有指针的语义,因此您对char **编码很多。 所以函数签名必须是:
char* reverse(char* sPhrase)
答案 3 :(得分:0)
char sPhrase[STRMAX];
是堆栈上的自动变量,因此无法重新分配地址。 应该被称为:
char *sPhrase;
这定义了一个指向char的STRMAX指针数组(作为自动变量,从函数返回后将被破坏)。
char* sOutput[STRMAX];
你需要的是:
char* sOutput = malloc( STRMAX );
因为你回来后不想让重复的字符串存活起来。
答案 4 :(得分:0)
我对指针很新,而且一般来说是c,但看起来反向()期待一个指针并且你传递了一个实际的字符数组。 尝试改变这条线:
char sPhrase[STRMAX];
为:
char *sPhrase[STRMAX];
答案 5 :(得分:0)
如果我将返回类型设置为void:
,则此方法有效/*
* code.c
*
* TASK
* Reverse a string by reversing pointers. Function should use return
* type char* and use a char* parameter as input.
*/
#include <stdio.h>
#include <string.h>
#define STRMAX 51
void reverse(char* sPhrase[]);
int main() {
char sPhrase[STRMAX];
char* sPPhrase[STRMAX];
char* sPReverse[STRMAX];
int iCntr;
printf("Enter string (max. 50 chars): ");
gets(sPhrase);
for (iCntr = 0; iCntr < strlen(sPhrase); iCntr++) {
sPPhrase[iCntr] = &sPhrase[iCntr];
}
reverse(sPPhrase); // Disabled return type char*
return 0;
}
void reverse(char* sPPhrase[]) {
char* sPOutput[STRMAX];
int iCnt = 0, iCntRev;
for (iCntRev = strlen(*sPPhrase)-2; iCntRev >= 0; iCntRev--) {
sPOutput[iCnt] = sPPhrase[iCntRev];
iCnt++;
}
*sPOutput[iCnt] = '\0'; // Don't forget to close the string
// return sPOutput; // Disabled return type char*
}
一旦我将返回类型重新发挥作用,就会出现问题。 sPReverse = reverse(sPPhrase);
仍会返回不兼容类型的赋值错误,即使返回类型与我存储返回指针数组的变量类型相同。
答案 6 :(得分:-2)
参数与变量名称不匹配。
reverse
有一个参数sPhrase
,它是指向字符数组的指针这解释了为什么存在“类型不匹配”。我已经包含了一个固定版本,试试这个。
char* reverse(char *sPhrase); int main() { char sPhrase[STRMAX]; char sReverse[STRMAX]; printf("Enter string (max. 50 chars): "); gets(sPhrase); sReverse = reverse(sPhrase); return 0; } char* reverse(char* sPhrase) { /*char* sOutput[STRMAX];*/ static char sOutput[STRMAX]; int iCnt = 0, iCntRev; for (iCntRev = strlen(sPhrase)-2; iCntRev >= 0; iCntRev--) { sPhrase[iCnt] = sOutput[iCntRev]; iCnt++; } sPhrase[iCnt] = '\0'; // Don't forget to close the string return sOutput; }
编译器非常聪明,可以检查“不兼容”类型。当这种事情发生时,C strict 非常放松,所以在确保声明与定义匹配以便成功编译时,你有责任。
我还更正了在sOutput
函数中使用reverse
变量的问题,并将其更改为static
,因为变量将超出范围并最终以垃圾,通过为关键字static
添加前缀将确保变量保持在范围内。
编辑:出于某种原因,我添加了打击标记以突破该行,但其他人正在以某种方式看到它...... ???
仔细观察,代码不起作用,我已修复它,考虑到其他人的输入和考虑,请注意我使用fgets
而不是邪恶的gets
函数,因为它是缓冲区溢出的滋生地,也改变了函数签名,使用const
关键字作为参数。
char * reverse(const char *sPhrase); int main() { char sPhrase[STRMAX]; char *sReverse; printf("Enter string (max. 50 chars): "); fgets(sPhrase, STRMAX - 1, stdin); sReverse = reverse(sPhrase); printf("Reversed string: %s\n", sReverse); return 0; } char *reverse(const char* sPhrase) { char* sOutput; int iCnt = 0, iCntRev; sOutput = (char *)malloc((STRMAX * sizeof(char)) + 1); if (sOutput){ for (iCntRev = strlen(sPhrase) - 1; iCntRev >= 0; iCntRev--) { *sOutput++ = sPhrase[iCntRev]; iCnt++; } *sOutput++ = '\0'; } return (sOutput - iCnt); }
这是一个很棒的tutorial指针。
希望这有帮助, 最好的祝福, 汤姆。