我用Java开发了几年,现在我想学习C,我注意到了一些差异。
在Java中,当我想从函数中返回一些内容时(例如,读取用户输入,我会写
)String s;
s = new Scanner(System.in).nextLine();
在C中我会写
char s[20];
scanf("%d", name);
不同之处在于,在Java中,函数返回一个可以直接赋值给变量的值,而在C中,函数将变量名称作为参数(相应地指向变量的指针)。
我注意到了许多C函数。当我编写自己的函数时,我应该像以前在Java中那样使用它,还是应该使用C函数来分配/返回函数中的值?
答案 0 :(得分:6)
仅使用返回值在C中执行操作的最大考虑因素是缺少例外。与Java不同,可以在常规调用/返回过程之外报告错误条件,C没有此类功能。换句话说,Scanner
可以选择在没有下一行时抛出异常; scanf
没有这样的选择。
如果要从函数返回错误状态,则限制为(1)将其返回静态变量(并发安全),(2)将其作为返回值返回,或者(3)将其返回到单独的“错误”对象中。有时选项(2)和(3)组合在一起。
结果,当状态作为返回值返回时,您经常会看到可能因scanf
结构而失败的C API,其余值通过指针修改。
if (scanf("%d%d", &i, &j) == 2) {
... // Got 2 numbers
} else {
fprintf(stderr, "Wrong input! Expected two numbers.");
}
未失败的API(例如isalpha(ch)
或tolower(ch)
)会直接返回其值。
答案 1 :(得分:3)
在Java中有一种称为garbage collector的东西,在C中你负责内存管理。
Java '把它交给我。
String s = MyFunction(); // Give it to me.
在Java中,当你调用一个从某个地方检索数据的函数时,它只是透明地分配它并返回它,垃圾收集器将在以后释放它。
C '把它放在那里。
在C中你必须管理内存。通常的做法是首先放置一些位置来放置数据,然后尝试检索它。
此外,您的代码应该更像这样:
char s[80]; // Static memory allocation, we hope it is enough.
scanf("%79s", s); // Now we retrieve data. (Put it there).
或类似的东西:
char* s = malloc(80 * sizeof(char)); // Dynamic memory allocation, we hope it is enough.
scanf("%79s", s); // Now we retrieve data. (Put it there).
// Do stuff with data
free(s); // Be responsible.
所以,是,你应该使用分配/返回值的C风格(这有意义,毕竟你做C)。
还有一点,如果你像Java一样编写它将会有什么:
char* MyTest() {
char* a = "pipo";
return a; // Big fail, 'a' is local and will not exist anymore after return.
}
char* MyOtherTest() {
char a[]= "pipo";
char* b = malloc(80 * sizeof(char));
strncpy(b, a, sizeof(a));
return b; // My bet this one will never be freed.
}
最后一个例子打破了一个众所周知的良好实践规则,调用者应负责分配/释放内存。
我看到的唯一例外是值类型,如简单类型或结构。
// Nice and simple.
int MyAdd(int a, int b) { return a+b; }
// Why ? just Why ?
void MyAdd(int a, int b, int* r) { *r = a+b; }
答案 2 :(得分:3)
@DasBlinkenLight有一个很好的观点:有时您需要函数的返回值来传回一个表示发生错误的整数以及发生了什么类型的错误。
正如@chmike指出的那样,另一个原因是,如果你想在这种情况下返回一个字符串,你必须使用malloc()
(C相当于Java的new
)。但是C没有垃圾收集,因此必须明确释放它。因此,将必须释放的指针传递给其他外部函数通常是“不礼仪”。 谁能说程序员甚至希望在堆上分配内存?使用堆是很昂贵的,并且程序员使用C的全部原因通常是因为有问题的程序需要< EM>快。
如果调用你的函数的程序员想要使用堆,他们会在scanf
的情况下通过自己调用malloc()
,然后给scanf
指向新的{I}。内存位置,然后您可以填写您的功能。这样scanf
的调用者就可以跟踪分配的所有内存,以及没有内存的内存。
当API 做返回已分配的指针时,通常是因为它无法避免,并且它们明确地记录了这一点,并且通常提供一个正确释放内存的函数,就像{{{ 3}} regfree
或POSIX regex的globfree
。这样,如果库确实分配了内存,那么库也会释放它。
答案 3 :(得分:2)
好吧,你应该知道C和Java之间的主要区别,
Java
为Object Oriented programming
,而C
则不是。{/ p>
Java
中的
几乎所有内容都是Object
(primitives
除外)。
你调用Scanner class
的方式是一种非常糟糕的方法,
你应该这样做:
Scanner scanner= new Scanner(System.in);
然后调用它来获取字符串:
String s = scanner.nextLine();
原因在于,每次拨打new Scanner(System.in)
时,您都会将扫描仪分配到同一Object
,因此它会从System.in
含义的开头重新开始,您会一直保持不变一遍又一遍地输入。
然而在C
中,它不是这样的。因为它不是你打电话的对象。
所以简而言之,只记得在java
中,一切都是reference
(cource的原始值除外),而在C中则不是这样的
编辑: 在将参数发送到方法java方面可能很棘手,因为它取决于您是发送原始数据还是对象。
因此通常最好让方法返回一些东西,而不是发送需要设置为参数的数据
例如,看一下场景:
1. Example 1
public void addOne(int i){
i++;
}
public static void main(String... args){
int i=0;
addOne(i);
System.out.println(i);
}
output= 0;
2. Example 2
public int addOne(int i){
return i++;
}
//output = 1
public static void main(String... args){
int i=0;
i = addOne(i);
System.out.println(i);
}
答案 4 :(得分:2)
Java是面向对象的,并返回一个对象,该对象是指向对象的指针。
在C中,您也可以返回指针,但不能返回数组。您显示的C代码不正确,因为声明为char的指针的s未初始化。 Scanf期望指向预分配缓冲区的指针作为第二个参数。它会将输入字符串写入缓冲区。我想你的意思是scanf("%d", s);
。
您编写的C代码将无法工作,因为s未指向用于存储字符的已分配缓冲区。您可以将s定义为char s[1024];
,在这种情况下,s等效于指向1024个字符的缓冲区的指针。
您还可以定义一个char *s = malloc(1024*sizeof(char));
,它将为s动态分配缓冲区。然后,当您不再需要缓冲区时,需要显式调用free(s)来释放缓冲区。这是因为C没有Java中的垃圾收集器。
答案 5 :(得分:-2)
要在C中读取char数组,首先必须将实际数组初始化,并限制它的长度。像这样的事情:
char str[20];
scanf("%s", &str);
请注意%s格式字符串告诉scanf捕获字符串,%d表示整数。