来自Java背景我正在学习C,但我发现那些模糊的编译器错误消息越来越令人沮丧。这是我的代码:
/*
* PURPOSE
* Do case-insensetive string comparison.
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int compareString(char cString1[], char cString2[]);
char strToLower(char cString[]);
int main() {
// Declarations
char cString1[50], cString2[50];
int isEqual;
// Input
puts("Enter string 1: ");
gets(cString1);
puts("Enter string 2: ");
gets(cString2);
// Call
isEqual = compareString(cString1, cString2);
if (isEqual == 0)
printf("Equal!\n");
else
printf("Not equal!\n");
return 0;
}
// WATCH OUT
// This method *will* modify its input arrays.
int compareString(char cString1[], char cString2[]) {
// To lowercase
cString1 = strToLower(cString1);
cString2 = strToLower(cString2);
// Do regular strcmp
return strcmp(cString1, cString2);
}
// WATCH OUT
// This method *will* modify its input arrays.
char strToLower(char cString[]) {
// Declarations
int iTeller;
for (iTeller = 0; cString[iTeller] != '\0'; iTeller++)
cString[iTeller] = (char)tolower(cString[iTeller]);
return cString;
}
这会产生两个警告。
有人可以解释这些警告吗?
答案 0 :(得分:37)
C字符串与Java字符串不同。它们本质上是字符数组。
您收到错误是因为strToLower返回了一个char。 char是C中的整数形式。您将它分配给char [],它是一个指针。因此“将整数转换为指针”。
你的strToLower完成了所有的更改,没有理由返回任何内容,特别是不是char。你应该“返回”void或char *。
在调用strToLower时,也不需要赋值,你实际上只是传递cString1的内存地址。
根据我的经验,对于任何来自Java / C#背景的人来说,C语言中的字符串是最难学习的。人们可以与内存分配相处(因为即使在Java中,您也经常分配数组)。如果你的最终目标是C ++而不是C,你可能更愿意关注C字符串,确保你理解基础知识,并且只使用STL的C ++字符串。
答案 1 :(得分:5)
strToLower的返回类型应为char*
而不是char
(或者它应该什么都不返回,因为它没有重新分配字符串)
答案 2 :(得分:2)
正如其他人已经注意到的那样,在一种情况下,您试图从声明返回{{1}的函数返回cString
(此上下文中的char *
值 - 指针) (这是一个整数)。在另一种情况下,您可以反过来:您正在为char
指针分配char
返回值。这就是触发警告的原因。您当然需要将返回值声明为char *
,而不是char *
。
请注意,从语言的角度来看,这些赋值实际上是约束违规(即它们是“错误”),因为在C中混合使用指针和整数是非法的(除此之外)从积分常数零)。你的编译器在这方面过于宽容,并将这些违规行为仅仅作为“警告”。
我还想注意的是,在几个答案中,您可能会注意到从函数返回char
这一相对奇怪的建议,因为您正在修改字符串。虽然它肯定会起作用(因为你确实在原地修改字符串),但从函数返回相同的值并没有什么错。实际上,在C语言中它是一个相当标准的实践(适用于void
等标准函数),因为如果您选择使用函数调用,它可以实现“链接”功能调用,以及成本如果你不使用“链接”,几乎没有。
也就是说,strcpy
实施中的任务看起来对我来说是多余的(即使它们不会破坏任何东西)。我要么摆脱它们
compareString
或使用“链接”并执行
int compareString(char cString1[], char cString2[]) {
// To lowercase
strToLower(cString1);
strToLower(cString2);
// Do regular strcmp
return strcmp(cString1, cString2);
}
(这是您的int compareString(char cString1[], char cString2[]) {
return strcmp(strToLower(cString1), strToLower(cString2));
}
返回会很方便的时候)。请记住,这种“链式”函数调用有时很难通过逐步调试器进行调试。
作为一个额外的,未实现的注释,我会说以这种破坏性方式实现字符串比较功能(修改输入字符串)可能不是最好的主意。在我看来,非破坏性功能将具有更大的价值。除了将输入字符串显式转换为小写字母外,通常更好的做法是实现自定义char-by-char不区分大小写的字符串比较函数,并使用它而不是调用标准char *
。
答案 3 :(得分:1)
1)不要使用gets
!您正在引入缓冲区溢出漏洞。请改用fgets(..., stdin)
。
2)在strToLower
中,您要返回char
而不是char
- 数组。要么将char*
作为Autopulated建议返回,要么只返回void
,因为您仍在修改输入。结果,只需写下
strToLower(cString1);
strToLower(cString2);
strcasecmp
(Linux&amp; Mac)或stricmp
(Windows)。答案 4 :(得分:0)
您不需要这两个分配:
cString1 = strToLower(cString1);
cString2 = strToLower(cString2);
您正在修改字符串。
警告是因为你返回一个char,并指定一个char [](相当于char *)
答案 5 :(得分:0)
您正在返回char,而不是char *,它是指向数组第一个字符的指针。
如果要返回新的字符数组而不是进行就地修改,可以要求已经分配的指针(char *)作为参数或未初始化的指针。在最后一种情况下,您必须为新字符串分配适当数量的字符,并记住在CALL参数中由值ALWAYS传递,因此在函数内部分配的数组的情况下,必须使用char **作为参数。当然,调用者必须在以后释放该指针。
答案 6 :(得分:0)
strToLower应该返回char *而不是char。这样的事情就可以了。
char *strToLower(char *cString)
答案 7 :(得分:-1)
char cString1[]
这是一个数组,即指向同一数据类型的一系列元素的第一个元素的指针。请注意,您没有通过值传递数组,而是按指针传递。
char strToLower(...)
然而,这会返回一个char。所以你的作业
cString1 = strToLower(cString1);
在赋值运算符的每一侧都有不同的类型..你实际上是为一个数组分配一个'char'(整数),它解析为一个简单的指针。由于C ++的隐式转换规则可行,但结果是垃圾,并且对数组的进一步访问会导致未定义的行为。
解决方案是让strToLower
返回char*
。