指针

变量名和变量对应存储的位置背后有一一映射的关系,而指针/索引就是开放了一部分权限给coder让他们可以自己建立这种关系。比如开辟一段连续内存 ,4*sizeof(int)来存4个整型变量,那么我至少需要知道这一段连续内存的某一个地址,才能进行访问,一般都是首地址。这种访问,用索引的方式,也就是变量和变量对应存储位置的一一映射的方式来公式化表示就是 a[0]~a[3],a就是首地址,a[0]等价于*a,a[3]等价于*(a+3)。

所以*a和a[0]一样,都是一个int类型变量,a是地址,*这个操作其实就是根据地址把内容物(变量)取出来,而变量对应的地址使用&来取,所以a=&(*a)。

那么为什么 地址不能直接用一个int来存储呢? 地址的值是可以使用int来存储没有问题,可是根据值取内容物这个特殊操作,却是我们做不到的。c++的指针就是为你封装了这个特殊操作,可以认为cpp为我们封装了一个指针类。提供了一个函数,比如叫做get_content,能够通过转译符'*'来调用这个函数,这个函数的功能就是根据地址取内容物。并且,能够通过int*来快速创建指针,实际等价于。new 指针['int'],int* 来实例化的指针,相当于给了参数'int'告诉构造器,为我开辟int类型的指针。

实例:

int *ptr; //指针所指向的类型是int  
int **ptr; //指针所指向的的类型是 int *  指向指针的指针(二级指针)

char a[5]="hello"
char *p=a;
int a=5;
int *p=&a;

函数指针

理解:

函数运行期间,每个函数都会占用一段连续的内存空间。而这段空间的起始地址(入口地址)就是函数名,我们可以将函数的入口地址赋给一个指针变量,我们就可以通过这个指针来调用函数。这样的指向函数的指针就叫做函数指针。

实例:

函数返回类型 (* 指针变量名)(参数类型1,参数类型2);

#include<stdio.h>
void PrintMin(int a, int b)
{
    if (a < b)
    {
        printf("%d", a);
    }
    else
    {
        printf("%d", b);
    }
}

int main()
{
    void(*pf)(int, int);
    pf = PrintMin;
    int x = 4, y = 5;
    pf(x, y);
    return 0;
}

那么上面的例子可能体现不出来,为什么不直接call函数名而是使用函数指针。换一个更恰当的例子,我们如果想要把函数当做参数传给另一个函数,使用的方法就是把指针指向一个函数,把这个指针传给另一个函数。这样的方法比起在函数中调用另外的指定函数(死代码),具有更好的封装性。比如stdlib的快排函数

qsort(void * _Base, size_t _NumOfElements, size_t _SizeOfElements,
int (__cdecl * _PtFuncCompare)(const void *, const void *));

的最后一个参数就是一个指向函数的指针,这个需要coder自己写一个比较准则,来完成对任意规则的数组的排序,比如我们要按照个位数从小到大排列一个数组,就需要如下的代码:

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
int PrintMin_digit(const void *a, const void *b)
{
    unsigned int *p1, *p2;
    p1 = (unsigned int *)a;
    p2 = (unsigned int *)b;
    return (*p1) % 10 - *(p2) % 10;
}
#define NUM 5
int main()
{
    unsigned int an[NUM] = { 8, 123, 11, 10, 4 };
    int (*pf)(const void *, const void *);
    pf = PrintMin_digit;
    qsort(an, NUM, sizeof(unsigned int), pf);
    for (int i = 0; i < NUM; i++)
        cout << an[i]<<endl;
    return 0;
}

位运算

1.按位与 &

常用来对某些特定位进行操作,比如对一个int的低8位bit全部置0:

N=N&0xffffff00;   //十六进制中两位等于1byte,相当于8bit,所以后两位00等于低八位bit都是0;如果n是short类型,那就ff00

2.同理或运算|

通常用来将某些bit置1

N&=0xff

3.按位异或^

不相同为1,否则为0

  • 通常用来将某些bit取反

N^=0xff

  • 异或有另一个特点,如果a^b=c则有c^b=a以及c^a=b;故可以用来做加密
  • a=a^b;b=b^a;a=a^b; 可以完成不用临时变量交换a,b值的功能

4.按位非~

5.按位左移<<

相当于左移一位是* 2^n

6.右移>>

同理,相当于除以2^n

引用

引用相当于一个变量的别名,只能引用变量,且初始化后无法再修改引用。应用和变量名等价。对其中任何一个操作,都是一样的。

定义式 类型名 & 引用名 =变量名 //引用的类型是 类型名 &

常引用 const 类型名 & 引用名 =变量名,一般用来作为形参,这样在栈中对引用的修改不影响到本来的值

常引用的特点是,不能通过引用修改原变量的内容。以此来控制变量的权限,达到更好的封装性。

比如,swap函数:

void swap(int & a,int & b)
{
    int temp;
    temp=a;a=b;b=temp;
}
int x=1,y=2;
swap(x,y);

以此,可以少些一些指针操作让代码看起来更简洁。

常量

const关键字除了可以定义一些常量,还有

const 类型* p 常量指针不可以通过常量指针修改p指向的内容,但常量指针指向可以改变;

另外,不能把常量指针赋值给非常量指针,反之可以。这是因为常量指针指向的内容倾向于不能修改,所以权限不能扩大,除非把指针类型做强制类型转换。

用处:常用来作为认为不应该修改指针指向内容,的函数参数。封装安全性有保证。

比如:

void myprintf(const char* p)
{
    printf("%s",p);
}

如果对p指向内容修改,会有编译错误。

results matching ""

    No results matching ""