过去有人曾对我说,“一个人爱上小溪,是因为没有见过大海。”而如今我终于可以说,“我已见过银河,但我仍只爱你一颗星。”
C语言程序中声明字符串有两种方式
1 | char *s = "string"; |
这两种方式看起来都是声明了一个内容为string的字符串,其实从内存的角度来看是有区别的。
当你运行
1 | *s = 'a'; |
时,第一个语句的程序就死掉了,第二个语句的程序却可以正确运行。
1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式类似于链表。
3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区
解释一下:
我们在程序中所声明的全局变量和静态变量(static修饰)的就是存放在全局区。
动态分配的变量则存放在堆区。
字符串常量储存在文字常量区。
而一些普通的变量比如函数参数,指针之类的也就存在于栈区。
栈(stack):
由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间
堆(heap):
需要程序员自己申请,并指明大小,在c中malloc函数
如p1=(char)malloc(10);
在C++中用new运算符
如p2=(char)malloc(10);
但是注意p1、p2本身是在栈中的
char s1[]=”aaaaaaaaaaaaaaa”;
char *s2=”bbbbbbbbbbbbbbbbb”;
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
char *c1 = “abc”;实际上先是在文字常量区分配了一块内存放”abc”,然后在栈上分配一地址给c1并指向
这块地址,然后改变常量”abc”自然会崩溃
然而char c2[] = “abc”,实际上abc分配内存的地方和上者并不一样,可以从
4199056
2293624 看出,完全是两块地方,推断4199056处于常量区,而2293624处于栈区
2293628
2293624
2293620 这段输出看出三个指针分配的区域为栈区,而且是从高地址到低地址
2293620 4199056 abc 看出编译器将c3优化指向常量区的”abc”
由于[ ]的优先级高于* 所以a先和 [ ]结合,它还是一个数组,数组中的元素才是char * ,前面讲到char * 是一个变量,保存的是字符串的地址。
那么看一下这个语句
1 | char *arr[] = {"aaaaa","bbbbb","ccccc"}; |
如果你输出sizeof(arr)的话答案并不是6 * 3 = 18,而是3 * 4 = 12;
因为数组arr中的元素是char *指针,指针占四个字节。
如果我在写一个
1 | char **s = arr; |
运行会发现是正确的,因为arr也就是&arr[0]。s则是指向char *的指针。
1 | printf("%s",*s); |
由此上面三条语句的输出结果是一样的。
还有一个问题
1 | char **s; |
这个语句也会导致程序崩溃。
因为s是一个无效的指针,*s的操作是UB,所以在执行上述语句需要这么做
1 | char **s; |