变量的本质是什么?是一段内存空间,它是程序一定时间内,运行过程中,对其进行读写的地方。
变量的名称代表的是内存空间地址,除去变量迷人的外表:整型,字符型,长整型,指针类型,数组 剩下的就是内存空间。说到空间,就要注意无非大小,方圆几里?起于何处,止于何方。
那些定义变量的时候叫整型的,或是字符型的是告诉我们世界这么大,然而变量的心就这么大,只能容下这么点心事,多的承受不起。不要超过我的空间大小,我地界之外都是法外之地,到那里苦海无边,回头是岸,家门之内保你无灾无妄,是你的心安之处。
现在你明白了所谓变量,加上那些花花绿绿的类型都是都是骗人的,他就是一块内存里的给那个变量名为xxx的自留地,变量名称就是让你知道他的地址在哪,你需要他指引你到达你想要到达内存空间。
分配给你的自留地是个不毛之地,你要用之前你得清理,初始化它,让他干干净净的,要不然他就不是你认为的样子。记住没有初始化变量就使用这是个祸害,千刀万剐的行为,等到有问题的时候就是程序不死,你不能死,他死你更不能死。这是先辈们死在沙滩上血泪教训,不是危言耸听,昨天我写的C语言翻车现场去看看就知道了。
好了言归正传,如果你的自留地够大,你除了种花,种草甚至种树都可以。了解变量空间的本质,你就发现原来C语言世界这么大,有这么多神来之笔。
请看下图演示代码
1 #include <stdio.h>
2 int main(int argc,char *argv[])
3 {
4 int i = 0;
5 char c = 1;
6 int j = 4417;
7
8 i = c;
9 c = j;
10
11 printf("sizeof(i)=%ld,i=%d,sizeof(c)=%ld,c=%d,c=%c\n",sizeof(i),i,sizeof(c),c,c);
12 printf("Hex (65)=%0x, Dec (0x1141)=%d\n",65,0x1141);
13 return 0 ;
14 }
程序会输出什么呢?说说你的答案?编译不通过,报类型错误?很遗憾没有!内存溢出报segment fault! 这个也没有!!!
使用如下的编译命令,一点错误也没有
gcc -Wall -o test test.c
./test
结果输出如下
这回明白了吗,C语言才不关心你的变量叫什么,你是什么类型,他只认那个叫XXX变量的内存自留地,这地有多大。所以 i = c; 没有任何问题 ,i,c 的空间大小分别为sizeof(i)=4,而sizeof(c)=1 i 容下 c 一点压力都没有,但c = j; 呢为什么没有内存溢出呢?答案是编译器帮你自动转换了,j的十六进制值是0X1141,被截断了j的高位字节,只取了第一个字节41所以变成c的数值是65,ASCII码对应的字符是A。对于C语言来说c这个变量只有种一盘花的空间,是种不下j这个大树的,结果只能砍掉这棵树一部分再分给c变量。
从上面可以看到C语言给了开发人员很大的自由,这种自由更接近计算机底层本质上处理过程,因为对于计算机来说这个是个空间,只是你要读写多少的数据问题。只要这个空间是你的进程可访问的地址那么是不受限制的,你需要自己维护这个空间里数据的完整、一致性、干净,给自己留一片净土。所以不要执迷于变量的类型,受困于这个类型到底能不能赋值,能不能这样。自留地是你的,你想种什么花,什么草,什么树那是你的自由。
明白这个你就知道了你定义了一个缓冲char array[1024](什么缓冲?没错这叫缓冲 ,教材叫字符数组,到你那就成了字符串)这个缓冲能放结构类型,能放长整型,能放字符型,浮点型等等。假如有这个一个需求你需要跟银行进行代收业务交易。你交易的协议约定如下
首1,2字节是包长度 数值, 第3,4,5节是版本号 字符, 第6,7,8,9,10,11字节是本次会话ID 字符往下是包体,包体内容有字符又有数值,但是又如何写入数据呢,记住变量本质是空间,这个空间是可放不同类型(切确说是不同大小长度)内容的空间
请看下面完整的演示代码,就明白如何写入不同的类型了
1 #include <stdio.h>
2 #include <string.h>
3 int main(int argc,char *argv[])
4 {
5 short pkglen=512 ; /* 包长 */
6 char version[4]={"1.0"}; /* 版本 */
7 char sessionid[6]={"123456"}; /* 会话ID */
8 char buf[1024];
9 char *pBuf = buf;
10
11 /* 封装数据包 */
12 *((short *)pBuf) = pkglen;
13 pBuf = pBuf +2;
14 sprintf(pBuf,version);
15 pBuf = pBuf + strlen(version);
16 sprintf(pBuf,sessionid);
17 pBuf = pBuf +strlen(pBuf);
18
19 /* 解析包 */
20 pBuf = buf;
21 short len = *((short *)pBuf);
22 char v[4];
23 strncpy(v,pBuf + 2,3);
24 v[3]='\0';
25 char s[6];
26 strncpy(s,pBuf + 2 +3,5);
27 s[5]='\0';
28 printf("pkglen=%d,version=%s,sessionid=%s\n",len,v,s);
29 return 0 ;
30 }
结果如下
接着来说说变量的类型,类型切确的说是在C语言中对变量进行操作时每次处理的位宽,例如int 在64位,gcc编译器下属
4字节的长度,对某个变量的读写每次都是相同的位宽,也是指针前进后退的步长。类型的作用就是告诉计算机每次处理的单元大小
看一下如下代码演示
1 #include <stdio.h>
2 #include <string.h>
3 int main(int argc,char *argv[])
4 {
5 char array[11]="";
6 strncpy(array, "1234567890", 10);
7 char *p = array;
8
9 p++;
10 printf("*p = %c, addr = %ld\n",*p, p);
11
12 p = p +3;
13 printf("*p = %c, addr = %ld\n",*p, p);
14
15 int *pi =(int *) array;
16 pi++;
17 printf("*pi = %c, addr = %ld\n",*pi,pi);
18 }
输出:
第一次 p++; 运行后p现在的位置是相对于array 地址的下一个字节处,也就是p前进的位宽是sizeof(char),即array[1]的地址,所以打印出来的结果是2
而pi++;pi 类型的位宽是4个字节,那么执行pi++后相当于往后走了四步sizeof(char)的大小,但只走了一步sizeof(int),可以看到程序的输出结果最后p和pi的地址是
相等的,也就是说他们现在都是同一个地方
这就是类型的本质,它限定了每次读写的位宽,是按固定位宽单元去访问。如同之前的12 *((short *)pBuf) = pkglen;
pBuf本来是char在转换成short后写入pkglen,直接往长度为4的内存里写了512,读者想想如果是一个结构的类型呢?
总之要记住变量的本质是一段内存空间,它的名称代表内存空间的位置,而它的类型是读写的位宽。