Pointer Traps

指针(1):

最近在CSDN论坛, 经常看到有坛友问指针方面的问题. 确实, 指针是个头痛的问题, 我一个多月前(那是我出学阶段), 对这个东西是郁闷之极.
今天我打算把我对指针的一些了解拿出来给大家分享一下.

下面我们先看几个程序:
看你能否猜出答案:

#include <stdio.h>

#define str "hello, world"

int main(void)
{
char *s1 = str;
char s2[] = str;
char *s3 = str;


// Test the meaning of s1, s2, s3
printf("s1 is %#p\ns2 is %#p\ns3 is %#p\n",
s1, s2, s3);

// Test the meanint of s1, s2, s3 when across operator "&"
printf("Now, we see: \n");
printf("s1 in %#p\ns2 in %#p\ns3 in %#p\n", &s1, &s2, &s3);   


// Test the operator "&" is effective
printf("%#p\n%#p\n", s2 + 1, (&s2) + 1);   // s2 是对象, s2 长 13, 所以 &s2 相当于
// (*p)[13] 中的p 故 &s2 + 1 在数值上增加13;

return 0;
}
下面 几 个要点或许对你有帮助

1. 指针本身与其所指的值是分开的, 指针本身是个整型变量.

我想记住3条规则是非常重要的:
a. 基本结构: 你有 1 个指针, 指向数据.但是那个指针和数据是分开的. 通常错误是申明一个指针, 但是没有把他指向一个数据.
b. 指针非关联化从指针开始, 通过它的箭头, 来使用它的数据. 只有当数据和指针都有时, 这个才起作用.
c. 指针赋值.  使 1 个指针指向另 1 个指针所指向的数据, 赋值过后, 2 个指针指向同一个值(叫数据共享);

Program 1.

#include <stdio.h>

int main(void)
{
int i = 3;
int *ip1, *ip2;

// let ip1 pointer to i;
ip1 = &i;
printf("ip is %#p And ip1's adress is %#p\n", ip1, &ip1);

// let ip2 pointer to data of ip1 pointer to
ip2 = ip1;

printf("Now, ip2 pointer to %d\n", *ip2);

getchar();
return 0;

}

Output:
ip is 0X0022FF48 And ip1's adress is 0X0022FF44    // 说明ip 与 ip指向的地方是独立的.
Now, ip2 pointer to 3
这个参照上面的说明理解比较简单.



2. "*" 和 "&" 运算符, 这两个运算(这是我自己的理解, 第 1 个要点对理解这里有帮助).

a. * 可以理解为到哪里去的意思, 比如 *ip 意思是到 ip 所指向的地方去(* 特殊情况见b).

b. int *ip; 这个语句通常很让人迷惑, 我曾经迷惑过. 在这里, * 是定义的一部分, 被定义的是 ip, 而不是 *ip.
  注意, c语言申明语句中被定义的永远只有名称(name), 去掉名称(name)后可成为类型如:
  int Array[N], 被定义的只有Array. N(常量)是限制范围的, 所以在编译过程中, 不会检查出越界的错误, 但是,
  对于新手来说运行出现异常(内存 0x00000000区域不能读,等等之类的信息), 很是郁闷(我亲身经历).关于这一类问题,
  以后有时间, 我专门讲讲. 还是回到指针问题上来, 毕竟这个对于新手来说是最容易出现问题.
  
c. & 是取某数据地址的意思.  &p 意思是要数据 p 的地址. (呵呵, 如果某个杀手对你执行& 操作, 你就危险了).

program 2

#include <stdio.h>

int main(void)
{
int i = 3;
int *ip = &i;          // 根据第二点, 赋值的是ip, 而不是 *ip

printf("ip is %#p, and he pointer to %d\n", ip, *ip);

*ip = &i;   // 在这里, 被赋值的是ip所指向的地方,
// 即, *ip;
printf("Now, ip is pointer to %d, and i is %d\n", *ip, i);

getchar();
return 0;
}
Output:
ip is 0X0022FF48, and he pointer to 3
Now, ip is pointer to 2293576, and i is 2293576
对于 *ip = &i 这条语句, 执行过程为, 首先, 取数据 i 的地址(地址数值)传到 ip 指向的地方. 根据 int *ip = &i; 这条语句, ip 指向
了 i, 因此,会得到 Now, ip is pointer to 2293576, and i is 2293576.(它是等于 0X0022FF48);
注意, *ip = &i这条语句, 大多数compiler 会警告, 请不要在乎, 继续执行.



3. 对于字符串:
// (str = "hello, world")
char *s1 = str;
char s2[] = str;
  (不理解的可以跳过不看这一条说明, 这一点, 我以后有时间详细说明).
对于初学者, 很多人可能会认为这两个申明是等价的(这也是对的---在外部观察着看来), 但是, 在计算机处理这两个申明时,
处理方式是截然不同的. 第一个, 声明没有进行数据复制, 第二个进行了数据复制, 复制str到 数组s2中.

那么, s1 和 s2是表示指针变量还是对象(字符串)?

a. s1 是表示指针变量, 这是很明显的, 申明中就说明了.
  
b. s2 是大多数情况下是表示指针, 但是在两个情况下(目前我只在实践中发现两个).

c  s2 在遇到 操作符 & 和 sizeof 时表示对象.
所以 sizeof(s2)是 对象字符串 s2 的长度 13, 而不是指针s2(指针是整型变量)的长度 4, 对于 &s2 来说, 可能复杂一点.
在这里 s2 是对象, s2 长 13, 所以 &s2 与 s2(做指针时) 数值上相等(但意义不同), 因为, &s2 相当于char (*p)[13] 中的 p.
因此 对 &s2 进行自增自减(++, --) 运算会有好事发生, 可以立即跳过一个字符串(稍后用例子示之).

注意, 对于 &s2, 可能有些编译器没有这么聪明, 会报错, 或者把s2当指针看. 在此说明一下, 我用的是gcc.

Program 3.

#include <stdio.h>

#define str "hello, world"   // 包括'\0', 长度为13. 你有兴趣可以数.

int main(void)
{
char *s1 = str;
char s2[] = str;
char *s3 = str;
char *s4 = str;

// sizeof 作用时 s1表示指针(本质是整型变量). s2 表示对象字符.
printf("s1 occopy %d memory cells\n", sizeof(s1));
printf("s2 occopy %d memory cells\n", sizeof s2);

// s 当指针变量用
printf("s1 is %#p\n", s1);
printf("s2 is %#p\n", s2);
printf("s3 is %#p\n", s3);
printf("s4 is %#p\n", s4);

// & 分别作用s1, s2, s1 是整型的指针变量, 但 s2 是对象
printf("s1's adress is %#p\n", &s1);
printf("s2's adress is %#p\n", &s2);
printf("s3's adress is %#p\n", &s3);
printf("s4's adress is %#p\n", &s4);

// 对指针进行加减运算
printf("s2 + 1 = %#p\nbut &s2 + 1 = %#p", s2 + 1, &s2 + 1);

getchar();
return 0;
}

Output:
s1 occopy 4 memory cells    // s1 是整型,占4 字节
s2 occopy 13 memory cells   // s2 是字符串对象, 占13字节.

// 各 s 所指向的地址值.
s1 is 0X00403064
s2 is 0X0022FF2F
s3 is 0X00403064
s4 is 0X00403064

// 各 s的地址, 由于在 & 的作用下,除 s2 是对象外, 其余是地址, 所以, s2 与 &s2 数值相等. 但意义不同, 请看下下面.
s1's adress is 0X0022FF3C // F2F - F28 != F28 - F24 == 4(一个指针的长度), 因为内存为了便于管理,
s2's adress is 0X0022FF2F // 采取对其方式, 你可以接着在后添加 s5, s6,... 会发现, 以后的都是相差4.
s3's adress is 0X0022FF28      // 不过你不要太猛, 使劲添加塞满内存. 估计你到死都没完成,
s4's adress is 0X0022FF24      // 前提是你不使用循环和数组.

// &s2 才是真正意义上的整个字符串的地址, s2只是字符串的中首字符的地址,  因此对&s2 + 1 后, 会跳过整个字符串.
s2 + 1 = 0X0022FF30
but &s2 + 1 = 0X0022FF3C  // 这里, &s2 + 1 后跳到 s1 所在的位置去了.



最后, 我想说, 你们在开始看见了, 我也是个新手, 所以, 有很多地方, 可能讲的不详细, 或不严谨, 所以请大家不要见怪.
还请各位给予批评与指点.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值