LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: zywwzy

我今天为招聘新员工出的C语言考题

[复制链接]
发表于 2004-7-30 18:06:14 | 显示全部楼层
GCC怎么处理没研究过
但要完成这个任务,从原理上讲应该不是难事吧,即
"已知若干变量的size, 在一个地址空间上以4字节为单位排列这些变量"
当然真正的处理过程要复杂的多,"优化"是编译过程中的一个难题.
要想知道细节,就得问研究编译的牛人了.
发表于 2004-7-31 09:39:17 | 显示全部楼层
呵呵,这种东西很玄妙的阿。
以前写过一个程序,开始是在x86上面运行的,后来放到alpha就会出来warning,不是编译的warning,是运行时,好象是说内存访问什么的,记不请出了。后来查出来是内存访问的问题,好像alpha不能按字节访问,必须对齐才行,没有考证过,反正把程序改了就好了。总之指针这种东西平台依赖性太强了,真是要小心啊。
发表于 2004-7-31 16:41:49 | 显示全部楼层
对齐当然是编译器控制的了。可以在命令行上用参数控制,也可以在程序中用#pragma pack(n)控制。不过要n比结构中最大的size的成员小才管用,否则还是按照最大size成员的大小对齐
btw,面试出这种问题,除非公司就是专门做系统级开发的,否则很无聊。而且要出也要把条件明确了。本身就是编译器和平台相关的,不说清楚没法回答啊。这个还算好了,刚看水木上有人被问
[PHP]
#include <iostream>
using namespace std ;

class Test
{
public:
        Test( int i, char ch ) : a(i), _ch(ch) {}
private:
        int a ;
        char _ch ;
};

int main()
{
        Test ft(376,'A') ;
        void* out = &ft ;
        char result = *((char*)out+1) ;
        cout << (int)result << endl ;
        return 0 ;
}

[/PHP]
result是多少,这个连字节序都要考虑了,真是无比同情这个可怜的人啊
发表于 2004-8-1 12:19:29 | 显示全部楼层
呵,那么多人对这个感兴趣。我有个建议,面试时可以搞些脑筋急转弯,看看谁先残。
发表于 2004-8-2 07:06:07 | 显示全部楼层
最初由 zywwzy 发表
佩服。
出这道题的本意就是要应试的人了解指针和缓冲区的问题。
因为作为公司的笔试题,严谨性要求不是特别高。但需要每个做题目的人都能看得懂这段问题代码。
其实这段代码可能你写过,我写过,结果可能不是你预期的,也不是我预期的。大家在不经意间留下的这些隐患需要我们有雪亮的眼睛去观察,去发现。
今天来应试的几个“高级程序员”都毫不犹豫地24,32,48,让我颇感失望。
^_^,以后这道题目要增加限制,前提是不打开任何优化选项的条件下或者直接改成主观题,让应试者根据条件阐述原因。



说句心里话,这种考试似乎进入了误区。编程人员的责任是编写优良的代码,
而不是去当hacker。

个人感觉只要能回答出结果不一定是24 32 48的就应该给满分。

(另外,不同的编译器变量在栈里面的顺序也不一定,而且编译器并不对
变量顺序做承诺。所以不排除结果是 24 32 48的可能。)
发表于 2004-8-2 08:22:12 | 显示全部楼层
同意
程序员首先应该避免编出结果"未定义"的代码
而不是关心这样的代码会出现什么结果
发表于 2004-10-18 14:23:55 | 显示全部楼层
感慨,好多强人,大家又都这么认真热烈的讨论,
我也要拼命加油了!!
干巴代!!
发表于 2004-10-19 16:43:03 | 显示全部楼层
我还有些问题要同大家探讨。

如果把最初的程序改成下面这样:
  1. #include <stdio.h>
  2. static int var1;
  3. static char var2;
  4. static int var3;
  5. int main(void)
  6. {
  7.         char str_val1[]="24\n";
  8.         char str_val2[]="32\n";
  9.         char str_val3[]="48\n";
  10.         sscanf(str_val1,"%d",&var1);
  11.         sscanf(str_val2,"%d",&var2);
  12.         sscanf(str_val3,"%d",&var3);
  13.         printf("var1=%d,var2=%d,var3=%d\n",var1,var2,var3);
  14.         return 0;
  15. }
复制代码
编译执行的结果就是 (至少我这里是这样)
  1. var1=24,var2=32,var3=48
复制代码
Linux 内核中用户空间的格式如下:
  1. start_code = 0x0        ------> +--------------------+
  2.                                 |        代码         |
  3. end_code, start_data    ------> +--------------------+
  4.                                 |        数据         |  由装载的
  5. end_data, start_brk     ------> +--------------------+
  6.                                 |        BSS         |  文件定义
  7. end_brk                 ------> +--------------------+
  8.                                           .
  9.                                           .
  10.                                           .
  11. start_code = 0x40000000 ------> +--------------------+
  12.                                 |        代码         |
  13. end_code, start_data    ------> +--------------------+
  14.                                 |        数据         |  共享 C 库
  15. end_data, start_bss     ------> +--------------------+
  16.                                 |        BSS         |   (重复)
  17. end_bss                 ------> +--------------------+
  18.                                           .
  19.                                           .
  20.                                           .
  21. start_stack             ------> +--------------------+
  22.                                 | 指向环境和参数的指针  |
  23. end_stack, start_arg    ------> +--------------------+
  24.                                 |        参数         |
  25. end_arg, start_env      ------> +--------------------+
  26.                                 |        环境         |
  27. end_env = 0xC0000000    ------> +--------------------+
复制代码
下面这个程序可以说明上面修改后的程序的虚拟地址空间:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <ctype.h>
  7. static int val1;
  8. static char val2;
  9. static int val3;
  10. void dumpaddr(FILE *fp, char *desc, void *addr);
  11. void mkprnt(char *buf);
  12. int main(int argc, char *argv[], char *envp[])
  13. {
  14.         FILE *p;
  15.         int i;
  16.         if ((p = popen("sort -n -k3 | "
  17.                 "gawk '{ printf "%-32s   %s\\n", $1, $2 }'", "w"))
  18.                 == NULL) {
  19.                 fprintf(stderr, "%s: cannot open pipe in dump(): %s\n",
  20.                         argv[0], strerror(errno));
  21.                 exit(-2);
  22.         }
  23.         for (i = 0; argv[i]; i++) {
  24.                 char buf[32];
  25.                 strcpy(buf, "arg:");
  26.                 strncpy(buf+4, argv[i], sizeof(buf)-6);
  27.                 buf[sizeof(buf) - 1] = '\0';
  28.                 mkprnt(buf);
  29.                 dumpaddr(p, buf, argv[i]);
  30.         }
  31.         for (i = 0; envp[i]; i++) {
  32.                 char buf[32];
  33.                 buf[0] = '&';
  34.                 strncpy(buf+1, envp[i], sizeof(buf)-2);
  35.                 buf[sizeof(buf) - 1] = '\0';
  36.                 mkprnt(buf);
  37.                 dumpaddr(p, buf, envp[i]);
  38.         }
  39.         dumpaddr(p, "main", main);
  40.         dumpaddr(p, "stderr", stderr);
  41.         dumpaddr(p, "stdout", stdout);
  42.         char str_val1[]="24\n";
  43.         char str_val2[]="32\n";
  44.         char str_val3[]="48\n";
  45.         int mval1;
  46.         char mval2;
  47.         int mval3;
  48.         dumpaddr(p, "str_val1", str_val1);
  49.         dumpaddr(p, "str_val2", str_val2);
  50.         dumpaddr(p, "str_val3", str_val3);
  51.         dumpaddr(p, "val1", &val1);
  52.         dumpaddr(p, "val2", &val2);
  53.         dumpaddr(p, "val3", &val3);
  54.         dumpaddr(p, "i", &i);
  55.         dumpaddr(p, "p", &p);
  56.         dumpaddr(p, "mval1", &mval1);
  57.         dumpaddr(p, "mval2", &mval2);
  58.         dumpaddr(p, "mval3", &mval3);
  59.         pclose(p);
  60.         return 0;
  61. }
  62. /* dumpaddr: write messege desc and addr into stream p */
  63. void dumpaddr(FILE *fp, char *desc, void *addr)
  64. {
  65.         fprintf(fp, "%-32s   0x%08X\t%-32lu\n",
  66.                 desc, (long) addr, (unsigned long) addr);
  67. }
  68. /* mkprnt: make buf printable */
  69. void mkprnt(char *buf)
  70. {
  71.         while (*buf)
  72.                 if (!isprint(*buf++))
  73.                         *buf = '@';
  74. }
复制代码
在我这里运行 ./y abcdef (y 是我输出的可执行文件) 的结果是:
  1. main                               0x080485C4
  2. [color=red]val1                               0x08049C50
  3. val2                               0x08049C54
  4. val3                               0x08049C58[/color]
  5. stdout                             0x40150F80
  6. stderr                             0x401510E0
  7. [color=blue]mval3                              0xBFFFF8D8
  8. mval2                              0xBFFFF8DF
  9. mval1                              0xBFFFF8E0[/color]
  10. str_val3                           0xBFFFF8E4
  11. str_val2                           0xBFFFF8E8
  12. str_val1                           0xBFFFF8EC
  13. i                                  0xBFFFF918
  14. p                                  0xBFFFF91C
  15. arg:./y                            0xBFFFFA87
  16. arg:abcdef                         0xBFFFFA8B
  17. &KDE_MULTIHEAD=false               0xBFFFFA92
  18. &TERM=xterm                        0xBFFFFAA6
  19. &SHELL=/bin/bash                   0xBFFFFAB1
  20. &XDM_MANAGED=/var/run/xdmctl/xd    0xBFFFFAC1
  21. &GTK2_RC_FILES=/etc/gtk-2.0/gtk    0xBFFFFB08
  22. &GTK_RC_FILES=/etc/gtk/gtkrc:/h    0xBFFFFB68
  23. &GS_LIB=/home/herbert/.fonts       0xBFFFFBBF
  24. &WINDOWID=48234501                 0xBFFFFBDB
  25. &KDE_FULL_SESSION=true             0xBFFFFBED
  26. &USER=herbert                      0xBFFFFC03
  27. &LS_COLORS=no=00:fi=00:di=01;34    0xBFFFFC10
  28. &LD_LIBRARY_PATH=/usr/lib/wine     0xBFFFFE45
  29. &XCURSOR_SIZE=                     0xBFFFFE63
  30. &SESSION_MANAGER=local/natsu:/t    0xBFFFFE71
  31. &XPSERVERLIST=:64                  0xBFFFFEA1
  32. &KONSOLE_DCOP=DCOPRef(konsole-3    0xBFFFFEB3
  33. &DESKTOP_SESSION=default           0xBFFFFEDE
  34. &PATH=/usr/local/bin:/usr/bin:/    0xBFFFFEF6
  35. &KONSOLE_DCOP_SESSION=DCOPRef(k    0xBFFFFF32
  36. &PWD=/home/herbert                 0xBFFFFF67
  37. &XMODIFIERS=@im=fcitx              0xBFFFFF79
  38. &LANG=zh_CN                        0xBFFFFF8E
  39. &SHLVL=2                           0xBFFFFF99
  40. &HOME=/home/herbert                0xBFFFFFA1
  41. &XCURSOR_THEME=default             0xBFFFFFB4
  42. &LOGNAME=herbert                   0xBFFFFFCA
  43. &DISPLAY=:0.0                      0xBFFFFFDA
  44. &COLORTERM=                        0xBFFFFFE7
  45. &_=./y                             0xBFFFFFF2
复制代码
可以看到大致上同前面大家讨论的相同。注意执行结果的红色和蓝色部分。此外,我发现 mval3 的“地址范围”与 mval2 的“地址范围”并不相连,因此堆栈上有“空洞”。不过仍然有几个问题:
请看执行结果的红色部分。为什么甚至对于 char 这样的类型,仍然要为它分配 4 个字节的空间?是否因为 doubleelec 所说的“对于大于8位的数,如果不按32位对齐,读取时就可能访问两次内存”?
发表于 2004-10-20 09:15:44 | 显示全部楼层
这个程序:

  1. #include <stdio.h>

  2. static int var1;
  3. static char var2;
  4. static int var3;

  5. int main(void)
  6. {
  7.         char str_val1[]="24\n";
  8.         char str_val2[]="32\n";
  9.         char str_val3[]="48\n";

  10.         sscanf(str_val1,"%d",&var1);
  11.         sscanf(str_val2,"%d",&var2);
  12.         sscanf(str_val3,"%d",&var3);
  13.         printf("var1=%d,var2=%d,var3=%d\n",var1,var2,var3);
  14.         return 0;
  15. }
复制代码

是看不到预期效果的。你定义的变量是静态变量,静态变量在运行时是放在.data段的,不是放在栈上的。我看了程序编译后的可执行文件的段头表(执行objdump -h),结果如下:

  1. [kj501@s2023 c]$ objdump -h a.out

  2. a.out:     file format elf32-i386

  3. Sections:
  4. Idx Name          Size      VMA       LMA       File off  Algn
  5.   0 .interp       00000013  08048114  08048114  00000114  2**0
  6.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  7.   1 .note.ABI-tag 00000020  08048128  08048128  00000128  2**2
  8.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  9.   2 .hash         0000002c  08048148  08048148  00000148  2**2
  10.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  11.   3 .dynsym       00000060  08048174  08048174  00000174  2**2
  12.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  13.   4 .dynstr       00000053  080481d4  080481d4  000001d4  2**0
  14.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  15.   5 .gnu.version  0000000c  08048228  08048228  00000228  2**1
  16.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  17.   6 .gnu.version_r 00000020  08048234  08048234  00000234  2**2
  18.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  19.   7 .rel.dyn      00000008  08048254  08048254  00000254  2**2
  20.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  21.   8 .rel.plt      00000018  0804825c  0804825c  0000025c  2**2
  22.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  23.   9 .init         00000017  08048274  08048274  00000274  2**2
  24.                   CONTENTS, ALLOC, LOAD, READONLY, CODE
  25. 10 .plt          00000040  0804828c  0804828c  0000028c  2**2
  26.                   CONTENTS, ALLOC, LOAD, READONLY, CODE
  27. 11 .text         00000234  080482d0  080482d0  000002d0  2**4
  28.                   CONTENTS, ALLOC, LOAD, READONLY, CODE
  29. 12 .fini         0000001a  08048504  08048504  00000504  2**2
  30.                   CONTENTS, ALLOC, LOAD, READONLY, CODE
  31. 13 .rodata       00000030  08048520  08048520  00000520  2**2
  32.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  33. 14 .eh_frame     00000004  08048550  08048550  00000550  2**2
  34.                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  35. 15 .data         0000000c  08049554  08049554  00000554  2**2
  36.                   CONTENTS, ALLOC, LOAD, DATA
  37. 16 .dynamic      000000c8  08049560  08049560  00000560  2**2
  38.                   CONTENTS, ALLOC, LOAD, DATA
  39. 17 .ctors        00000008  08049628  08049628  00000628  2**2
  40.                   CONTENTS, ALLOC, LOAD, DATA
  41. 18 .dtors        00000008  08049630  08049630  00000630  2**2
  42.                   CONTENTS, ALLOC, LOAD, DATA
  43. 19 .jcr          00000004  08049638  08049638  00000638  2**2
  44.                   CONTENTS, ALLOC, LOAD, DATA
  45. 20 .got          0000001c  0804963c  0804963c  0000063c  2**2
  46.                   CONTENTS, ALLOC, LOAD, DATA
  47. 21 .bss          00000010  08049658  08049658  00000658  2**2
  48.                   ALLOC
  49. 22 .comment      00000132  00000000  00000000  00000658  2**0
  50.                   CONTENTS, READONLY
  51. 23 .debug_aranges 00000078  00000000  00000000  00000790  2**3
  52.                   CONTENTS, READONLY, DEBUGGING
  53. 24 .debug_pubnames 00000025  00000000  00000000  00000808  2**0
  54.                   CONTENTS, READONLY, DEBUGGING
  55. 25 .debug_info   00000a0e  00000000  00000000  0000082d  2**0
  56.                   CONTENTS, READONLY, DEBUGGING
  57. 26 .debug_abbrev 00000138  00000000  00000000  0000123b  2**0
  58.                   CONTENTS, READONLY, DEBUGGING
  59. 27 .debug_line   00000258  00000000  00000000  00001373  2**0
  60.                   CONTENTS, READONLY, DEBUGGING
  61. 28 .debug_str    000006a3  00000000  00000000  000015cb  2**0
  62.                   CONTENTS, READONLY, DEBUGGING
  63. [kj501@s2023 c]$
复制代码

可见.data段是按照2的2次方也就是4字节对齐的。除非你填入8个字节的长整数,你才能看到数据被覆盖的效果。
发表于 2004-10-20 09:17:34 | 显示全部楼层
对于char类型,按4个字节对齐确实能减少处理器访问内存的次数。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表