简体   繁体   English

通过函数修改数组的元素

[英]Modifying elements of an array through a function

I'm learning about pointers and I can't get this code to work. 我正在学习有关指针的信息,但无法使此代码正常工作。 Here's what I have so far: 这是我到目前为止的内容:

void incrementArray(int* a[]) {
    for(auto& x : a) {
        ++x;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    int array[] = {0,1,2,3,4,5,6,7,8,9};

    for(auto x : array) {
        cout << x << '\n';
    }

    incrementArray(&array);

    for(auto x : array) {
        cout << x << '\n';
    }
}

I'm getting the following error: 我收到以下错误:

'incrementArray' : cannot convert parameter 1 from 'int (*)[10]' to 'int *[]' 'incrementArray':无法将参数1从'int(*)[10]'转换为'int * []'

What can I do to fix my code? 我该如何解决我的代码?

C-style arrays have funny syntax. C样式的数组具有有趣的语法。 To pass the array to a function, use int a[] This does not copy the array and changes to the array inside the function will modify the external array. 要将数组传递给函数,请使用int a[]这不会复制数组,并且对函数内部数组的更改将修改外部数组。 You only need to call incrementArray(array); 您只需要调用incrementArray(array); no & needed 否与需要

You could try using std::array class which follows more normal syntax. 您可以尝试使用遵循更常规语法的std::array类。

you have a pointer as a parameter (a reference to an array), but you wish to modify the actual thing it's pointing to, so you gotta change *a, not a. 您有一个指针作为参数(对数组的引用),但是您希望修改它所指向的实际对象,因此必须更改* a而不是a。

You could use an array, vector, list, etc object that would have methods already associated to them that do most of the manipulation you could want 您可以使用数组,向量,列表等对象,该对象具有已经与它们关联的方法,这些方法可以执行您想要的大多数操作

What you are trying to do will not work since the signature of a function taking int a[] as an argument does not contain the necessary length information needed to write a for-each loop (ie to instantiate the begin() and end() templates needed to use the for-each syntax). 您尝试执行的操作将不起作用,因为以int a[]作为参数的函数的签名不包含编写for-each循环(即,实例化begin()end()所需的必要长度信息。使用for-each语法所需的模板)。 GCC's warning says this fairly clearly: GCC的警告相当清楚地表明了这一点:

Error:(14, 19) cannot build range expression with array function parameter 'a' since 
parameter with array type 'int *[]' is treated as pointer type 'int **'

I thought this might be do-able with a template, but . 我认为使用模板可以做到这一点,但是。 . .

EDIT: 编辑:

It can be done with templates, just took me a moment to wrap my head around the syntax. 可以使用模板来完成,只是花了我一点时间来围绕语法。 Here is your example in working condition: 这是您的工作状态示例:

template <size_t N>
void incArray(int (&a)[N]) {
    for(auto& x : a) {
        ++x;
    }
}

int main(int argc, const char * argv[])
{
    int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto x : array) {
        cout << x << " ";
    }
    cout << endl;

    incArray(array);

    for (auto x : array) {
        cout << x << " ";
    }
    cout << endl;

    return 0;
}

There are a couple approaches you could take to increment the elements of an array, all of which require knowing where to start and where to end. 您可以采用几种方法来增加数组的元素,所有这些方法都需要知道从哪里开始和从哪里结束。 The simple way of doing what you want is to just pass the start and end address pointers, but you could also pass a start address with some offset. 执行所需操作的简单方法是仅传递起始地址和结束地址指针,但是您也可以传递具有一定偏移量的起始地址。 Since you are using a C-Style array, your int element has and address int* , so your std::begin(array) is an int* to the first element while std::end(array) points to the address of the location after your last allocated element. 由于您使用的是C-Style数组,因此int元素的地址为int* ,因此std::begin(array)是第一个元素的int* ,而std::end(array)指向第一个元素的地址最后分配的元素之后的位置。 In your program, the std::end() address points to the memory location after your 10th allocated element. 在您的程序中, std::end()地址指向您分配的第10个元素之后的内存位置。 If you had an array with a size allocation (int other_arr[40]), std::end() will point to the first address after the allocation ( std::end(other_arr) would be std::begin(other_arr)+41 ). 如果您有一个具有大小分配的数组(int other_arr [40]),则std::end()将指向分配后的第一个地址( std::end(other_arr)将为std::begin(other_arr)+41 )。 C++ has recently introduced non-member std::begin() and std::end() in the <iterator> library, which returns a pointer to the respective element locations in your C-Array. C ++最近在<iterator>库中引入了非成员std::begin()std::end() ,该库返回指向C数组中各个元素位置的指针。

#include <algorithm>    // std::copy
#include <iostream>     // std::cout
#include <iterator>     // std::begin

void increment_elements(int* begin, const int* end) {
    while (begin != end) {
        ++(*begin); 
        ++begin;
    }
}

// An increment functor for std::transform
int increase_element(int i) {
    return ++i;
}

int main() {
    int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    for (const int x : array) {
        std::cout << x << ' ';
    }
    std::cout << '\n';

    increment_elements(std::begin(array), std::end(array));

    // Another way to write your print statement above
    std::copy(std::begin(array), 
              std::end(array), 
              std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';

    // Transform array elements through increase_element() 
    // and print result to cout.
    std::transform(std::begin(array),
                   std::end(array),
                   std::ostream_iterator<int>(std::cout, " "),
                   increase_element);
    std::cout << '\n';
}

The generalized version of the increment_elements() function can be found in the <algorithm> library as the function std::transform() documented here . 可以在<algorithm>库中找到increment_elements()形式的increment_elements()函数的通用版本,作为此处记录的函数std::transform()

Since you are learning now, here are some habits that you can start to utilize: 由于您现在正在学习,因此您可以开始使用以下一些习惯:

Do not use using namespace std; 不要使用using namespace std; at the global level. 在全球范围内。 By pulling everything in the standard library into the global namespace, you "pollute" it with functionality that can be called if a function call for it exists, since it doesn't require a std:: prefix qualification. 通过将标准库中的所有内容提取到全局名称空间中,您可以对其进行“污染”,如果存在对其的函数调用,则可以对其进行调用,因为它不需要std::前缀限定。 Say you were to write a function that calculated the euclidean distance between two (x,y) points, double distance(Point* p1, Point* p2) . 假设您要编写一个函数,该函数计算两个(x,y)点之间的欧式距离,即double distance(Point* p1, Point* p2) You decide to use any of the STL containers, such as <vector> . 您决定使用任何STL容器,例如<vector> The containers utilize the <iterator> library, which has its own std::distance(T*, T*) function to calculate the distance between two addresses in memory. 容器利用<iterator>库,该库具有自己的std::distance(T*, T*)函数来计算内存中两个地址之间的距离。 By bringing std into the global namespace by using namespace std; 通过using namespace std;std引入全局命名using namespace std; , you now have 2 functions with the same signature in the same namespace, that do 2 completely different things. ,您现在拥有2个在相同名称空间中具有相同签名的函数,它们执行2个完全不同的事情。 This is very bad yet easily avoidable. 这非常糟糕,但很容易避免。 This general guideline is probably unnecessary for small projects, but I still recommend you just don't ever do it for any project. 对于小型项目,可能不需要此通用准则,但是我仍然建议您不要对任何项目都这样做。 Ever. 曾经

const or const T& your read only operations. constconst T&您的只读操作。 When doing operations where you are pulling data for reading and you don't want to modify the data, initialize using const or const T& . 在进行操作以提取数据以进行读取而又不想修改数据时,请使用constconst T&初始化。 const by itself is sufficient for primitive datatypes (int, float, double, char), but non-primitives will require const T& ( T is the type). 对于原始数据类型(int,float,double,char)而言, const本身就足够了,但非原始数据将需要const T&T是类型)。 Values that are of type const T& (called const referenced) are read-only ( const ) and directly accessed ( & ). const T&类型的值(称为const引用)是只读的( const ),可以直接访问( & )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM