- #include <stdio.h>
- int main(void)
- {
- char *p = "123"; //等价于char *p; p = "123";
- char a[] = "123"; //等价于char a[] = {'1', '2', '3', '\0' };
- printf("address a = %p, p = %p, &p = %p\n", a, p, &p);
- a[2] = '4';
- // p[2] = '5';
- printf("a[2] = %c, p[2] = %c\n", a[2], p[2]); //a[2]等价于*(a+2),p[2]等价于*(p+2)
- printf("\"abcd\"[1] = %c, \"abcd\" = %p, &\"abcd\"[2] = %p\n",
- "abcd"[1], "abcd", &"abcd"[2]);
- return 0;
- }
1、两者的差别
代码中第5行的意思是声明一个字符指针变量p,然后给它赋值为字符串常量“123”的首地址(如示例中的0x8048590)。在C语言中,字符串都是以字符数组的形式存储的,而这里的字符串常量就是一个无名字符数组,由于没有数组名,所以一般通过指针变量获得它的首地址来间接地进行数据访问,如示例中,先通过指针变量p获得无名字符数组的首地址,然后通过第13行的p[2]来访问这个无名字符数组中的第3个元素(即字符’3’)。
注意:指针变量的存储空间(在32位机中为4个字节)只够存储一个地址值(在32位机中是一个4字节的无符号整数),这里不能错误地认为是把字符串常量“123”赋值给p或者*p。
字符串常量一般存储在可执行文件的只读数据段(section .rodata),不能对其中的元素进行修改,所以,把第11行的语句加入到程序中,执行时会产生段错误,尽管用编译器GCC可成功编译。
第6行的意思是声明一个字符数组并给它初始化为字符串“123”(字符串以空字符为结尾)。
注意:数组只有在初始化阶段才能同时为其中的多个元素赋值。
因此,5、6两行中的字符串“123”的含义是完全不同的:第5行中的“123”代表着它(无名字符数组)自身的首地址,而第6行的“123”仅仅是字符数组a的初始值
通过下面的图示更能直观地理解两者的差异(小端模式):
2、比较酷的用法
除了可以通过指针变量间接地访问无名字符数组的元素以外,也可以通过数组下标或指针的形式直接访问其中的元素,如”abcd”[1]或者*(“abcd” +1),它们获得的值都是字符’b’。
因为字符串常量代表它自身的首地址(如0x80485fa),而访问数组元素都是通过类似于“ *( 数组名(即首地址)+ sizeof(元素数据类型) x n(第n+1个元素) ) ”这样的形式进行访问的,所以”abcd”[1](等价于 *(“abcd” + 1) )的意思是从无名字符数组首地址加1的地址(即0x80485fb)中获得第2个元素的值(即字符’b’)。
例子结果输出:
- address a = 0xbfdd0f78, p = 0x8048590, &p = 0xbfdd0f74
- a[2] = 4, p[2] = 3
- "abcd"[1] = b, "abcd" = 0x80485fa, &"abcd"[2] = 0x80485fc
尽管指针变量的值是一个32位的无符号整数,但在printf函数中,还是特别使用了另外的格式符%p来输出其中的值,说明指针变量是有它特有的数据类型的。虽然使用%x等格式符能正确地输出指针变量的值,但一般会警告数据类型不匹配。