(数组和字符串)Java与C的区别

8

我了解C语言,现在正在学习Java,对于它在数组和字符串上的处理方式感到困惑。它与C语言中的数组和字符串完全不同。请帮助我理解C语言和Java语言(在处理字符串和数组时)之间的实际区别。


1
你能具体一点吗?“告诉我Java中字符串和数组的工作原理”太宽泛了。 - Alexander Sagen
1
http://www.comp.lancs.ac.uk/~ss/java2c/diffs.html - Emil
7个回答

27

在C语言中

数组

C语言中的数组只是一种访问连续内存空间的语法糖,或者说是指针符号的变体。为了避免分配大块的连续内存,并避免自己操作可变大小的数据时需要重新分配内存,你可以使用常见的计算机科学数据结构概念的实现(例如链表,它使用指针来指示系列中下一个元素的内存地址)。

你可以用数组符号替代指针算术,在C语言中互换使用。

以下代码将使用不同的访问方法打印出一个数组的5个元素:

#include <stdio.h>

int main(int ac, char **av) {
  char arr[2] = {'a', 'b'};

  printf("0:%c 0:%c 1:%c 1:%c\n", arr[0], *arr, arr[1], *(arr + 1));
  return (0);
}

以下内容适用于整型变量。请注意对应整型变量大小的微小修改:
#include <stdio.h>

int main(int ac, char **av) {
  int arr[2] = {42, -42};

  printf("0:%d 0:%d 1:%d 1:%d\n", arr[0], *arr, arr[1], *(arr + 4));
  return (0);
}

(要获取给定数据类型的大小,请使用 sizeof。)

字符串

在这里,我假设您想了解传统的 C 字符串实现,而不是第三方库提供的实现。

C 中的字符串基本上只是字符数组。这样做的主要原因很明显:由于您需要经常操作字符串并将它们打印到流中,使用连续的内存空间是有意义且易于实现的。 但是,由于您需要记住连续内存空间的大小,以避免无意间访问禁止访问的内容,因此我们依赖于“NULL 结尾字符串”的概念,这意味着一个 N 个字符的字符串实际上是一个由 N + 1 个字符组成的数组,并以 '\0' 字符结尾,这个字符被用作查找字符串结尾时的默认字符。

一个简单的声明如下:

char *test = "my test";

这相当于:
char test[8] = { 'm', 'y', ' ', 't', 'e', 's', 't', '\0' };

(注意末尾的'\0')

然而,你必须意识到,在这种情况下,字符串"my test"是静态的,那就是你直接指向的内存空间。这意味着当你尝试动态修改它时,你会遇到问题。

例如,以下代码将会出现错误(在之前的声明后面添加):

test[4] = 'H'; /* expect a violent complaint here */

为了拥有一个可以实际修改的字符串,你可以简单地声明一个字符串:

#include <stdio.h>
#include <stdlib.h>

int main(int ac, char **av) {
  char *test = strdup("my test");

  printf("%s\n", test);
  return (0);
}

strdup()是C标准库的一个函数,它为您的字符串分配内存并将字符注入其中。或者,您可以使用malloc()手动分配内存并复制字符,或使用像strcpy()这样的函数。

因此,此特定声明是可变的,您可以自由修改字符串的内容(最终只是一个使用malloc()分配的动态分配的字符数组)。

如果您需要更改此字符串的长度(添加/删除其中的字符),则每次都需要注意已分配的内存。例如,如果您没有先重新分配一些附加内存,则调用strcat()将失败。但是,某些函数将为您处理此问题。

C字符串默认情况下不支持Unicode。您需要自行实现管理代码点,或考虑使用第三方库。


在Java中

数组

Java中的数组与其C语言版本非常相似(甚至有一个方法用于高效的数组到数组的拷贝,使用了底层本地实现:System.arraycopy())。它们表示连续的内存空间。

然而,它们将这些底层数组封装在一个对象中(该对象为您跟踪数组的大小/长度)。

Java数组的内容是可以修改的,但与其C语言的对应物类似,当试图扩展数组时,您需要分配更多的内存(除了您间接地这样做之外,通常会重新分配完整的数组,而不是像在C中那样使用realloc())。

字符串

Java中的字符串是不可变的,这意味着一旦初始化就无法更改,并且对字符串的操作实际上会创建新的字符串实例。查找StringBuilderStringBuffer以使用现有实例进行高效字符串操作,并注意其内部实现细节(特别是在有效预设缓冲区容量时,以避免频繁重新分配)。

例如,以下代码将从someString和"another string"产生第3个字符串实例:

String myNewStr = someString + "another string";

在底层实现中,Java String*类也使用字符数组,就像它们的C父类一样。
这意味着它们使用比裸机C实现更多的内存,因为您有实例的开销。
不仅如此,它们实际上使用了更多的内存,因为Java String类默认提供Unicode支持,这意味着它允许每个字符使用多个code points(与C相比并不是一件微不足道的事情)。
另一方面,请注意,除非考虑性能,否则无需担心线程、内存和实现寻找尾随'\0'字符的函数。

还有什么?

还有很多可以讲和研究的。

目前你的问题比较广泛,如果你在评论中添加子问题,我很乐意进行编辑。

此外,也许这可以帮助:


2
这个答案的伟大程度完全超出了尺度! - Prof. Falken
@Amigable Clark Kant:谢谢,很高兴你喜欢它 :) - haylem

17
在C语言中,字符串通常只是字符数组(或指向字符的指针),以NUL(\0)字符结尾。您可以像处理任何数组一样处理字符串。
而在Java中,字符串不是数组。Java字符串是java.lang.String类的实例(对象)。它们表示字符数据,但内部实现不暴露给程序员。您不能将它们视为数组,但是如果需要,您可以提取字符串数据作为字节数组或字符数组(方法getBytesgetChars)。还请注意,Java字符始终是16位,而C中的字符通常(并非总是)是8位。

6
在Java中,String是不可变的。如果你想稍微改变一个String,你只能创建一个新的String(例如使用StringBuilder,或者像C程序员那样使用String.format())。 - ivy

8

数组:

最显然的区别是Java在声明数组时不使用与C相同的语法。 在C中,数组下标是声明符的一部分,而在Java中,它是类型说明的一部分:

int[] arr;  // Java, arr is null until array object is instantiated
int arr[];  // C, incomplete declaration

请注意,在Java中,arr存在但具有null值。在C语言中,只有在出现完整声明之后,arr才存在。
int[][] 2Darr; // Java, arr is null until array object is instantiated
int 2Darr[][]; // Illegal declaration in C; size must be specified for at least
               // the outer dimension

在Java中,数组对象必须使用new操作进行实例化,同时指定数组的大小:
int[] arr = new int [10];
int[][] 2Darr = new int[10][20];

如果数组不是基本类型,那么每个单独的数组元素都必须单独实例化:

String[] strs = new String[10];
for (int i = 0; i < strs.length; i++)
  strs[i] = new String("some value");

Java中的数组表达式不像C中的数组表达式那样将其类型“退化”为指针类型(这很方便,因为Java本身没有指针类型);Java中的数组类型是“一流”的对象,这意味着它们在任何语境下都保留了它们所有的类型特征。当您将一个数组对象传递给方法时,该方法接收到的是一个数组对象,而不是指针。

Java数组知道它们的大小(由.length属性给出)。

字符串:

与C不同,Java提供了一个独立的String数据类型。请不要将Java字符串视为带有零结尾的char数组;它们是不同的。Java String对象是不可变的;您无法修改String对象的内容。您可以从现有String对象的修改内容创建一个新的String对象。还有像StringBuilder和StringBuffer这样的类,允许您直接操作字符数据并创建新的String对象。

希望这有所帮助。


1
在Java中,您可以声明int[] aint a[]。两者完全可互换。 - Grodriguez
1
非常完整。除了:Java还支持在变量名上放置方括号的旧C数组声明样式。然而,这被认为是不好的风格。 - Darron
жҲ‘дёҚдјҡдҪҝз”Ёnew String("some value")пјҢиҖҢжҳҜзӣҙжҺҘдҪҝз”Ё"some value"гҖӮ - Thomas Lötzer
Java 数组也是对象,由于在Java中每个类都是Object类的子类,所以数组也拥有Object类的所有方法。更多信息请参见http://java.sun.com/docs/books/jls/first_edition/html/10.doc.html。 - Masse
@Grodriguez 我最喜欢 int[] a[] 这种类型:> - atamanroman

2

C语言中的字符串只是字符数组。当发现NUL字符(\0)时,字符串就结束了,这只是一种约定。

所有字符串操作都依赖于C标准库中的函数,如strlen()、strcpy()等。

要获得C“字符串”的大小,必须传递指向单独函数的指针。你可以认为在C中根本没有字符串,只有char数组的约定。

另一方面,Java将字符串作为语言本身的一部分内置了进去。Java String具有方法,例如可以告诉你它的大小。Java也有像C一样的基本类型,如float和int。

但它还有“对象”,而String就是一种对象。

这与C和C++之间的区别非常相似。


1

在C语言中,字符串实际上是以'\0'结尾的字符数组。而在Java中,字符串是一个类。Java字符串可以与C++中的std::string进行比较,而不是C语言中的字符数组。

声明:在C语言中 - char str[100]; 在Java中 - String str;

在Java中,大多数情况下,您不需要担心字符串的实现,因为提供了丰富的成员函数来处理它。在C语言中,也有许多API,如strlen、strcpy、strcat等,这些API对于正常操作已经足够了。

主要的区别在于当您需要执行涉及两个字符串的某些操作时。例如,假设将一个字符串分配给另一个字符串。在Java中,这很简单。

String str1("This is Stack Overflow.");
String str2;

str2 = str1;

但在C语言中,您将不得不使用循环来分配每个字符。现在再次强调,这并不意味着Java更快,因为在内部,Java也会执行相同的操作。只是程序员不知道而已。

在Java中,一些操作可以使用自然运算符来完成,例如比较。

str1 == str2。

但在C语言中,您将不得不使用strcmp函数来完成此操作。

strcmp(str1,str2);

在使用C语言时,您必须并且必须知道如何在内部操作字符串。但在Java中则不需要。

现在,在C中,当您在堆区创建字符串时,您还必须格外小心。

char * str1 = malloc(100);

您将需要使用free(str1)释放此内存。在Java中,程序员无需了解堆内存或栈内存,因此这样的事情不会出现。


在Java中使用“==”运算符比较字符串并不能保证正确,因为当处理对象时,该运算符仅比较对象的引用。要进行比较,请使用“.equals()”或C语言中的“strcmp()”。此外,Java不支持像C++那样的运算符重载来重载“==”运算符,但是您可以覆盖方法“.equals()”以实现相同的效果。 - Aboelnour
== 运算符测试的是身份,而不是相等性。为了测试相等性,请使用 equals 方法。这不仅适用于字符串,也适用于所有对象。 - Grodriguez

0

String是JAVA中的一个对象,它与C语言中的字符数组不同。


0
如果你真的想知道区别,你需要了解C语言中的指针和Java中的引用之间的差异。当你在C语言中说:char str[10]; ==> 你在内存中分配了一个由10个块组成的序列,每个块的大小为sizeof(char),并以null结尾,因此你可以使用普通的指针操作来处理字符串。
而在Java中,当你说String str; ==> 你创建了一个继承了一些方法的java.lang.String对象,这些方法在java.lang.String类中,如compare()、equals()、contains()、charAt()等等。
在C语言中,要执行普通的字符串操作,你需要使用指针或者使用头文件中准备好的函数来处理内存块,没有多余的东西。例如:比较两个字符串 ==> strcmp(str1,str2);
而在Java中,正如我所说,所有的东西都是对象,如果你想比较两个字符串:
String str1; String str2; str1.equals(str2);
在C语言中,一个字符串必须以NULL结尾,以便知道何时停止,如果你试图读取字符串后面的块,不会发生什么不好的事情(它将编译,并且可能不会崩溃)。
Java:正如我所说,String是一个对象,因此如果您尝试访问字符串之外的元素,它将抛出IndexOutOfBoundException异常,除非您处理此异常,否则程序将崩溃。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接