10.11 函数形参问题
分析如下程序的输出:
void fun(int m, int n)
{
printf("m = %d, n = %d\n", m, n);
}
int main(void)
{
int k = 3;
fun(k += 3, ++k);
printf("k = %d", k);
return 0;
}
主要考察是调用函数的时候参数的入栈顺序以及变量的运算,参数入栈顺序一般是从右到左,所以先执行++k,将4传给n;再执行k += 3将7的值传给m。注意这里是k做运算之后才传入的函数,所以k += 3, ++k并不是在调用函数的栈中执行的,所以k的值是保存下来的,所以最终输出为:
m = 7, n = 4
k = 7
这种方式和编译器中得函数调用关系相关即先后入栈顺序。不过不同编译器得处理不同。也是因为 C 标准中对这种方式说明为未定义,所以各个编译器厂商都有自己得理解,所以最后产生得结果完全不同。因为这样,所以遇见这种函数,我们首先要考虑我们得编译器会如何处理这样得函数,其次看函数得调用方式,不同得调用方式,可能产生不同得结果。最后是看编译器优化。
10.12 二级指针偏移
分析下面程序:
#include <stdio.h>
void fun(int b[][3])
{
++b;
b[1][1] = 120;
}
int main(void)
{
int a[][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
fun(a);
printf("a[2][1] = %d\n", a[2][1]);
return 0;
}
首先要了解数组传入函数后的变化**:我们知道数组传入函数的时候,会转换为指针,但是对于二维数组,只转换一次**,即int b[][3]
传入函数的时候是以
int (*b)[3]
的形式,即一个指向数组的指针,数组中有三个元素。所以传入a后,相当于b = a[0]
。
++b
代表指针b移动一个步长的位置,这里b指向一个三个元素的int型数组,所以++b
之后b = a[1][]
。
指向b[1][1] = 120
, 其中b[1][1]
相当于*(*(b+1)+1)
,相当于指针b偏移了一个步长之后又偏移了一个元素的位置,用a表示a[1+1][0+1]
即a[2][1] = 120
,所以printf输出的就是120。
10.13 #ifdef与#if define 的区别
用一段程序说明:
#include <stdio.h>
#define A
#define B
void test(int a,int b)
{
}
int main(void)
{
#if 0
#ifdef A&&B
test(printf("a\n"),printf("b\n"));
#endif
#endif
#if defined(A)&&defined(B)
test(printf("a\n"),printf("b\n"));
#endif
return 0;
}
如果是#if defined(A)&&defined(B)
的形式,输出:
b
a
如果是#ifdef A&&B
的形式,会提示:
因为#ifdef虽然我们最常见也用得最多,可是#if defined(XXX)却可以有 &&,||,!,逻辑与,逻辑或,逻辑非等操作,而#ifdef却不支持这样的特性。
10.14指针实现交换数据时的溢出情况
程序如下:
void swap(int *a, int *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
请问上述程序可否实现两个int数据的交换?看起来没毛病,可是容易忽略一个问题,那就是int是有符号数据的溢出!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nwq6O0su-1602905143136)(…/…/…/%E8%BD%AF%E4%BB%B6%E7%AE%A1%E5%AE%B6/Typora/%E5%9B%BE%E7%89%87%E5%BA%93/image-20201017103737242.png)]
**只有当a、b同符号的时候才可能发送溢出!!!**因为这里有加减操作,所以当两个同符号的数据相加的时候就有可能溢出。
比如:
交换-5, -7。 以4bit为例。
-5 = 1011 (补码) -7 = 1001 (补码)
(-5)+ (-7)= 10100=0100=4 (溢出后为4)
4-(-7)= 4 +7=0100 + 0111 = 1011 = -5的补码
4-(-5)= 4 + 5 = 0100 + 0101 = 1001 = -7的补码
注意,函数是在第一步的时候可能溢出,但是后面的操作是基于溢出的数据的,而且后面又可以把溢出的部分减回来,所以这里虽然可能会发送溢出,但是仍然可以得到正确的数据。
10.15 三目运算的数据转换
程序如下:
#include <stdio.h>
void test()
{
printf("int: %d\n", sizeof(int));
printf("double: %d\n", sizeof(double));
printf("test1: %d\n", sizeof(100 < 1 ? 0.1 : 1));
printf("test2: %d\n", sizeof(100 > 1 ? 0.1 : 1));
}
int main(void)
{
test();
return 0;
}
输出结果为:
int: 4
double: 8
test1: 8
test2: 8
如果按照我们分析,(100 < 1 ? 0.1 : 1)
输出的会是1,也就是int型数据,但是实际上被转换为double类型了。
实际上三目运算最后输出的是一个变量,在一个表达式中,数据格式要统一,所以在得出运算之前就将所有变量转换为同一种数据格式了。