我要说的是一个小问题,感觉写在博客里面比较好。以后看到了可以回忆警示一下。
问题是这样的:在Java中数组之间能不能通过数组名直接赋值,为什么?
在C中数组之间能不能通过数组名直接赋值,为什么?
我们先来分析C语言中的情况:
char arr[3]={'a','b','c'}; char arrB[3]=arr; //错的。 int intArr[3]={1,2,3}; int intArr2[3]=intArr; //错的。
原因很简单,在C语言中数组名相当于一个引用(C++中的引用),由于数组名不是一个变量,所以是不能作为赋值操作符的左值的。举个极端的例子,就像1++;和1=2;肯定是非法的一样(至于深层次的原因,可能是因为C语言为了安全性所做的规定)。
但是注意了在C语言中,以下却是可以的:
char *p="hello"; char *pB=p; printf("%s\n",pB); //输出结果: hello
原因也很简单,“hello”在常量区,p指针保存一个地址指向了"hello"。将p的值赋值给pB也仅仅就是将这个字符串地址的值赋值给了pB。pB是一个指针变量可以作为赋值操作符的左值,所以他接受到了“hello”的地址。
所以,此时的内存布局是:p和pB两个指针所存储的值是一样的,都是“hello”在常量区的地址。也就是常说的p和pB指向了同一块内存。
下来看一下Java环境下的分析:
public class Son{ public static void main(String args[]){ int arr[]={1,2,3}; int arrB[]=arr; arrB[0]=5; System.out.println(Arrays.toString(arr)); } } //运行结果: [5, 2, 3]
可见在Java环境中,采用数组名实现数组之间的赋值是可行的。原因也很简单,在Java中都是值传递,Java中的引用相当于C中的指针。所以arr和arrB是指向了同一块堆。是不是和上面那个C的例子很相像。
最后说一下,在C中结构体是可以通过结构体名相互赋值的:
typedef struct Node{ int value; }node; int main() { node node1={3}; node node2=node1; printf("%d\n",node2.value); node2.value=8; printf("%d\n",node1.value); system("pause"); return 0; } //输出结果: 3 3
我们发现结果竟然不是3和8,所以结构体通过结构体名进行赋值就是简单的值传递的。node1和node2在堆内存中开辟了两个空间,各自是独立的。仅仅是将node1的成员的值赋值给了node2相应的成员。
最后看一个我经常容易忽视的问题:
char *p="hello"; char *pB=p pB[1]='a'; printf("%s\n",pB);
猜猜上面会输出什么?
答案:发生中断,报错。
原因分析:“hello”是在常量区呀,p只是个指针,并没有给p分配用于存储“hello”的空间,所以p和pB同时指向了常量区的"hello"。常量区是不能修改的。所以报错。
解决办法:
char *p=(char*)malloc(sizeof("hello")); memcpy(p,"hello",sizeof("hello")); char *pB=p; pB[2]='a'; printf("%s\n",p); printf("%s\n",pB);
此时,p拥有了自己的存储"hello"的内存空间(在堆区),但是pB没有。pB仅仅就是指向了p的那块内存。所以可以说pB就是个穷指针。