C:char *的问题

时间:2010-01-22 13:39:30

标签: c pointers

/*
 * 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);

    • [错误]分配中的不兼容类型
    • [警告]从不兼容的指针类型
    • 传递“反向”的arg 1
  • return sOutput;

    • [警告]函数返回局部变量的地址
    • [警告]从不兼容的指针类型返回

这些警告意味着什么?我该如何修补错误?该函数应该将char *作为返回类型和参数保留,因为我将这个小程序作为C培训课程的一部分。

7 个答案:

答案 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)

让一些事情摆脱困境:

  1. 永远不会使用gets(); 在您的程序中引入故障点。如果你传递的是一个大小为10个字符的缓冲区,并且用户输入100个字符,那么gets()会很快将这些额外的90个字符写入紧跟缓冲区后的内存中,从而导致各种混乱。缓冲区溢出是一种简单且常见的恶意软件攻击,使用gets()的任何代码都是设计不安全的 。请改用fgets()

  2. 您不能像在行sReverse = reverse(sPhrase);中那样分配数组对象。如果要复制reverse返回的字符串的内容,则需要使用strcpy()或类似内容:strcpy(sReverse, reverse(sPhrase));

  3. “赋值中的不兼容类型”诊断来自于您尝试将指针值(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的指针数组,这不是你想要的这里;这是“从不兼容类型返回”警告的来源。

    “局部变量的返回地址”警告来自于您尝试返回函数本地变量的地址并具有自动范围的事实。函数退出后,该变量不再存在,并且存储在该位置的值可能不再有效。有几种方法可以解决这个问题:

    1. 将sOutput声明为静态:static char sOutput[STRMAX];。这将导致sOutput的内存在程序启动时分配并保持分配直到程序退出,因此数组的内容将在调用reverse之间保持不变。该变量仍然是函数的本地变量(它不能通过该函数之外的名称访问)。但是,这意味着该功能不再是线程安全的,而且这是一个丑陋的解决方案。

    2. reverse内动态分配缓冲区并返回该地址。这样做的好处是您可以根据需要调整缓冲区大小,而不必担心线程安全。缓冲区将持续存在,直到您显式释放它(或直到程序退出)。缺点是调用者现在负责在内存完成后释放该内存。避免头痛与内存管理的最好方法是首先避免内存管理,这个问题并没有真正要求它。

    3. 执行反向操作(即在输入数组中执行反向操作),并返回输入数组的地址。这意味着你正在破坏你的输入,这可能不是你想要的。

    4. 将目标数组作为第二个输入传递给函数,不要打扰返回值(或者,如果 返回某些内容,则返回目标数组的地址):

    5.     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)

参数与变量名称不匹配。

  • sPhrase被声明为字符数组。
  • 函数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指针。

希望这有帮助, 最好的祝福, 汤姆。