国内很多的公司呢,面试的时候为了检测你对C的了解程度,就会出这种类型的题。并且出的这些题呢有这些特点:
1、运算符优先级问题。
2、类型之间的转换及无符号和有符号运算问题。
3、大端和小端及内存分配问题(比如一个变量它在内存里是怎么表现的、结构体对齐、malloc等)。
4、宏问题。
5、其它(大家都知道天朝人民的创意是无限的)。
这段代码就具有1和3的特点,3是我加的,我先说说1吧:
"[]"看到这个东西可能大家马上就联想到了数组,没错定义一个数组没它是不行的,并且它还是一个运算符(下标运算符),数组名通过它加一个整数的参数(偏移量)就可以访问到该数组的某一个元素。如果a是一个数组,那么a[1]就是该数组的第一个元素。并且a[1]和*(a+1)是一样的,因为数组名a其实就是该数组第一个元素的指针嘛。记得谭浩强教授的书里管它叫“变址运算符”,其实也就是为了解释它和指针之间的关系。
介绍就到这里,不懂的同学还得好好看看书。
那么这个大家应该明白了吧,如果转换成指针的话应该是这样的:
首先1+"abc"结果就是字符b的地址。前面还有一个'*',这个运算符大家都懂的,它出现在指针的左边代表“间接引用这个地址里的值”,所以我喜欢叫它“间接引用(dereference)运算符”,有些叫兽管它叫“解引运算符”,其实是错的,错误的原因在于它把dereference翻译成了“解引用”。其实它自己都不知道这个单词的准确意思,只好借助于一些翻译工具等,返回的结果是可想而知的。 说了那么多,1["abc"]其实就是间接引用到了字符'b',所以它的结果就是字符'b'。
好了,那么这个呢:
这里又出现了一个'&'(取址运算符),这里就和第1点,运算符优先级的问题有关了。baidu一下你就知道'&'的优先级是比"[]"的优先级低的,其实不查你也应该知道,因为"&6"这个代码是错误的,因为不能对一个“字面整数常量”进行取址操作。所以应该先是执行的"6["Hello,What is that?\n%s"]"然后才是对这个运算的结果进行取址。整理一下就是:
结果就是字符'W'的地址,那么它就是子字符串"What is that?\n%s",前面的"Hello,"被跳过了。
看了运行结果的朋友现在应该猜到了,sizeof(X)=4,sizeof(Y)=6, sizeof(Z)=8。
当然这段代码只能在x86,也就是你定义一个int它的大小是4个字节上的环境上运行。如果不是那么就达不到预期的效果了,看了下面的解释之后你就明白了。
那么这就是第4点,结构体对齐问题有关了,比如你定义一个结构体:
如果int是4个字节,那么short就应该是2个字节,很多书上说结构体的大小就是所有字段的总和,所以sizeof(struct X);应该返回8。你运行一下,应该返回的是8。那么书上说的就是正确的吗?其实不然,如果你改变一下位置:
现在sizeof(struct X);就会返回12,它在内存里面应该是这样的:
b|**XX|
a|****|
c|**XX|
*代表实际占用的空间,X代表实际未被使用的空间(空洞),之所以会这样是因为编译器为了提高访问某一个字段的效率。
而刚才那个未改变顺序之前的结构体的内存结构应该是这样的:
a|****|
.|****|
^ ^
b c
b的后面紧跟着两个字节的c。
由于对齐的原因,结构体的大小实际上跟占用空间最大的那个字段有关,大小往往是它的整数倍。有趣的是,在定义结构的时候可以简单的按从大到小的顺序来安排每个字段便可以节省更多的空间。当然一般情况下没必要,如果这样安排会影响结构的可读性的话,那就更没必要了。
还有结构体对齐不一定所有编译器都会这样做,说不定有些聪明的编译器有更好的方法。
至于第2点和第4点以及第3点的其它问题,还是让Z版来给大家介绍吧!
[ 本帖最后由 lz1091914999 于 2012-12-2 00:30 编辑 ]
1、运算符优先级问题。
2、类型之间的转换及无符号和有符号运算问题。
3、大端和小端及内存分配问题(比如一个变量它在内存里是怎么表现的、结构体对齐、malloc等)。
4、宏问题。
5、其它(大家都知道天朝人民的创意是无限的)。
这段代码就具有1和3的特点,3是我加的,我先说说1吧:
"[]"看到这个东西可能大家马上就联想到了数组,没错定义一个数组没它是不行的,并且它还是一个运算符(下标运算符),数组名通过它加一个整数的参数(偏移量)就可以访问到该数组的某一个元素。如果a是一个数组,那么a[1]就是该数组的第一个元素。并且a[1]和*(a+1)是一样的,因为数组名a其实就是该数组第一个元素的指针嘛。记得谭浩强教授的书里管它叫“变址运算符”,其实也就是为了解释它和指针之间的关系。
介绍就到这里,不懂的同学还得好好看看书。
1["abc"]
那么这个大家应该明白了吧,如果转换成指针的话应该是这样的:
*(1+"abc")
首先1+"abc"结果就是字符b的地址。前面还有一个'*',这个运算符大家都懂的,它出现在指针的左边代表“间接引用这个地址里的值”,所以我喜欢叫它“间接引用(dereference)运算符”,有些叫兽管它叫“解引运算符”,其实是错的,错误的原因在于它把dereference翻译成了“解引用”。其实它自己都不知道这个单词的准确意思,只好借助于一些翻译工具等,返回的结果是可想而知的。 说了那么多,1["abc"]其实就是间接引用到了字符'b',所以它的结果就是字符'b'。
好了,那么这个呢:
&6["Hello,What is that?\n%s"]
这里又出现了一个'&'(取址运算符),这里就和第1点,运算符优先级的问题有关了。baidu一下你就知道'&'的优先级是比"[]"的优先级低的,其实不查你也应该知道,因为"&6"这个代码是错误的,因为不能对一个“字面整数常量”进行取址操作。所以应该先是执行的"6["Hello,What is that?\n%s"]"然后才是对这个运算的结果进行取址。整理一下就是:
&*(6+"Hello,What is that?\n%s")
结果就是字符'W'的地址,那么它就是子字符串"What is that?\n%s",前面的"Hello,"被跳过了。
看了运行结果的朋友现在应该猜到了,sizeof(X)=4,sizeof(Y)=6, sizeof(Z)=8。
当然这段代码只能在x86,也就是你定义一个int它的大小是4个字节上的环境上运行。如果不是那么就达不到预期的效果了,看了下面的解释之后你就明白了。
那么这就是第4点,结构体对齐问题有关了,比如你定义一个结构体:
程序代码:
struct X { int a; short b; short c; };
如果int是4个字节,那么short就应该是2个字节,很多书上说结构体的大小就是所有字段的总和,所以sizeof(struct X);应该返回8。你运行一下,应该返回的是8。那么书上说的就是正确的吗?其实不然,如果你改变一下位置:
程序代码:
struct X { short b; int a; short c; };
现在sizeof(struct X);就会返回12,它在内存里面应该是这样的:
b|**XX|
a|****|
c|**XX|
*代表实际占用的空间,X代表实际未被使用的空间(空洞),之所以会这样是因为编译器为了提高访问某一个字段的效率。
而刚才那个未改变顺序之前的结构体的内存结构应该是这样的:
a|****|
.|****|
^ ^
b c
b的后面紧跟着两个字节的c。
由于对齐的原因,结构体的大小实际上跟占用空间最大的那个字段有关,大小往往是它的整数倍。有趣的是,在定义结构的时候可以简单的按从大到小的顺序来安排每个字段便可以节省更多的空间。当然一般情况下没必要,如果这样安排会影响结构的可读性的话,那就更没必要了。
还有结构体对齐不一定所有编译器都会这样做,说不定有些聪明的编译器有更好的方法。
至于第2点和第4点以及第3点的其它问题,还是让Z版来给大家介绍吧!
[ 本帖最后由 lz1091914999 于 2012-12-2 00:30 编辑 ]
My life is brilliant