我们都知道数组名取地址的值和数组的首地址是同一个值,但是他们的指类是不同的,如果强转的话是会出问题的。看看下面这个程序片段:
<pre name="code" class="cpp">#include <stdio.h>
int main(void)
{
char str[128];
printf("str: %p\n", str);
printf("str + 1: %p\n", str + 1);
printf("&str: %p\n", &str);
printf("&str + 1: %p\n", &str + 1);
return 0;
}
int main(void){ int i = 0; char str[128]; memset(str, 0, sizeof(char) * 128); printf("str: %p\n", str); printf("&str: %p\n", &str); foo((char **)&str); return 0;}
从语法上讲,程序是可以编译过去的,但一运行就会出现段错误。
段错误出在foo()函数中的第三条printf语句中的 **t 表达式,其实从第二条printf语句中已经看到,*t的值为0x0, 这样再对*t做指向运算,一定是段错误。
问题出现在两点,
1.在main()函数中,str的值与&str的值虽然是相同的,但他们的所指向的内容是不同的;
str指向一个字符,也就是一个内存单元,所以(str + 1) - str 的值应该是1;
而&str指向一个数组,即(&str + 1)指向下一个数组,类似于二维数组中的行指针,所以(&str + 1) - &str 的值应该是sizeof(str),即str数组的大小。
这个可以通过下边的程序片段来验证:
#include <stdio.h>
int foo(char **t)
{
printf("t: %p\n", t);
printf("*t: %p\n", *t);
printf("**t: %p\n", **t);
return 0;
}
2.在foo()函数中,t 的值其实是:t == &str == str。但此时 t 是二级指针,对于表达式 (*t), 从计算机的角度来讲,它的值等于 (*str), 而且 t 的类型是char **, 所以(*t) 的类型就是char *,从这里可看出,(*str)原本的类型是char, 但这里由(*t)得到的类型却是一个指针,它占8字节(64位机器),也就是str数组的前8个内存单元,由于我们使用memset()全赋值为0,所以(*t)的值就是0x0,所以(**t)这个表达式就会出现但错误。
那如果我们在foo()函数中确实需要(*t)++,(**t)等这样的操作怎么办?
在main()函数中单独申请一个指针p,赋值为str, 然后再将&p作为参数传进去,即:
char *p = str;
foo(&p);