C、C++中指针加 1 的问题

本文通过一段C语言代码,解析了指针运算和内存存储相关知识。分析了将数组首地址转换为整型再加1后取新整数的结果,还探讨了指针加1、指针相减等操作的输出结果及原因,并给出了相应的汇编代码进行解释。
 先用贝尔的一道笔试题简要的说明一下吧:

【题】说明以下程序。
#include
<stdio.h>


int main(void)

{

    unsigned int a[3] = {0x01020304, 0x05060708, 0x090a0b0c};
    unsigned int *p = (unsigned int *)((int)a +1);
    printf("%x/n", *p);

    return 0;

}


【题目解析】

    这段程序的输出结果应该是 8010203(即0x08010203)。
    内存存储结构如(图1)所示(在 big-endian 的系统中)。在处的语句中,首先把数组 a 的首地址转化为整型,然后再加 1 ,再强制转化为 (unsigned int *) ,这个时候 p 值应该是 0x1235,所以取的是从 0x1235 到 0x1238 四个字节组成的新的整数,所以就是 0x08010203。
    给出公式的话就是 (a[0]>>8 | a[1]<<24)。



图 1 内存结构示意图



【补充说明】

    看一下这条语句输出是什么呢?
        p = a;
        printf("%d  %08x  %d  %d  %d",
               (p+1)-p, *(p+1), &a[1]-&a[0],(int)(p+1)-(int)p, (char *)(p+1)-(char *)p );

    结果是: 1  05060708  1  4  4  

    第一个输出大家可能理解为理所当然了,我们先看后面的结果。

    第二个输出值是数组 a 的 a[1] 元素的值,也就是说 (p+1) 指向的地址是 0x1238,而不是0x1235。为什么呢?因为指针加 1 的时候,实际上是加上了他所指向数据类型的宽度,这样指针就可以指向下一个元素而不是一个元素的一半了。汇编代码如下:
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,                       ;注意这里,实际加的是 4,而不是1
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx

    第三个输出按理来说应该是 0x1238-0x1234 = 4,但是为什么是 1 呢?因为返回的是这种元素的个数,而不是真正的地址差值,也就是把差值再除以元素宽度。下面是 VC 的汇编代码,参数 &a[1]-&a[0] 的压栈过程。
00401043   lea         ecx,[ebp-8]        ; 取 a[1] 的地址值
00401046   lea         edx,[ebp-0Ch]      ; 取 a[0] 的地址值
00401049   sub         ecx,edx            ; 计算地址值之差,并放入 ecx 中 (值为4)
0040104B   sar         ecx,2              ; 将 ecx 中的数值右移两位,也就是除以 4 (值为1)
0040104E   push        ecx                ; 参数压栈,供函数 printf() 使用

    第四个输出是 4,因为int类型的相减就是实际数字的相减,不需考虑其它因素,汇编代码如下:
00401063   mov         edx,dword ptr [ebp-10h]
00401066   mov         eax,dword ptr [edx+4]
00401069   push        eax                ; 注意!压栈之前没有右移操作

    第五个输出,讲到这里 我想大家应该都明白了为什么是4了,我只给出汇编代码:

0040104F   mov         eax,dword ptr [ebp-10h]
00401052   add         eax,4
00401055   sub         eax,dword ptr [ebp-10h]
00401058   push        eax

    现在在回头看一下第一个输出结果的汇编代码。在执行 p+1 的时候实际上是加了 4 (int类型的宽度),只是在减的时候将结果除了个 4,所以结果仍然是 1。
    ;(p+1)-p
0040106A   mov         ecx,dword ptr [ebp-10h]
0040106D   add         ecx,4
00401070   sub         ecx,dword ptr [ebp-10h]
00401073   sar         ecx,2
00401076   push        ecx
    

【后记】

    这些以前都有点印象,但是不是十分确定,昨天就试验了一下,找出了一下规律。其实这些 C99 上应该都有的,我同学正在帮我找,找到了将链接贴到这里。


      When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. (结果是两个数组元素的下标之差)  

### 指针声明与初始化 C语言和C++指针的声明和初始化上基本致,但C++提供了更多的安全性和灵活性。在C语言中,指针可以被声明为任何类型,并且可以被赋值为`NULL`,表示指针不指向任何有效的对象[^1]。例如: ```c int *p = NULL; // C语言中指可以初始化为NULL ``` 而在C++中,虽然也可以使用`NULL`,但从C++11开始推荐使用`nullptr`来代替`NULL`,以提高类型安全性[^1]: ```cpp int *p = nullptr; // C++11中推荐使用nullptr ``` ### 指针运算 在指针运算方面,C语言和C++都支持指针的算术运算,如`p++`、`p--`、`p + n`和`p - n`等操作,这些操作在两种语言中的行为是致的。指针可以指向数组中的元素,并且可以通过指针来遍历数组[^1]。 ```c int arr[5] = {1, 2, 3, 4, 5}; int *p = arr; for (int i = 0; i < 5; i++) { printf("%d ", *(p + i)); // C语言中使用指针遍历数组 } ``` ```cpp int arr[5] = {1, 2, 3, 4, 5}; int *p = arr; for (int i = 0; i < 5; i++) { std::cout << *(p + i) << " "; // C++中使用指针遍历数组 } ``` ### 函数指针 C语言和C++都支持函数指针的概念,允许将函数作为参数传递给其他函数,或者作为返回值从函数返回。这在实现回调机制或函数对象模式时非常有用[^1]。 ```c void func(int x) { printf("Value: %d\n", x); } void callFunc(void (*f)(int), int x) { f(x); } int main() { callFunc(func, 10); // 使用函数指针调用函数 return 0; } ``` ```cpp void func(int x) { std::cout << "Value: " << x << std::endl; } void callFunc(void (*f)(int), int x) { f(x); } int main() { callFunc(func, 10); // 使用函数指针调用函数 return 0; } ``` ### 指针与引用 C++引入了引用的概念,这是C语言所没有的特性。引用可以看作是变量的别名,它必须在声明时初始化,并且旦初始化后就不能改变引用的对象。相比之下,指针可以在运行时改变其所指向的对象[^1]。 ```cpp int a = 5; int &ref = a; // 引用必须初始化 ref = 10; // 修改a的值 int b = 20; ref = b; // 修改a的值为b的值,而不是改变ref引用的对象 ``` ### 安全性和灵活性 C++在安全性方面做了改进,比如通过使用`nullptr`来避免空指针问题,以及通过引用减少指针错误的可能性。此外,C++11引入了智能指针(如`std::unique_ptr`和`std::shared_ptr`),进步增强了内存管理的安全性。 ```cpp #include <memory> std::unique_ptr<int> p(new int(10)); // 使用智能指针管理内存 ``` ### 相关问题 1. 如何在C++中使用智能指针? 2. C语言中如何实现类似C++引用的功能? 3. C++11中引入的智能指针有哪些优势? 4. 在C++中,如何正确使用`nullptr`替代`NULL`? 5. C++中的引用和指针在性能上有何差异?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值