C语言指针代码中三类打印语句的运行疑问咨询
先明确前提:代码里用calloc(n, sizeof(double))分配了10个double类型的内存空间,calloc会把分配的内存初始化为0.0,这是后续分析的基础。下面逐个看三个测试场景:
场景1:仅执行 printf("%f\n",*a++); /*Print 1*/
对应的测试代码片段:
for (i=0;i<10;i++){ printf("%f\n",*a++); /*Print 1*/ // printf("%f\n",a++); /*Print 2*/ // printf("%f\n",a[i]); /*Print 3*/ }
指针行为:
*a++的操作逻辑是先取值,后移动指针。后置++的优先级高于解引用*,所以每次循环会先读取当前a指向的元素值,然后把a向后移动一个double的长度(通常是8字节),指向下一个元素:
- 第1次循环:
a指向数组第0个元素,读取a[0]的值后,a指向a[1] - 第2次循环:读取
a[1]的值后,a指向a[2] - ...
- 第10次循环:读取
a[9]的值后,a指向数组末尾的下一个位置(此时a越界,但循环已结束,不会再访问)
输出结果:
因为每个元素都是0.0,所以会连续输出10行:
0.000000 0.000000 ... 0.000000
场景2:仅执行 printf("%f\n",a++); /*Print 2*/
对应的测试代码片段:
for (i=0;i<10;i++){ // printf("%f\n",*a++); /*Print 1*/ printf("%f\n",a++); /*Print 2*/ // printf("%f\n",a[i]); /*Print 3*/ }
指针行为:
这里a++会直接让指针a向后移动一个double的长度,每次循环移动一次,和场景1的指针移动逻辑一致:循环结束后a指向数组末尾的下一个位置。
输出结果:
这里有个致命问题:格式符不匹配导致的未定义行为!%f是用来打印double类型数值的,但a++是一个指针(本质是地址,属于整数类型),把指针地址用%f解析成浮点数,结果完全不可预测——可能是一个极大的乱数,也可能导致程序崩溃,不同编译器、平台的输出都会不一样。
举个例子,假设a的初始地址是0x7f8900000000,这个地址值被当作double解析后,会是一个非常大的浮点数,和我们预期的0完全无关。
场景3:仅执行 printf("%f\n",a[i]); /*Print 3*/
对应的测试代码片段:
for (i=0;i<10;i++){ // printf("%f\n",*a++); /*Print 1*/ // printf("%f\n",a++); /*Print 2*/ printf("%f\n",a[i]); /*Print 3*/ }
指针行为:
这里a没有任何自增操作,始终保持指向数组的起始位置(a[0]的地址)。a[i]等价于*(a + i),也就是从a的起始位置偏移i个double长度的位置,读取对应元素的值。
输出结果:
因为数组所有元素都是0.0,循环i从0到9,会依次输出a[0]到a[9]的值,也就是10行0.000000。循环结束后,a仍然指向数组的起始地址。
内容的提问来源于stack exchange,提问作者Thomas D.

