通过RealSense代码说明一些C语言问题

描述

以前也读了这个RGBD相机的一些源码但是发现自己的基本功是一点也不好,所以就搁置了很久,今天试图来回答一些问题。

说好的不会再出新品结果还是出了,估计这个市场还是可以的

命令

自己就看一个例子里面的代码

命令

就一个C文件,看的也简单一些

 

set(CMAKE_EXPORT_COMPILE_COMMANDS 1)


add_executable(rs-color rs-color.c ../example.h)
include_directories(../../common ../../third-party/imgui ../../C)
target_link_libraries(rs-color ${DEPENDENCIES})
set_target_properties (rs-color PROPERTIES
FOLDER "Examples/C"
)


install(TARGETS rs-color RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

 

编译的文件都是使用Cmake来控制的,所以一开始写一些这个。

set(CMAKE_EXPORT_COMPILE_COMMANDS 1) 这个命令会让CMake在编译过程中输出 JSON 格式的编译命令到 compile_commands.json 文件中。这对于IDE的代码补全和静态分析很有帮助。

add_executable(rs-color rs-color.c ../example.h) 这个命令定义了一个rs-color的可执行目标,源代码文件是rs-color.c和example.h。

include_directories(../../common ../../third-party/imgui ../../C) 这个命令指定了包含路径,用于解析rs-color.c和example.h中的#include语句。

target_link_libraries(rs-color ${DEPENDENCIES}) 这个命令为rs-color目标指定了链接库,其中${DEPENDENCIES}是一个变量,包含所有依赖库的名字。

set_target_properties (rs-color PROPERTIES FOLDER "Examples/C" )这个命令为rs-color目标设置了一些属性,这里指定了在IDE的项目视图中,rs-color会出现在Examples/C文件夹下。

命令

直接下来就看几个代码,是不是和学的C语言不太一样

这条语句rs2_error *e = 0;

1. 定义了一个指针e,指向rs2_error类型。

2. 将e初始化为0,也就是RS2_ERROR_NONE,表示没有错误。

 

rs2_context *ctx = rs2_create_context(RS2_API_VERSION, &e);

 

源码使用 Intel RealSense SDK 的 C API 创建了一个上下文对象 rs2_context,并将其地址赋给 ctx 变量。

同时,它传递了 RealSense SDK 的 API 版本号 RS2_API_VERSION 给 rs2_create_context() 函数,该函数将返回一个 rs2_context 对象。

在创建上下文对象时,还传递了一个错误处理器(error-handler)的地址 &e,以便在运行时捕获可能出现的错误。

如果发生someError,会通过*e = ...将e设置为相应的错误码。之后,在调用者可以检查*e的值来查看doSomething()是否发生错误。这是C语言中一个很常见的错误处理方式 - 通过指针传递错误码,如果有错误,被调用的函数会设置该指针为错误值。

命令

调用者在调用后检查该值来进行错误处理。这种方式的好处是:

可以从函数返回额外的错误信息(除了函数返回值本身)。

不需要定义专门的错误返回类型,可以直接使用指向已有类型的指针。

简单和实用,适用于C语言的语法和习惯。

当然,这个方式也有一定的缺点,比如:

1. 如果不注意检查错误指针,难以发现错误,这会影响健壮性。

2. 错误指针需要与函数的正确返回值区分开来检查,有一定难度。

3. 错误信息只能通过指定的指针值返回,表达能力较有限。所以,对于rs2_error *e这样的指针,是可以直接作为C函数的参数进行传递的,这是C语言中一种简单实用的错误处理方式,但是也有一定的缺陷。

命令

在这里被定义了很多

命令

更具体的实现

1.析构函数~rs2_context(),在rs2_context被释放时会调用ctx->stop()。这是用于停止某个线程或后台服务。

2. 一个std::shared_ptr<:context>成员ctx。这是一个librealsense库中的context对象,通过shared_ptr智能指针进行管理。可以猜测,这个rs2_context结构体表示一个与librealsense的context相关的对象,它在释放时会停止该context,并通过shared_ptr管理context的生命周期。 其实这也是一个使用RAII(Resource Acquisition Is Initialization),通过在对象构造和析构时启动和停止资源,实现资源的自动管理。

 

 

  rs2_context *ctx = rs2_create_context(RS2_API_VERSION, &e);

 

这段代码中,通过调用rs2_create_context()函数创建了一个rs2_context 实例,并将该实例的指针赋值给了 ctx 变量。这个 rs2_context 实例是在 rs2_create_context() 函数内部创建并初始化的,然后将其地址返回给调用者,调用者通过 ctx 变量来访问和操作该实例。

可以说rs2_context实例已经被初始化并设置好了,但不能直接说 rs2_context 里面是一个已经初始化过的结构体,因为 rs2_context 只是一个指针类型,并不存储任何数据。

结构体指针在 C 语言中被广泛使用,因为它可以方便地访问和操作结构体中的成员,而不需要对整个结构体进行复制。结构体指针可以通过指针运算和取值运算符来访问结构体成员,这使得对结构体的操作变得更加高效。

以下是一些常见的使用结构体指针的场景:

传递结构体指针作为函数参数:这种情况下,函数可以通过指针来访问和修改结构体的成员,从而避免了对整个结构体进行复制,提高了程序的效率。

动态内存分配:当需要动态创建一个结构体对象时,需要使用结构体指针来指向该对象。在动态分配内存时,使用结构体指针可以确保只分配所需的内存,并且可以方便地释放分配的内存。

链表和树等数据结构:链表和树等数据结构通常使用结构体指针来连接不同的节点,从而构建出完整的数据结构。

 

 rs2_context *rs2_create_context(int api_version, rs2_error **error);

 

- api_version参数指定了librealsense API的版本号,用于向后兼容。
-e是个指针,如果有错误发生,会被设置为错误码。这是C语言常见的错误返回方式。

包含了以下字段:

message:错误消息,描述错误 details 的字符串。

function:发生错误的函数名。

args:发生错误的函数调用的参数。

exception_type:错误类型,很可能是一个enum,表示出错的原因。这个结构体通常用来表示异常信息,或是库函数调用后的错误详情。

 

c++
struct rs2_error 
{
    std::string message;   // 错误消息
    std::string function;  // 函数名
    std::string args;     // 函数参数
    rs2_exception_type exception_type; // 错误类型
};

 

如果一个函数返回一个结构体类型的值,那么需要使用一个结构体类型的变量来接收这个返回值。因为结构体类型通常比较大,如果直接将结构体类型的值作为函数的返回值返回,会导致复制整个结构体的内存空间,从而影响程序的性能。因此,C 语言中通常使用指向结构体的指针作为返回值,而不是直接返回结构体类型的值。

 

#include 
#include 


struct person {
    char name[50];
    int age;
};


struct person* create_person(const char* name, int age) {
    struct person* p = (struct person*) malloc(sizeof(struct person));
    if (p != NULL) {
        strcpy(p->name, name);
        p->age = age;
    }
    return p;
}


int main() {
    struct person* p = create_person("John Doe", 30);
    if (p != NULL) {
        printf("Name: %s
", p->name);
        printf("Age: %d
", p->age);
        free(p);
    }
    return 0;
}

 

完整代码

命令

看这个

在上面的例子中,create_person() 函数返回一个指向:

 

 struct person

 

结构体的指针。

该函数使用malloc()函数动态分配内存来存储结构体,然后将结构体的成员赋值为传入的参数。最后,该函数返回指向该结构体的指针。在 main() 函数中,调用 create_person() 函数来创建一个结构体实例,并打印出其成员的值。在使用完结构体后,还需要调用 free() 函数来释放动态分配的内存。

更具体的来分析:

 

    struct person* p = (struct person*) malloc(sizeof(struct person));

 

调用malloc()函数,请求分配sizeof(struct person)字节的内存块。这将分配足够存放person结构体的内存。

malloc()返回 void *,所以我们需要强制类型转换为struct person*。这会将void *转换为person结构体的指针。

person *p将保存分配的内存块地址,我们可以通过p访问这个person对象。

分配的内存未初始化,需要手工初始化 person 对象的各个字段。

使用malloc()获取内存,强制类型转换为需要的指针类型,使用后释放内存。

 

https://www.intelrealsense.com/
https://github.com/IntelRealSense/librealsens

 

审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分