LinuxSir.cn,穿越时空的Linuxsir!

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

C语言里面的指针和数组

[复制链接]
发表于 2004-12-24 21:28:14 | 显示全部楼层
Post by sybaselu
我来凑合两句:
int const *p               //p是常数指针,只能指向一个int数,所指的内容可以变,但p不能变;

说反了吧。
发表于 2004-12-25 12:43:47 | 显示全部楼层
指针只是一个普通变量,数组是一段连续分配的内存空间
发表于 2004-12-26 20:40:56 | 显示全部楼层
这个问题其实很简单,只要看看编译器产生的代码,即使不作任何说明也能分清指针和数组的区别了。
  1. #include <stdio.h>

  2. int main(int argc, char *argv[])
  3. {
  4.         int a[] = {1, 2, 3};
  5.         int *p = &a[1];

  6.         a[1] = 4;
  7.         *p = 4;
  8.         printf("%d\n%d\n%d\n%p\n%p\n", a[0], *p, a[1], a, p);
  9.         return 0;
  10. }

复制代码

用 gcc 编译产生的汇编代码是这样的:
  1.         # 一些给汇编器看的内容
  2.         .file   "x.c"
  3.         .section        .rodata
  4. .LC0:
  5.         .string "%d\n%d\n%d\n%p\n%p\n"
  6.         .text
  7. .globl main
  8.         .type   main, @function

  9. # main 函数
  10. main:
  11.         # main 函数的初始化
  12.         pushl   %ebp
  13.         movl    %esp, %ebp
  14.         subl    $72, %esp
  15.         andl    $-16, %esp
  16.         movl    $0, %eax
  17.         subl    %eax, %esp

  18.         # 数组 a 的初始化。地址为:
  19.         # a[0]    <->    -24(%ebp)
  20.         # a[1]    <->    -20(%ebp)
  21.         # a[2]    <->    -16(%ebp)
  22.         # 注意数组元素的访问方式
  23.         movl    $1, -24(%ebp)
  24.         movl    $2, -20(%ebp)
  25.         movl    $3, -16(%ebp)

  26.         # 指针 p 的初始化:共三步
  27.         # 取数组 a 的起始地址
  28.         leal    -24(%ebp), %eax
  29.         # 取 a[1] 的地址
  30.         addl    $4, %eax
  31.         # 为指针 p 赋值
  32.         movl    %eax, -28(%ebp)

  33.         # a[1] = 4
  34.         movl    $4, -20(%ebp)

  35.         # *p = 4:共两步
  36.         # 取指针 p 的地址
  37.         movl    -28(%ebp), %eax
  38.         # 移入指针 p 指向的地址
  39.         # 注意寻址方式
  40.         movl    $4, (%eax)

  41.         # 下面准备调用 printf
  42.         # 参数:从左至右压入堆栈但写入的顺序相反
  43.         # 首先压入指针 p 指向的地址
  44.         movl    -28(%ebp), %eax
  45.         movl    %eax, 20(%esp)
  46.         # 再压入数组 a 的起始地址
  47.         leal    -24(%ebp), %eax
  48.         movl    %eax, 16(%esp)
  49.         # 压入 a[1]:只一步
  50.         movl    -20(%ebp), %eax
  51.         movl    %eax, 12(%esp)
  52.         # 压入 *p:分两步
  53.         # 首先取出指针 p 指向的地址
  54.         movl    -28(%ebp), %eax
  55.         # 然后压入这个地址中的内容
  56.         # 注意寻址方式
  57.         movl    (%eax), %eax
  58.         movl    %eax, 8(%esp)
  59.         # 压入 a[0]:只一步
  60.         movl    -24(%ebp), %eax
  61.         movl    %eax, 4(%esp)
  62.         # 参数全部压入堆栈,调用 printf
  63.         movl    $.LC0, (%esp)
  64.         call    printf

  65.         # 准备从 main 返回,return 0
  66.         movl    $0, %eax
  67.         leave
  68.         ret

  69.         # 其他一些内容
  70.         .size   main, .-main
  71.         .section        .note.GNU-stack,"",@progbits
  72.         .ident  "GCC: (GNU) 3.3.4 (Debian 1:3.3.4-13)"
复制代码

我加了些注释,同时也说明了 C 中的指针和数组是如何被使用的。可以看到的是,数组元素的大小在编译时确定,并被写入到汇编代码中。这样,就可以用诸如 -24(%ebp) 和 -20(%ebp) 这样的形式访问数组元素而不必担心数据被覆盖,并且可以使用像 addl  $4, %eax 这样的汇编指令。读了这些汇编指令,就能够彻底明白数组与指针的区别了吧。
发表于 2004-12-27 02:01:38 | 显示全部楼层
herberteuler
强!
赞!
发表于 2004-12-27 16:20:29 | 显示全部楼层
Post by kj501
来一段林锐的C/C++编程指南吧,说得比较清楚:
7.3.1 修改内容
        示例7-3-1中,字符数组a的容量是6个字符,其内容为hello\0。a的
内容可以改变,如a[0]= ‘X’。指针p指向常量字符串“world”(位于静态
存储区,内容为world\0),常量字符串的内容是不可以被修改的。从语法上
看,编译器并不觉得语句p[0]= ‘X’有什么不妥,但是该语句企图修改常量
字符串的内容而导致运行错误。

  char a[] = “hello”;
  a[0] = ‘X’;
  cout << a << endl;
  char *p = “world”;     // 注意p指向常量字符串
  p[0] = ‘X’;                   // 编译器不能发现该错误
  cout << p << endl;

如下代码在不同的平台上有不同的结果:

  1. int
  2. foo(void)
  3. {
  4.         char *p="world\n";
  5.         p[0]='X';
  6.         return 0;
  7. }

复制代码

在AIX5,sun C++ 编译器上可以运行,结果是Xorld.
在linux上无法运行,发生段错误。
汇编代码如下:

  1.         .file   "t.c"
  2.         .section        .rodata
  3. .LC0:
  4.         .string "world\n"
  5.         .text
  6. .globl foo
  7.         .type   foo, @function
  8. foo:
  9.         pushl   %ebp
  10.         movl    %esp, %ebp
  11.         subl    $4, %esp
  12.         movl    $.LC0, -4(%ebp)
  13.         movl    -4(%ebp), %eax
  14.         movb    $88, (%eax)
  15.         movl    $0, %eax
  16.         leave
  17.         ret
  18.         .size   foo, .-foo
  19.         .section        .note.GNU-stack,"",@progbits
  20.         .ident  "GCC: (GNU) 3.4.1 (Mandrakelinux 10.1 3.4.1-4mdk)"
复制代码

我认为这是与操作系统有关的,对于 C来说(非C++).
发表于 2004-12-27 17:44:36 | 显示全部楼层
这个问题确实和操作系统有关。大多数的操作系统是把静态数据设置成只读的。
发表于 2004-12-27 20:31:00 | 显示全部楼层
Post by kj501
这个问题确实和操作系统有关。大多数的操作系统是把静态数据设置成只读的。

说得不对。应该是char *s="csaf";之类的字符串将被当作只读字符串处理。但有些操作系统对于只读数据保存的区域不一定能有效保护。可以会出现本来应该只读的字符串可以被改写的情况。
发表于 2004-12-27 20:54:54 | 显示全部楼层

有几个 kj501 啊?
发表于 2004-12-28 09:05:40 | 显示全部楼层
Post by kj501
这个问题确实和操作系统有关。大多数的操作系统是把静态数据设置成只读的。

没错。如下代码就可以正常运行。
  1. #include        <string.h>

  2. int
  3. main(void)
  4. {
  5.         char *p,ch[20];
  6.         strcpy(ch,"world\n");
  7.         p=ch;
  8.         p[0]='X';
  9.         printf("p=%s",p);
  10.         return 0;
  11. }
复制代码
发表于 2004-12-28 10:13:09 | 显示全部楼层
Post by herberteuler

有几个 kj501 啊?

我就是我呀。两个贴子都是我发的,刚开始时大脑不清楚,后来才发现自己说错了。但我的风格是不喜欢改贴子。所以就重新发贴进行纠正。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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