变量是一块内存区域, 对变量的常用操作包括:
1. 声明, 也就是分配内存, 同时以标识名称来命名
2.赋值, 用给定的数据, 替换该内存中的数据
3.在右值表达式中引用, 也就是提取该内存中的数据, 代入表达式中
4.*p 这个表达式, 返回值就是p指定的那块内存区域中的数据(也许未命名, 也许其他人写的代码中已命名, 但作为被调用函数来说, 不知道变量名, 但收到了存取的地址和数据类型)
5.*p 作为左值表达式时, 就是赋值, 也就是 "用给定的数据, 替换该内存中的数据"
6.*p 在右值表达式中引用, 也就是"提取该内存中的数据, 代入表达式中"
//2018-11-4补充:
7.关于字串常量:
"abc"是字串常量, 它的使用有两种情况:1.初始化一个char数组 2.在表达式中出现
1).char a[]="abc", b[4]="abc";//这种情况下, "abc"就是{'a', 'b', 'c', '\0'}的简化写法;
2).在表达式中时, "abc"是一个const char * const的指针, 指向只读字符对象首字节地址的只读指针对象
基于第2项的原因, char *p="abc"; p会指向的字串常量"abc"的首字节地址(注意与7.1不同, p是字符指针, 不是数组)
但是p[0]='b';这种语句, 在运行时会导致程序崩溃. 因为, 这相当于"abc"[0]='b'; 也就是想把字串常量"abc"修改为字串常量"bbc", 基于内存写保护的原因, 程序就崩了, 所以 const char *p="abc";才是合理的声明
8.关于数组变量名:
char a[4]="abc";
a是数组{'a', 'b', 'c', '\0'}的名字, 它的使用有两种情况:1.代表整个数组 2.在右值表达式中, 代表指向0号数组成员的指针
1). sizeof(a); //a代表整个数组
a="abc"; //这个会编译失败, 编译器给出的原因中明确写着, a的类型是char[4](代表整个数组), 而"abc"这个表达式的类型是 char * (正如第7条所述, "abc"在表达式中, 实际是const char * const, 从赋值兼容角度看, 编译器的提示也没问题)
2). 当a在表达式中出现时, 就是指向数组中首成员的指针, 也就是首成员的首字节地址
因此 char *p; p=a; 这个是再自然不过了, 这时p也指向a[4]数组中的a[0]这个成员的首字节地址了
在函数调用时, 实参向形参赋值, 也是这种用法
3). 数组名a在表达式中是一个const指针, 因为它必需永远指向a[0];
所以a++;这种语句一定是编译失败, 因为a作为指针时的类型是 const char *
(与第7条"abc"字串常量的指针类型对比, 少了最后的const)
char *p; p=a; p++;这种肯定没问题, 因为p不是const变量了
(以下代码在win10+啊哈c中测试)
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a[]="abc";
char *b=a;
//a="xyz"; //这个会提示a的类型为char[4]
printf("%c, %c, %c\n", "abc"[0], *("abc"+1), b[2]); //输出a, b, c
printf("字串常量abc地址:%p, 数组a地址:%p\n", "abc", b); //输出不同的地址
const char *p;
p="abc";
printf("%p, %p\n", p, "abc"); //输出相同的地址, 且与前一行的第一个地址相同
system("pause");
return 0;
}