C++和C#的指针小解

昨天和赵崇说了一下工作的事情,说起了性能问题就讨论起了数据结果和指针对性能的影响,以前一直没有想到这方面的事情,这几天专门抽时间回顾一下这方面的知识,然后一点一点的总结一下,看看数据结构和指针在咱们代码中是如何实现效率的提升的。

今天咱们先说一下指针,关于指针,在学C++的时候到时接触过指针,但是当时学的云里雾里,也没能好好的总结一下,以至于忘的差不多了,如果大家也有对指针不熟悉的地方,我们先来回顾一下C++的指针吧。

在C++中,指针是这样子定义的:指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区。让我们分别说明。

int *ptr;
char *ptr;
int **ptr;
int (*ptr)[3];
int *(*ptr)[4];

通过上边的例子,大家把指针的申明语法去了,剩下的就是指针了,所以上边的指针就是“ptr”。

然后我们看一下指针的类型:

int *ptr; //指针的类型是int *
char *ptr; //指针的类型是char *
int **ptr; //指针的类型是 int **
int (*ptr)[3]; //指针的类型是 int(*)[3]
int *(*ptr)[4]; //指针的类型是 int *(*)[4]

怎么样?找出指针的类型的方法是不是很简单?

指针所指向的类型

当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。

指针所指向的类型

int *ptr; //指针的类型是int
char *ptr; //指针的类型是char
int **ptr; //指针的类型是 int *
int (*ptr)[3]; //指针的类型是 int()[3]
int *(*ptr)[4]; //指针的类型是 int *()[4]

指针的值

这个需要看操作系统,如果咱们的系统是32位的,那么咱们的指针就是32位长度的一个值,因为计算机的内存长度是32位,同理,64位的就是64位长度的值,当然这个64和32,都是用0和1表示的,因为计算机只能知道0和1.

指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

运算符&和*

这里&是取地址运算符,*是...书上叫做“间接运算符”。&a的运算结果是一个指针,指针的类型是a的类型加个*(例如int*),指针所指向的类型是a的类型(int),指针所指向的地址嘛,那就是a的地址。*p的运算结果就五花八门了。总之*p的结果是p所指向的东西,这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。

说过来翻过去,就是一个咱们手机导航过程,a就是你家,&a是指向你家的地址,例如p=&a,那么p就是我家的地址,那么*p的*就相当于咱们手机的导航过,通过你输入的地址,来找到你家。

上边的例子,只是简单的说了一下什么叫做地址,那么如果大家想要更深层次的理解指针的话,给大家推荐一篇博客,写的非常的基础http://www.cnblogs.com/basilwang/archive/2010/09/20/1831493.html

那么为什么要有指针呢,如不你设计一个函数

struct get(){

.........

}

返回一个结构体对象的函数,你要知道,C++中,这样的返回值都是复制传递的过程,也就是说,你返回这个结构体的时候,程序会复制一个一样的结构体对象在栈里面,然后接受的变量在通过拷贝构造函数,复制一个新的变量。最后程序在析构掉这个临时的。如果结构体很小,没什么问题,如果很大呢?这样一构造,一析构,会非常浪费时间。但是指针就不一样了,管你怎么弄,反正就是4字节,和一个int一样,完全没区别。

那么接下来咱们说一下C++到C#的”进化史”吧,平时我们见的代码好像没有再像C++的代码用到了指针,后来人们就说微软是不是就没有指针,其实微软是有的,大家右击咱们的解决方案下的类库-à生成à不安全代码,大家勾选一下啊,一样可以用,只不过写类和方法的时候前边加上个一个unsafe。例如:

public partial unsafe class Demo

static unsafe void Copy(byte[] src, int srcIndex,
            byte[] dst, int dstIndex, int count)
        {
				//。。。。。
}

还有一个关键字要注意,那就是Fixed,他的作用就是一个钉子,大家看了上边的介绍会发现指针其实也是计算机中的0和1。指针也占用内存,只是他的大小是固定的。而fixed语句可用于设置指向托管变量的指针并在
statement 执行期间“钉住”该变量。如果没有 fixed语句,则指向可移动托管变量的指针的地址可变,因为垃圾回收可能不可预知地重定位变量。

C# 编译器只允许在 fixed语句中分配指向托管变量的指针,但无法修改在 fixed
语句中初始化的指针。

可以用数组或字符串的地址初始化指针:

fixed (int* p = arr) ...  // equivalent to p = &arr[0]
fixed (char* p = str) ... // equivalent to p = &str[0]

下边为大家呈现一个完整的例子,一个C#使用指针的例子

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace cursorTest
{
     class Program
    {
        // 使用unsafe标注该方法
        static unsafe void Copy(byte[] src, int srcIndex,
            byte[] dst, int dstIndex, int count)
        {
            if (src == null || srcIndex < 0 ||
                dst == null || dstIndex < 0 || count < 0)
            {
                throw new ArgumentException();
            }
            int srcLen = src.Length;
            int dstLen = dst.Length;
            if (srcLen - srcIndex < count ||
                dstLen - dstIndex < count)
            {
                throw new ArgumentException();
            }

            // 用fixed钉住指针,不让他改变
            fixed (byte* pSrc = src, pDst = dst)
            {
                byte* ps = pSrc;
                byte* pd = pDst;

                // 循环复制
                for (int n = 0; n < count / 4; n++)
                {
                    *((int*)pd) = *((int*)ps);
                    pd += 4;
                    ps += 4;
                }

                //完成赋值
                for (int n = 0; n < count % 4; n++)
                {
                    *pd = *ps;
                    pd++;
                    ps++;
                }
            }
        }

        static void Main(string[] args)
        {
            byte[] a = new byte[100];
            byte[] b = new byte[100];
            for (int i = 0; i < 100; ++i)
                a[i] = (byte)i;
            Copy(a, 0, b, 0, 100);
            Console.WriteLine("The first 10 elements are:");
            for (int i = 0; i < 10; ++i)
                Console.Write(b[i] + " ");
            Console.WriteLine("\n");
            Console.ReadLine();
        }

    }
}

但是为什么我们的代码现在都不怎么用指针呢,因为在公共语言运行库 (CLR)
中,不安全代码是指无法验证的代码。C# 中的不安全代码不一定是危险的,只是其安全性无法由 CLR进行验证的代码。因此,CLR
只对在完全受信任的程序集中的不安全代码执行操作。如果使用不安全代码,由您负责确保您的代码不会引起安全风险或指针错误,所以你如果对你的代码非常有保证的话,用也是没问题的。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2025-01-04 11:04:13

C++和C#的指针小解的相关文章

[c/c++] programming之路(17)、高级指针

一.二级指针 二级指针的作用:1.函数改变外部变量指针2.外挂改变一个指针的值 1 #include<stdio.h> 2 #include<stdlib.h> 3 4 void main(){ 5 int a = 10; 6 int b = 20; 7 int *p1 = &a; 8 int *p2 = &b; 9 int **pp = &p1; 10 printf("%d,", **pp); 11 printf("\n%x,

C++学习笔记----2.4 C++引用在本质上是什么,它和指针到底有什么区别

从概念上讲.指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变. 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量). 在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的: 指针传递参数本质上是值传递的方式,它所传递的是一个地址值.值传递过程中,被调

【转】父类子类指针相互转换问题

1.当自己的类指针指向自己类的对象时,无论调用的是虚函数还是实函数,其调用的都是自己的: 2.当指向父类对象的父类指针被强制转换成子类指针时候,子类指针调用函数时,只有非重写函数是自己的,虚函数是父类的: 3.当指向子类对象的子类指针被强制转换成父类指针的时候,也就是父类指针指向子类对象,此时,父类指针调用的虚函数都是子类的,而非虚函数都是自己的. 将上面三句话总结成一句话就是:当父类子类有同名非虚函数的时候,调用的是转换后的指针类型的函数: 当父类子类有同名虚函数的时候呢,调用的是指针转换前指

C++ Primer 学习笔记与思考_7 void和void*指针的使用方法

(一)void的含义 void的字面意思是"无类型",void差点儿仅仅有"凝视"和限制程序的作用,由于从来没有人会定义一个void变量,让我们试着来定义: void a; 这行语句编译时会出错.提示"illegal use of type 'void'".只是.即使void a的编译不会出错.它也没有不论什么实际意义. void真正发挥的作用在于: (1) 对函数返回的限定: (2) 对函数參数的限定. int f(void); equal t

当this指针成为指向之类的基类指针时,也能形成多态

this指针: 1)对象中没有函数,只有成员变量 2)对象调用函数,通过this指针告诉函数是哪个对象自己谁. 1 #include<iostream> 2 using namespace std; 3 class Shape 4 { 5 public: 6 //void cal_display(Shape* this) 7 void cal_display(){ 8 display(); 9 this->display(); 10 } 11 private: 12 virtual vo

指针x(超简单的x)

指针! 1 #include<cstdio> 2 #include<iostream> 3 4 using namespace std; 5 6 /* 7 相加或者相乘 8 */ 9 10 int main() 11 { 12 int a,b,s,t; 13 cin>>a>>b; 14 int *p; 15 p=&a; 16 int *q; 17 q=&b; 18 s=(*p)+(*q); 19 t=(*p)*(*q); 20 printf(

二重指针实现排序

1 //双指针对十个数排序 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #define N 8 6 //2017.3.5 7 int main() 8 { 9 //初始化数组 赋值N个数 用指针数组指向 10 int num[N]; 11 int *pNum[N]; 12 int **p = NULL; 13 for (int i = 0; i < N; i++) 14 { 15 num[i] = rand() % 100;/

c指针-专题

六---指针 内存和地址怎么理解呢? 机器中有一些位置,每一个位置被称为[字节]/byte,许多现代机器上,每个字节包含8个位.更大内存单位[字],通常包含2个或4个字节组成. 一个字包含4个字节,它的地址是什么? 他仍然只有一个地址,是最左边还是最右边的那个字节的位置,取决于机器. 机器事实-关于整型的起始位置: 在要求边界对齐(boundaryalignment)的机器上,整型存储的起始位置只能是某些特定的字节,通常是2或4的倍数. 变量名和地址关系? 所有高级语言的特性之一,就是通过名字而

智能指针的原理和简单实现

什么是智能指针? 智能指针实质上是一个类,定义一个类来封装资源的分配和释放.这个类的构造函数中传入一个指针,完成资源的分配和初始化.在析构函数中释放传入的该指针,完成资源的释放. 为什么要用智能指针? 智能指针就是智能,自动化的管理指针所指向的动态资源. 例如以下情况:代码中经常会忘记释放动态开辟的内存资源,导致内存泄露. // case1 void Test2() {  int* p1 = new int(2);  bool isEnd = true;  //...  if (isEnd)