来自知乎 https://www.zhihu.com/question/26117197
问题
具体来说,下面这些情况让我感到疑惑:
-
既然 char *a 只是定义了一个指针 a,而并不为其指向的字符串分配内存,那么为什么下面两行代码又能够编译通过,且执行结果看上去是正常的?
char *a;
a = “abcde”;
这两行程序的执行过程中,编译器/计算机都做了些什么?
在这两行程序的基础上,为什么赋值语句 *a = ‘A’ 又是不合法的(会导致运行时错误)?
相比于上面两行程序,以下两行也是不合法的,为什么?
char a[];
a = “abcde”; -
以下两种初始化方式是否有本质区别?
char a[] = “abcde”;
char *b = “abcde”; -
为什么下面的代码
char *a, *b;
a = “abcde”; b = “abcde”;
其运行结果中, a 和 b 会指向同一内存地址?而上一问中得到的 a 和 b 就不会指向同一内存地址。 -
对于数组声明(或初始化)的变量,例如
char a[6];
或
char a[]=“abcde”;
这句话是否正确?当 a 作为右值(rvalue)时,其类型为 char*;而当 a 作为左值(lvalue)时,其类型就是 char[6]。
我有这个疑问,是因为我试过几次,发现以下代码可以正常运行
char a[]=“abcde”;
char *b;
b = a;
而以下代码会导致类型转换错误
char *a=“abcde”;
char b[6];
b = a;
甚至,以下代码也会导致类型转换错误,且错误提示和上一段代码是一样的
char a[]=“abcde”;
char b[6];
b = a;
解答
作者:知乎用户
链接:https://www.zhihu.com/question/26117197/answer/32179051
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
为了简化讨论这里只考虑变量被声明到函数体内这一种情形。全局变量的情形下需要考虑目标文件的导出符号而有其复杂性;另一方面,局部变量似乎更有代表性。此外我们也不讨论局部静态变量。
1.char *a; //这一行中编译器做的事情是,它知道有一个变量 a 可能需要放在栈上;假如后面需要取变量 a 的地址,那么编译器就被迫使 a 占用栈空间;而假如编译器决定使 a 不占用栈空间就要给 a 在它需要保持值的期间保留一个寄存器。 在执行时计算机不为此做任何事情。a = “abcde”; //这一行中编译器做的事情是它知道有这么一个匿名数组 “abcde”,它需要把匿名数组保存到目标文件中,并且生成一个指令,把匿名数组在运行时的首地址赋值给变量 a。具体赋值给了一段内存还是给了一个寄存器这要看 a 是不是在栈上占有空间,并且有必要即刻将 a 的值更新(否则可以在晚一些更新这个值,又或者 a 的值此后再也没被使用那么这个操作就可以被彻底优化掉)。 在执行时这一句就表现为在寄存器间或者在寄存器与内存间移动数据。*a = ‘A’ 之所以会在运行时发发生错误,是因为匿名数组所在的存储空间是不可写的。
具体情况请参照下面 (2.) 处的解释。char a[];a = “abcde”;以上是不合法的。首先,声明数组时如果没有给变量初始化就必须指明数组的长度。其次,即使改成char a[6];a = “abcde”;也是不合法的,这是因为你不能将指针值赋给一个数组。或者用一个更简单的说法:类型不同,并且不存在一个隐式转换使得 char* 被转换成 char[6] 。下面为了便于讨论,用 char[] 指代 char[N] 系列类型中的任意一个有效类型。
2.char a[] = “abcde”;char b = “abcde”;这2行的区别要归结于 char[6] 类型的初始化器与 char 类型的初始化器的差别。char 数组的初始化器会将匿名数组的内容复制到对象所处的空间中,而 char* 的初始化器会将匿名数组的首地址赋值给对象。
3.char *a, *b;a = “abcde”; b = “abcde”;a 和 b 的值不一定相同,这取决于编译器的行为。但是在前述的 (2.) 中数组 a 的首地址一定和 b 保存着的地址不同,这是因为 (2.) 处 a 所处的空间是可写的也就是说数组 a 的首地址是可写的,char 数组初始化器必须把匿名数组的位于只读空间的内容复制到 a 所处的可写的空间;与此同时 (2.) 处 b 保存的是匿名数组的首地址,换句话说保存的是一个只读的地址,这个地址当然就不同于数组 a 的首地址。
4.char a[6];或char a[]=“abcde”;这句话是否正确?当 a 作为右值(rvalue)时,其类型为 char*;而当 a 作为左值(lvalue)时,其类型就是 char[6]。这句话是错误的。从 char[6] 到 char* 有一个隐式转换,这并不意味着 a 会有飘忽不定的类型。例如 char 总是能隐式转换到 int,说 char 类型的变量有时候具有类型 int 显然是错误的。char a[]=“abcde”;char b;b = a;合法。原理如前所述,char[] 可以隐式转换成 char。char a=“abcde”;char b[6];b = a;不合法。如前所述,不存在一个隐式转换使得 char 被转换成 char[]。char a[]=“abcde”;char b[6];b = a;不合法。char[] 类型的变量不能被赋值(具体来说,所有数组都不能作为左值使用)。如果把 “abcde” 改成 “abc” 之类那么也会是一个类型不匹配错误。