c++ 数组长度问题

@Ta 2022-03-15 3441点击
不会 c++ 的惨状,最近要写一个 dll,因为我是写 java 的,所以代码都是先写一份 java 的,然后对照着翻译到 c++,int size = (sizeof(result) / sizeof(*result)); 这句是网上搜的 c++ 求数组长度,为什么返回是 1 ?QQ截图20220315140154.jpg
回复列表(9|隐藏机器人聊天)
  • @Ta / 2022-03-15 / /
    太痛苦了,代码要对照写两份,调试两份,本来 java 写好了,输出结果也 ok,但是搬到 c++ 后数据各种偏移,前几天被 c++ 的对象传参坑惨了,今天又冒出一个数组长度问题,本来在 java 直接 length 就有结果,不熟悉 c++ 的语法
  • @Ta / 2022-03-15 / /

    写 dll 的大佬
    小米MIX2s(白)

  • @Ta / 2022-03-15 / /

    result是一个指针类型,sizeof(result)是指针的长度(32位系统通常为4个字节,64位系统通常为8个字节)
    小米8 Explore Edition (透明色)

  • @Ta / 2022-03-16 / /

    @yiluo不要用传统C风格数组和指针。

    无法从作为参数传递过来的C数组指针获取数据长度(6楼代码注释里解释了为什么),必须单独传递数据长度,或者使用“结束标记”(比如像C字符串一样使用'\0'作为结束)。

    应该使用和Java类似的容器对象(在C++中称为“标准模板库”STL)。

    比如STL的“向量”容器(std::vector<>)就和Java的ArrayList<>非常类似。

    #include <stdio.h>
    #include <vector>
    
    using namespace std;
    
    int main() {
        vector<float> result;
        for (int i=0; i<100; i++) {
            result.push_back(i * 3.1415926);
        }
        printf("result size: %d\n", result.size());
        printf("result[12] = %f\n", result[12]);
        result[33] = 12315.0;
        printf("result[33] = %f\n", result[33]);
        result.resize(51);
        printf("result size: %d\n", result.size());
    
        return 0;
    }
    

    result size: 100
    result[12] = 37.699112
    result[33] = 12315.000000
    result size: 51

  • @Ta / 2022-03-16 / /

    @yiluo,如果想像Java一样自动管理内存,可以使用智能指针:

    #include <stdio.h>
    #include <vector>
    #include <memory>
    
    using namespace std;
    
    void fixBar(shared_ptr<vector<float>> result) {
        printf("fixBar: result size: %d\n", result->size());
        for (int i=0; i<result->size(); i++) {
            result->at(i) += 12315.0;
        }
        result->push_back(31152.0);
    }
    
    int main() {
        // auto自动推断result的类型,它在此处相当于shared_ptr<vector<float>>。
        // make_shared创建一个由C++自动管理生命周期的共享对象,
        // 可以把该对象传递到任何地方,都不会失效,并且始终是同一个对象。
        // 和Java一样,在该对象的所有引用都释放后,该对象自动释放。
        auto result = make_shared<vector<float>>();
    
        for (int i=0; i<100; i++) {
            // result是引用类型而不是值类型,所以使用“->”取代“.”作为方法调用操作符。
            result->push_back(i * 3.1415926);
        }
        printf("result size: %d\n", result->size());
    
        // 因为result现在是引用类型,所以对它进行数组操作需要先解引用。
        // 由于操作符优先级的关系,需要加括号。
        (*result)[12] = 12315.0;
        printf("result[12] = %f\n", (*result)[12]);
    
        // 或者使用->at()方法,可读性更好。
        result->at(12) = 12306.0;
        printf("result[12] = %f\n", result->at(12));
    
        printf("result[33] = %f\n", result->at(33));
        result->resize(51);
        printf("result size: %d\n", result->size());
    
        fixBar(result);
        printf("after fixBar: result size: %d\n", result->size());
        printf("result[12] = %f\n", result->at(12));
        printf("result[33] = %f\n", result->at(33));
    
        return 0;
    }
    

    result size: 100
    result[12] = 12315.000000
    result[12] = 12306.000000
    result[33] = 103.672554
    result size: 51
    fixBar: result size: 51
    after fixBar: result size: 52
    result[12] = 24621.000000
    result[33] = 12418.672852

  • @Ta / 2022-03-16 / /

    @yiluo,如果想用固定大小的数组,可以用std::array
    如果想把数组作为参数传递给只接收传统C风格数组的外部函数,可以使用data()方法。

    #include <stdio.h>
    #include <memory>
    #include <array>
    #include <vector>
    
    using namespace std;
    
    // 一个只能接收C数组的外部函数
    // 注意:C数组必然需要单独传递大小,除非它使用特殊的取值作为结束标记(比如'\0')。
    void ExternalFunction1(float result[], int size) {
        printf("fake result size: %d\n", sizeof(result));
        printf("real result size: %d\n", size);
        printf("result[%d] = %f\n", size-1, result[size-1]);
    }
    
    // 还是只能接收C数组的外部函数
    // C数组之所以需要单独传递大小,是因为参数中的C数组写法(xxx[])实际上是C指针(*xxx)的语法糖。
    // 所以其实该函数和上面的函数完全相同。
    void ExternalFunction2(float *result, int size) {
        // 这获取到的是谁的大小?
        // 首先思考一个问题:result是什么?是一个float类型的指针。
        // 所以这获取到的自然是指针的大小。
        // 在32位程序中,一个指针4字节。在64位程序中,一个指针8字节。
        printf("fake result size: %d\n", sizeof(result));
    
        // 有没有其他方法仅通过指针就能获取它指向的数据大小?
        // 答案是没有。指针所包含的全部信息,只是一个内存地址:一个,不是一批,只有开始,没有结束。
        // 所以光靠指针,我们只能找到第一个数据,然后我们只能沿着指针往后移动,找到后续数据。
        // 如果数据本身不能指示自己在哪里结束,并且也没有额外的参数告诉我们数据在哪里结束,
        // 光靠指针我们当然不知道数据在哪里结束。
    
        // C风格指针必须结合额外信息才能得知数据的结束位置,可以是这些信息:
        // 1. 数据自己标识自己哪里结束。比如放一个'\0'作为结束标记。C风格字符串就是这么做的。
        // 2. 额外传递长度参数,大部分C风格函数都是这么做的,通常有两个参数,一个指针一个长度。
        // 3. 分别传递开始和结束指针,有些迭代器函数是这么做的。用结束指针减去开始指针就是数据长度。
    
        printf("real result size: %d\n", size);
        printf("result[%d] = %f\n", size-1, result[size-1]);
    }
    
    int main() {
        // 固定大小的数组对象,模板参数第一个是类型,第二个是数组大小
        auto result1 = make_shared<array<float, 100>>();
        // 默认为空的数组对象,相当于Java的ArrayList
        auto result2 = make_shared<vector<float>>();
    
        for (int i=0; i<result1->size(); i++) {
            result1->at(i) = i * 3.1415926; // 固定大小,所以直接赋值
            result2->push_back(i * 3.1415926); // 默认为空,所以需要插入
        }
    
        printf("result1 size: %d, result2 size: %d\n", result1->size(), result2->size());
        printf("result1[12] = %f, result2[12] = %f\n", result1->at(12), result2->at(12));
    
        result1->at(33) = 12315.0;
        result2->at(33) = 12315.0;
        printf("result1[33] = %f, result2[33] = %f\n", result1->at(33), result2->at(33));
    
        printf("--- result 1 ---\n");
        // 如何把数组对象传递给只能接收传统C风格数组指针的外部函数?
        // 使用->data()方法就可以。
        ExternalFunction1(result1->data(), result1->size());
    
        printf("--- result 2 ---\n");
        ExternalFunction1(result2->data(), result2->size());
    
        return 0;
    }
    

    result1 size: 100, result2 size: 100
    result1[12] = 37.699112, result2[12] = 37.699112
    result1[33] = 12315.000000, result2[33] = 12315.000000
    --- result 1 ---
    fake result size: 8
    real result size: 100
    result[99] = 311.017670
    --- result 2 ---
    fake result size: 8
    real result size: 100
    result[99] = 311.017670

  • @Ta / 2022-03-16 / /

    @yiluo,既然Java是完全的面向对象编程,所以改成C++的时候,就应该用对象取代对象。

    • 使用std::string代替Java String。
    • 使用std::array代替简单Java数组。
    • 使用std::vector代替Java ArrayList。
    • 使用std::list代替Java LinkedList。
    • 使用std::unordered_map代替Java HashMap。

    它们是完全对应的数据结构,无论是性能、实现方法还是实际用起来都很类似。
    再结合智能指针,就可以实现和Java一样的面向对象+自动内存管理了。

    早日抛弃C风格字符串和数组,让C++生活更轻松。

  • @Ta / 2022-03-16 / /

    @yiluo,可以在这里找到标准模板库里可用的容器:https://zh.cppreference.com/w/cpp/container
    这是字符串库(std::string)的文档:https://zh.cppreference.com/w/cpp/string
    智能指针的文档:https://zh.cppreference.com/w/cpp/memory/shared_ptr


    要使用某个容器时,首先需要引用它所在的头文件。

    比如std::vector定义于头文件 <vector>,所以开头要加#include <vector>

    图片.png

  • @Ta / 2022-03-16 / /
    老虎真是太牛了!
添加新回复
回复需要登录