GVKun编程网logo

copy_to_user包含数组(指针)的结构(typedef数组指针)

19

对于想了解copy_to_user包含数组的读者,本文将是一篇不可错过的文章,我们将详细介绍指针的结构,并且为您提供关于10、C语言——指针与一维数组(指针数组与数组指针)、c–为什么copy_to_

对于想了解copy_to_user包含数组的读者,本文将是一篇不可错过的文章,我们将详细介绍指针的结构,并且为您提供关于10、C语言 —— 指针与一维数组(指针数组与数组指针)、c – 为什么copy_to_user花费数百毫秒?、C++ 地址(指针)大小、C++(指针和高级指针)-上篇的有价值信息。

本文目录一览:

copy_to_user包含数组(指针)的结构(typedef数组指针)

copy_to_user包含数组(指针)的结构(typedef数组指针)

披露:我对C相当陌生。如果你能够解释任何答案,我将不胜感激。

我正在编写一个Linux内核模块,并且在我正在写的一个函数中,我需要将一个结构复制到用户空间,如下所示:

typedef struct { uint32_t someProperty; uint32_t numOfFruits; uint32_t *arrayOfFruits; } ObjectCapabilities;

我正在实现的API有一些文档将arrayOfFruits成员描述为“一个大小为numOfFruits的数组,其中每个元素都是一个FRUIT_TYPE常量”。 我很困惑如何做到这一点,因为arrayOfFruits是一个指针。 当我copy_to_user ObjectCapabilities结构时,它只会将指针 arrayOfFruits复制到用户空间。

用户空间如何连续访问数组的元素? 这是我的尝试:

打印字符数组垃圾

何时返回E_POINTER和何时E_INVALIDARG?

分段错误使用memset而不是for循环来初始化int **

pthread:我想要传递一个结构指针在主要由pthread调用的函数

从库中查找argc和argv

ObjectCapabilities caps; caps.someProperty = 1024; caps.numOfFruits = 3; uint32_t localArray[] = { FRUIT_TYPE_APPLE,FRUIT_TYPE_ORANGE,FRUIT_TYPE_BANANA }; caps.arrayOfFruits = localArray;

然后为副本…我可以这样做吗?

copy_to_user((void *)destination,&caps,(sizeof(caps) + (sizeof(localArray) / sizeof((localArray)[0]))));

如何打印进程在C中使用的内存的每个字节?

传递结构指针与传递结构

指针和虚拟内存

仍然不明白指向C中数组的数组的指针

SWIG:映射typedef的数组

用户需要为所有被复制的数据提供足够的空间。 理想情况下,他会告诉你他提供了多少空间,并检查一切是否合适。

复制出来的数据应该(通常)不包括任何指针,因为它们对于不同的“进程”是“本地的”(内核可以被看作是一个单独的进程,而内核/用户交互涉及进程即处理IPC,类似于通过本地甚至互联网连接的套接字发送东西)。

由于内核对进程有非常深入的了解,所以你可以在一定程度上避开这些规则,例如,你可以计算出用户的指针,然后拷贝一份原始数据,指针被适当修改。 但那样太浪费了 或者,您可以复制内核指针,而不是在用户代码中使用它,但是现在您正在“泄漏数据”,“坏人”有时可以以各种方式利用这些数据。 在安全人士说,你已经离开了一个开放的“秘密通道”。

最后,那么做到这一点的“正确”方式往往是这样的:

struct user_interface_version_of_struct { int property; int count; int data[]; /* of size "count" */ };

用户代码malloc (或者以其他方式安排有足够的空间)“用户界面版本”,并且对内核进行一些系统调用(只要涉及到“读取”, read , receive , rcvmsg , ioctl ,类型操作),并告诉内核:“这里是存放结构的内存,这里有多大”(以字节为单位,或最大count数值,或者其他:用户和内核只需要协议)。 然后,内核端代码以适当的方式验证用户的值,然而复制出来也是最方便的,或者返回一个错误。

“最方便”有时是两个单独的拷贝操作,或者一些put_user调用,例如,如果内核端有你所显示的数据结构,你可以这样做:

/* let''s say ulen is the user supplied length in bytes,and uaddr is the user-supplied address */ struct user_interface_version_of_struct *p; needed = sizeof(*p) + 3 * sizeof(int); if (needed > ulen) return -ENOMEM; /* user did not supply enough space */ p = uaddr; error = put_user(1024,&p->property); if (error == 0) error = put_user(3,&p->count); if (error == 0 && copy_to_user(&p->data,localArray,3 * sizeof(int)) error = -EFAULT;

不过,您可能会遇到一些不太适合的界面。

编辑:如果你正在添加自己的系统调用(而不是参与read或ioctl例如),你可以分开标题和数据,在亚当罗森菲尔德的答案 。

你不能复制原始指针,因为指向内核空间的指针对于用户空间是没有意义的(并且如果被解引用,将会出现段错误)。

这样做的典型方式是要求用户空间代码分配内存,并将指向该内存的指针传递给系统调用。 如果程序没有传入足够大的缓冲区,那么会失败(例如EFAULT )。 如果程序没有办法事先知道需要多少内存,那么通常你会返回传递NULL指针所需的数据量。

来自用户空间的示例用法:

// Fixed-size data typedef struct { uint32_t someProperty; uint32_t numOfFruits; } ObjectCapabilities; // First query the number of fruits we need ObjectCapabilities caps; int r = sys_get_fruit(&caps,NULL,0); if (r != 0) { /* Handle error */ } // Now allocate memory and query the fruit uint32_t *arrayOfFruits = malloc(caps.numOfFruits * sizeof(uint32_t)); r = sys_get_fruit(&caps,arrayOfFruits,caps.numOfFruits); if (r != 0) { /* Handle error */ }

下面是相应的代码在系统调用另一端的内核空间中的样子:

int sys_get_fruit(ObjectCapabilities __user *userCaps,uint32_t __user *userFruit,uint32_t numFruits) { ObjectCapabilities caps; caps.someProperty = 1024; caps.numOfFruits = 3; // copy out fixed-size data int r = copy_to_user(userCaps,sizeof(caps)); if (r != 0) return r; uint32_t localArray[] = { FRUIT_TYPE_APPLE,FRUIT_TYPE_BANANA }; // Attempt to copy variable-sized data. Check the size first. if (numFruits * sizeof(uint32_t) < sizeof(localArray)) return -EFAULT; return copy_to_user(userFruit,sizeof(localArray)); }

使用copy_to_user你可以做两个副本给用户。

//copy the struct copy_to_user((void *)destination,sizeof(caps)); //copy the array. copy_to_user((void *)destination->array,sizeof(localArray);

总结

以上是小编为你收集整理的copy_to_user包含数组(指针)的结构全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

10、C语言 —— 指针与一维数组(指针数组与数组指针)

10、C语言 —— 指针与一维数组(指针数组与数组指针)

1、使用指针修改数组元素

int array[2];
int *p;
p = array;
// 或 p = &a[0];

*p = 10;

printf("a[0]=%d\n", a[0]);    // 输出:a[0]=10


2、使用指针遍历数组

// a、平常我们是这样遍历数组的
int array[3] = {1, 2, 3};
for(int i=0; i<3; i++) {
    printf("a[%d]=%d\n", i, a[i]);
}

// b、用指针可以这样遍历数组
// 如果 p 是指向数组array的首元素array[0]
// 那么 p+1 则会根据数据类型 int 从而指向数组array的第二个元素array[1]
// 在16位编译器环境下,p+1代表增加2个字节
int *p = array;
for(int i=0; i<3; i++) {
    printf("a[%d]=%d\n", i, *(p+i));
}

// c、指针遍历2
// 此种方法相比于上一种方法不同的是:上一种方法指针p不变,而这种p每循环一次就改变一个
for(int i=0; i<3; i++) {
    printf("a[%d]=%d\n", i, *(p++));
}

// d、指针遍历3
// 数组跟指针有着密不可分的关系,array[i]也可以写成*(array+i)
for(int i=0; i<3; i++) {
    printf("a[%d]=%d\n", i, *(array+i));
}


3、指针,数组与函数参数

// 定义一个修改数组首元素的函数
void change(char c[]) {
    c[0] = 0;
}

void main() {
    char a[3];
    change(a);    // 传入数组地址
    printf("a[0]=%d\n", a[0]);    // 输入:a[0]=10
}

// 也可传入指针
void main() {
    char a[3];
    char *p = a;
    change(p);    // 传入地址
    printf("a[0]=%d\n", a[0]);    // 输入:a[0]=10
}

// 当然,change函数还可以改成这样
void change(char *c) {
    *c = 10;
    // 或c[0] = 10;
}

    


4、指针数组与数组指针

    a、指针数组:int *p[n];

    • 因为优先级:() > [] > *

    • p首先是一个数组,再由 int * 说明这是一个整型指针数组

    • 即数组里的元素都存放着变量的地址

    b、数组指针(行指针):int (*p)[n];

    • p是一个指针,指向一个整型的一维数组

int a[3][4];
int (*p)[4];    //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

// 所以数组指针也称指向一维数组的指针,亦称行指针。




c – 为什么copy_to_user花费数百毫秒?

c – 为什么copy_to_user花费数百毫秒?

我有一些运行apache trafficserver的linux服务器,发现大约1/20的请求需要几毫秒,这比预期的要长得多(应该是1-2ms).

在通过systemtap进行跟踪之后,我发现在linux内核copy_to_user()调用中花费了大量时间. (ACCEPT-> inet_csk_accept-> move_addr_to_user-> copy_to_user)

服务器负载退出低(<100qps),copy_to_user()只复制16字节的数据(struct sockaddr),但花费数百毫秒. 由于我不再使用systemtap和内核跟踪技术,我无法进一步调查原因.我检查了cpu使用情况,交换使用情况 谢谢你的建议. 硬件:
> cpu:Intel(R)Xeon(R)cpu E5-2640 v3 @ 2.60GHz
>记忆:64G
>磁盘:11 * 6T硬盘

软件:

> centos6 2.6.32-696.el6.x86_64#1 SMP Tue Mar 21 19:29:05 UTC 2017 x86_64 x86_64 x86_64 GNU / Linux
> apache trafficserver 5.3.2配置10G ram
> Nginx 1.10.2,代理用户对trafficserver的请求

// systemtap
probe kernel.function("move_addr_to_user")
{
    ts["move_addr_to_user",pid()] = gettimeofday_ms()
}

probe kernel.function("move_addr_to_user").return
{
    printf("[%d]move_addr_to_user done:%d %s %d\n",gettimeofday_ms(),pid(),execname(),gettimeofday_ms()-ts["move_addr_to_user",pid()])
}

probe kernel.function("copy_to_user")
{
    ts["copy_to_user",pid()] = gettimeofday_ms()
}

probe kernel.function("copy_to_user").return
{
    printf("[%d]copy_to_user done:%d %s %d %d\n",gettimeofday_ms()-ts["copy_to_user",pid()],gettimeofday_ms()-ts["__copy_to_user",pid()])
}

// output:
[1495630190767] 16 16 move_addr_to_user done:24145 [ACCEPT 0:8080] 0
[1495630191164]copy_to_user done:24145 [ACCEPT 0:8080] 0
[1495630191164] 16 16 move_addr_to_user done:24145 [ACCEPT 0:8080] 0
[1495630192172]copy_to_user done:24145 [ACCEPT 0:8080] 861
[1495630192172] 16 16 move_addr_to_user done:24145 [ACCEPT 0:8080] 861
[1495630192173]copy_to_user done:24145 [ACCEPT 0:8080] 0
[1495630192173] 16 16 move_addr_to_user done:24145 [ACCEPT 0:8080] 0
[1495630192173]copy_to_user done:24145 [ACCEPT 0:8080] 0
[1495630192173] 16 16 move_addr_to_user done:24145 [ACCEPT 0:8080] 0
[1495630192173]copy_to_user done:24145 [ACCEPT 0:8080] 0



free -g
             total       used       free     shared    buffers     cached
Mem:            62         55          6          0          0         32
-/+ buffers/cache:         23         39
Swap:           15          0         15

top - 20:57:39 up 24 days,19:26,2 users,load average: 7.70,9.43,9.62
Tasks: 643 total,1 running,642 sleeping,0 stopped,0 zombie
cpu(s):  0.1%us,1.0%sy,0.0%ni,97.5%id,1.1%wa,0.0%hi,0.3%si,0.0%st
Mem:  65560992k total,58525192k used,7035800k free,365084k buffers
Swap: 16777212k total,0k used,16777212k free,33957572k cached
  PID USER      PR  NI  VIRT  RES  SHR S %cpu %MEM    TIME+  COMMAND                                                                                                                                                                         
24145 traffics  20   0 21.7g  12g 6212 S 24.7 19.3 212:42.85 [ET_NET 0]                                                                                                                                                                     
22173 root      20   0  677m 325m 1180 S  3.6  0.5   0:41.10 Nginx                                                                                                                                                                           
22161 root      20   0  677m 325m 1184 S  2.6  0.5   0:47.50 Nginx                                                                                                                                                                           
22168 root      20   0  677m 326m 2076 S  2.6  0.5   0:28.31 Nginx                                                                                                                                                                           
22150 root      20   0  677m 325m 1208 S  1.6  0.5   0:42.75 Nginx                                                                                                                                                                           
22165 root      20   0  677m 325m 1200 S  1.6  0.5   0:31.77 Nginx

更新:

本月@employee感谢您的建议,在__do_page_fault上添加一些探测后,我发现时间花在__do_page_fault上 – > down_read(安培; MM-> mmap_sem来);

    [1495677639202]copy_to_user done:24145 [ACCEPT 0:8080] 1
    [1495677639202] 16 16 move_addr_to_user done:24145 [ACCEPT 0:8080] 1
    [1495677639314]copy_to_user done:24145 [ACCEPT 0:8080] 0
    [1495677639314] 16 16 move_addr_to_user done:24145 [ACCEPT 0:8080] 0
    [1495677641329]do page fault done:24145 [ACCEPT 0:8080] 622
    [1495677641329]copy_to_user done:24145 [ACCEPT 0:8080] 622
    [1495677641329] 16 16 move_addr_to_user done:24145 [ACCEPT 0:8080] 622

@Ricardo Biehl Pasquali感谢您的建议. Apache trafficserver在单独的线程中读/写hdd. trafficserver有1个线程接受连接,88个(每个硬盘8个线程)线程做阻塞读/写(缓存内容),但我不明白为什么在其他线程中阻塞读/写可能导致__do_page_fault()的高延迟.

    Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
    sda               0.00    16.00    0.00    4.00     0.00   160.00    40.00     0.05   13.50    0.00   13.50   7.75   3.10
    sdi               0.00     0.00    0.00    0.00     0.00     0.00      0.00     0.00    0.00    0.00    0.00   0.00   0.00
    sde               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
    sdd               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
    sdj               0.00     0.00    6.00    0.00  4326.00     0.00   721.00     0.06   10.50   10.50    0.00   6.83   4.10
    sdc               0.00     0.00    2.00    0.00  1472.00     0.00   736.00     0.04   18.50   18.50    0.00   9.50   1.90
    sdh               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
    sdg               0.00     0.00   14.00    0.00 10464.00     0.00   747.43     0.04    2.71    2.71    0.00   1.79   2.50

更新:

问题解决了!
根本原因是trafficserver定期调用system()来备份我们编写的古代插件中的文件.

最佳答案
好吧,合理的解释是你正在查看页面错误.一个可能的原因是服务器分叉/退出取决于负载如何变化而新的孩子必须对页面进行故障,但作为一个小故障,除非存在内存不足,否则不应该花费很长时间.

另一个是内存不足,你实际上在交换.

无论哪种方式,鉴于你已经走到这一步,我看不出有什么问题会进一步发展.您想将探测器放在__do_page_fault上并从那里继续.

C++ 地址(指针)大小

C++ 地址(指针)大小

#include <iostream>
using namespace std;
void main()
{
   double a=9.56900;
   double *p=&a;
   char b=''a'';
   char *q=&b;
   cout<<sizeof(&a)<<endl;
   cout<<sizeof(p)<<endl;
   cout<<sizeof(&b)<<endl;
   cout<<sizeof(q)<<endl;
}

输出结果为:4,4,4,4;

为什么涉及地址都是4,我用的是vc6.0!难道里面定义指针的大小了?

C++(指针和高级指针)-上篇

C++(指针和高级指针)-上篇

【在指针中存储地址】

int *pAge=nullptr;   //将PAge声明为int指针,即用于存储int变量的地址

如果将指针初始化为0或者NUll,以后必须将变量的地址赋给它,如下例代码:

int howOld=50;
int *pAge=nullptr;
pAge=&howOld;

【间接运算符(解除引用运算符)】

int howOld=50;
int *pAge=nullptr;
pAge=&howOld;
int yourAge;
yourAge=*pAge;   //*表示存储在.....处的值。

【通过指针访问指针中存储的内容,指针存储的内容(地址)指向的值,和指针本身的地址】

int howOld=50;
int *pAge=nullptr;
pAge=&howOld;
std::cout<<"the address of howOld"<<pAge<<std::endl;   //howOld的地址
std::cout<<"the value of howOld"<<*pAge<<std::endl;    //howOld的值
std::cout<<"the address of pAge"<<&pAge<<std::endl;    //pAge本身的地址

【为何使用指针】

指针最常用于完成如下三项任务。

1.管理堆中的数据

2.访问类的成员和成员函数。

3.按引用将变量传递给函数

【栈和堆】

程序员通常需要处理下述的5个内存区域

1.全局名称空间

2.堆

3.寄存器

4.代码空间

5.栈

局部变量和函数参数存储在栈中,

代码存储在代码空间中

全局变量在全局名称空间中。

寄存器用于内部管理,如跟踪栈顶和指令指针。

余下的几乎所有内存都分配给了堆。堆也被称为自由存储区

(每当函数返回时,都会清理栈,此时,所有局部变量都不在作用域内,从而从栈中删除,。

只有到程序结束后才会清理堆,因此使用完预留的内存后,你需要负责将其释放。让不再需要的信息留在堆中称为内存泄漏)

堆的优点在于,在显示释放前,你预留的内存始终可用。如果在函数中预留堆中的内存,在函数返回后,该内存仍可用。

 

 

【使用关键字new】

在C++中,要分配堆中的内存,可使用关键字new,并在它后面指定要为之分配内存对象的类型,让编译器知道需要多少个内存,例如,new unsigned short int从堆中分配2个字节内存,而new long 分配4字节内存。

关键字new 返回一个内存地址,必须将其赋给指针。要在堆中创建一个unsigned short ,可编写如下代码:

unsigned short int *pPointer;
pPointer=new unsigned short int;

当然也可在声明指针的时候初始化它:

unsigned short int *pPointer=new unsigned short int;

无论采取哪种方式,pPointer 都将指向堆中的一个unsigned short int.可像使用指向变量的指针那样使用它,将一个值存储到该内存区域:

*pPointer=72;

如果不能从堆中分配内存(因为内存资源有限),将引发异常。

【使用关键字delete】

使用完分配的内存区域后,必须对指针调用delete,将内存归还给堆。别忘了,指针本身为局部变量,这不同于它指向的内存;当申明指针的函数返回时,指针将不再在作用域中,因此被丢弃。然而,使用new关键字分配的内存并不会自动释放,这些内存将不可用,这被称为内存泄露,因此程序结束前,内存不会归还给堆,就像内存从计算机中漏掉了。

要将内存归还给堆,可使用关键字delete,例如

delete pPointer

 删除指针时,实际释放了其地址存储在指针中的内存,这被称为将指针指向的内存归还给堆。指针还是指针,可重新给它复制

对指针调用delete时,将释放它指向的内存。如果再次对该指针调用delete,这将导致程序崩溃!删除指针时,应将其设置为NULL,而对空指针调用delete是安全的。

Animal *pDog=new Animal;
delete pDog;     //free the memory
pDog=nullptr;     //sets pointer to null
delete pDog;       //harmless

 以下程序Heap演示了如何给一个变量分配堆内存,然后使用并删除它,并再次使用指针

//Heap.cpp
#include<iostream>

int main()
{
   int localVariable=5;
   int *pLocal=&localVariable;
   int *pHeap=new int;
   if(pHeap==nullptr)
   {
       std::cout<<"Error!No memory for pHeap!";
       return 1;
   }
   *pHeap=7;
   std::cout<<"localVariable: "<<localVariable<<std::endl;
   std::cout<<"*pLocal: "<<pLocal<<std::endl;
   std::cout<<"pHeap: "<<pHeap<<std::endl;
   delete pHeap;
   if(pHeap==nullptr)
   {
       std::cout<<"Error!No memory for pHeap!";
       return 1;
   }
   *pHeap=9;
   std::cout<<"pHeap: "<<pHeap<<std::endl;
   delete pHeap;
   return 0;
}

程序输出如下:

localVariable:5

*pLocal:5

*pHeap:7

*pHeap:9

虽然最后调用delete pHeap是多余的(程序结束时,将自动归还内存),但是显式的释放内存是一个不错的主意,修改或扩展该程序时,这种操作将带来明显的好处

 

【避免内存泄漏】

第一种内存泄漏是由于:在函数返回值之前没有delete pPointer,当申明指针的函数返回时,指针将不再在作用域中,因此被丢弃。然而,使用new关键字分配的内存并不会自动释放,这些内存将不可用,这被称为内存泄露

另一种内存泄漏是因为:没有释放指针指向的内存就给他重新复制。例如下面的代码:

unsigned short int *pPointer=new unsigned short int;
*pPointer=72;
pPointer=new unsigned short int;
*pPointer=85;

这样会导致第一个内存区域(值为72)不可用.导致程序在结束之前都无法将其释放

正确的代码如下:

unsigned short int *pPointer=new unsigned short int;
*pPointer=72;
delete pPointer
pPointer=new unsigned short int;
*pPointer=85;

注意:程序中每个new 调用都必须有对应的delete调用,应跟踪哪个指针指向了内存区域,并使用完后将内存释放,这很重要。

【开发高级指针】

【在堆中创建对象】

定义了类型Cat后,便可以声明一个指向这种对象的指针,并在堆中实例化一个Cat对象,就像在栈中实例化一样,其语法与在堆中分配int相同

Cat *pCat=new Cat;

这将调用默认构造函数-不接受任何参数的构造函数。每当在堆中或栈中创建对象时,都将调用构造函数

【删除对象】

对指向堆中对象的指针调用delete时,将调用对象的析构函数,然后释放内存。这让类有机会执行清理工作,就像销毁栈中的对象一样。

 【使用指针访问数据成员】

可以这样访问: (*pCat).GetAge();

也可以这样访问:pCat->GetAge();

【堆中的数据成员】

类可能有一个或多个数据成员为指针,指向堆中的对象。可在构造函数或成员函数中分配内存,并在析构函数中释放内存,例如以下程序

 1 #include<iostream>
 2 
 3 class SimpleCat
 4 {
 5 public:
 6     SimpleCat();
 7     ~SimpleCat();
 8      int GetAge() const {return *itsAge;}
 9      void SetAge(int age){*itsAge=age;}
10        
11      int GetWeight() const {return *itsWeight;}
12      void SetWeight(int weight){*itsWeight=weight;}
13 
14 private:
15     int *itsAge;
16     int *itsWeight;
17 };
18 
19 SimpleCat::SimpleCat()
20 {
21     itsAge=new int(2);
22     itsWeight=new int(5);
23 }
24 
25 SimpleCat::~SimpleCat()
26 {
27     delete itsAge;
28     delete itsWeight;
29 }
30 int main()
31 {
32     SimpleCat *Frisky=new SimpleCat;
33     std::cout<<"Frisky is "<<Frisky->GetAge()<<" years old\n";
34     Frisky->SetAge(23);
35     std::cout<<"Frisky is "<<Frisky->GetAge()<<" years old\n";
36     delete Frisky;
37     system("pause");
38     return 0;
39 }

第25~29行的析构函数释放分配的内存。在析构函数中,将nullptr赋给这些指针没有任何意义,因为他们将不可访问。对于已经删除的指针,应该将nullptr赋给它,但这是可违反这种规则的唯一地方,虽然遵守这种规则没有任何坏处

(调用方(这里指main())不知道itsAge和itsWeight是指向堆内存的指针,它不断的调用GetAge()和SetAge(),而内存管理的细节隐藏在类中实现)。

 

this指针:

每个类成员函数都有一个隐藏的参数:this指针,它指向相应的对象。因此,每次调用GetAge()或者SetAge()时,都通过隐藏参数包含指向相应对象的this指针。

this指针指向其函数被调用的对象,通常不需要它,而只是调用函数并设置成员变量,但偶尔需要访问对象本身(可能旨在返回一个指向当前对象的指针),在这种情况下,this指针将很有用。(该指针将在后续讲到)

 

 

悬摆指针:

悬摆指针是导致讨厌且难以发现的bug的罪魁祸首之一!!!!

对指针调用delete(从而释放他指向的内存)后,如果没有重新赋值就使用它,将导致悬摆指针

注意!!!:::对指针调用delete后,千万不要使用它。改指针仍指向原来的内存区域。但编译器可能在这里存储了其他数据;使用该指针会导致程序崩溃。更糟糕的是,程序可能继续运行,但几分钟后崩溃了。所以出于安全考虑。删除指针后,应将其设置为nullptr,这便解除了它的武装。

 

const指针:

声明指针时,可在类型前,类型后或者两个使用const 。例如下面声明都合法

const int *pOne;
int * const pTwo;
const int * const pThree;

pOne是指向整型常量的指针,即使用该指针不能修改它指向的值。  例如 *pOne=5是非法的

pTwo是指向整型的常量指针,可修改指向的整型变量,但pTwo不能指向其他变量。不能给常量指针重新赋值,例如: pTwo=&x; 是非法的

pThree 是指向整型常量的常量指针,不能修改它指向的值,也不能让它指向其他的变量。

 

const指针和const成员函数

如果声明了一个指向常量对象的指针,那么使用该指针只能调用常量函数

#include<iostream>

class Rectangle
{
public:
    Rectangle();
    ~Rectangle();
    void SetLength(int length){itsLength=length;}
    int GetLength() const {return itsLength;}

    void SetWidth(int width){itsWidth=width;}
    int GetWidth() const {return itsWidth;}


private:
    int itsLength;
    int itsWidth;

};

Rectangle::Rectangle():itsLength(10),itsWidth(5)
{
}

Rectangle::~Rectangle()
{
}
int main()
{
    Rectangle *pRect=new Rectangle;
    const Rectangle *pConstRect=new Rectangle;
    Rectangle * const pConstPtr=new Rectangle;

    std::cout<<"pRect width: "<<pRect->GetWidth()<<" feet\n";
    std::cout<<"pConstRect width: "<<pConstRect->GetWidth()<<" feet\n";
    std::cout<<"pConstPtr width: "<<pConstPtr->GetWidth()<<" feet\n";

    pRect->SetWidth(10);
    //pConstRect->SetWidth(10);
    pConstPtr->SetWidth(10);

    std::cout<<"pRect width: "<<pRect->GetWidth()<<" feet\n";
    std::cout<<"pConstRect width: "<<pConstRect->GetWidth()<<" feet\n";
    std::cout<<"pConstPtr width: "<<pConstPtr->GetWidth()<<" feet\n";
    system("pause");
    return 0;
}

可以看到由于编译器不允许pConstRect调用SetWidth()函数,只能注释掉才能运行。

运行结果如下所示:

pRect width: 5 feet
pConstRect width: 5 feet
pConstPtr width: 5 feet
pRect width: 10 feet
pConstRect width: 5 feet
pConstPtr width: 10 feet

总结:

1.指针可指向整型等简单数据类型,也可指向对象。

2.可在堆中创建对象,并将其删除。如果你声明了一个类,可声明指向其对象的指针,并在堆中实例化对象。

3.类的数据成员可以是指向堆中对象的指针。可以在类的构造函数或其他函数中分配内存,并在析构函数中将其释放。

 

今天关于copy_to_user包含数组指针的结构的讲解已经结束,谢谢您的阅读,如果想了解更多关于10、C语言 —— 指针与一维数组(指针数组与数组指针)、c – 为什么copy_to_user花费数百毫秒?、C++ 地址(指针)大小、C++(指针和高级指针)-上篇的相关知识,请在本站搜索。

本文标签: