编写一个函数的原型,该函数采用正好16个整数的数组

时间:2011-01-17 06:11:27

标签: c++ c arrays function-prototypes

其中一个面试问题让我“编写了一个C函数的原型,它采用了16个整数的数组”,我想知道它可能是什么?也许像这样的函数声明:

void foo(int a[], int len);

还是其他什么?

如果语言是C ++,那该怎么办?

5 个答案:

答案 0 :(得分:44)

在C中,这需要一个指向16个整数数组的指针:

void special_case(int (*array)[16]);

将通过以下方式调用:

int array[16];
special_case(&array);

在C ++中,您也可以使用对数组的引用,如Nawaz的回答所示。 (问题在标题中询问C,最初只在标签中提到C ++。)


使用以下变体的任何版本:

void alternative(int array[16]);

最终相当于:

void alternative(int *array);

在实践中将接受任何大小的数组。


问题是问题 - special_case()是否真的阻止传递不同大小的数组。答案是'是'。

void special_case(int (*array)[16]);

void anon(void)
{

    int array16[16];
    int array18[18];
    special_case(&array16);
    special_case(&array18);
}

编译器(MacOS X 10.6.6上的GCC 4.5.2,正好相反)抱怨(警告):

$ gcc -c xx.c
xx.c: In function ‘anon’:
xx.c:9:5: warning: passing argument 1 of ‘special_case’ from incompatible pointer type
xx.c:1:6: note: expected ‘int (*)[16]’ but argument is of type ‘int (*)[18]’
$

更改为GCC 4.2.1 - 由Apple提供 - 警告是:

$ /usr/bin/gcc -c xx.c
xx.c: In function ‘anon’:
xx.c:9: warning: passing argument 1 of ‘special_case’ from incompatible pointer type
$

4.5.2中的警告更好,但实质内容相同。

答案 1 :(得分:11)

有几种方法可以声明固定大小的数组参数:

void foo(int values[16]);

接受任何指针 - int,但数组大小用作文档

void foo(int (*values)[16]);

接受指向具有正好16个元素的数组的指针

void foo(int values[static 16]);

接受指向具有至少16个元素

的数组的第一个元素的指针
struct bar { int values[16]; };
void foo(struct bar bar);

接受一个装有16个元素的数组的结构,并按值传递它们。

答案 2 :(得分:6)

<强>&安培;在C ++中是必要的:

void foo(int (&a)[16]); // & is necessary. (in C++)

注意:&amp;是必要的,否则你可以传递任何大小的数组!


对于C:

void foo(int (*a)[16]) //one way
{
}

typedef int (*IntArr16)[16]; //other way
void bar(IntArr16 a)
{
}

int main(void) 
{
        int a[16];
        foo(&a); //call like this - otherwise you'll get warning!
        bar(&a); //call like this - otherwise you'll get warning!
        return 0;
}

演示:http://www.ideone.com/fWva6

答案 3 :(得分:2)

我认为最简单的类型安全方法是声明一个包含数组的结构,然后传递:

struct Array16 {
  int elt[16];
};


void Foo(struct Array16* matrix);

答案 4 :(得分:1)

您已经获得了C的一些答案,以及C ++的答案,但还有另一种方法可以在C ++中完成。

正如Nawaz所说,要传递一个N大小的数组,你可以用C ++做到这一点:

const size_t N = 16; // For your question.

void foo(int (&arr)[N]) {
    // Do something with arr.
}

但是,从C ++ 11开始,您还可以使用std :: array容器,它可以使用更自然的语法传递(假设您熟悉模板语法)。

#include <array>

const size_t N = 16;

void bar(std::array<int, N> arr) {
    // Do something with arr.
}

作为容器,std :: array允许与普通C风格数组大致相同的功能,同时还增加了其他功能。

std::array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
int arr2[5] = { 1, 2, 3, 4, 5 };

// Operator[]:
for (int i = 0; i < 5; i++) {
    assert(arr1[i] == arr2[i]);
}

// Fill:
arr1.fill(0);
for (int i = 0; i < 5; i++) {
    arr2[i] = 0;
}

// Check size:
size_t arr1Size = arr1.size();
size_t arr2Size = sizeof(arr2) / sizeof(arr2[0]);

// Foreach (C++11 syntax):
for (int &i : arr1) {
    // Use i.
}
for (int &i : arr2) {
    // Use i.
}

但是,据我所知(当时肯定是有限的),除非你使用成员函数data()来获取实际数组的地址,否则指针算术对std :: array是不安全的。第一。这既是为了防止将来对std :: array类的修改破坏你的代码,也因为一些STL实现可能会存储除实际数组之外的其他数据。

请注意,这对新代码最有用,或者如果您将预先存在的代码转换为使用std :: arrays而不是C样式数组。由于std :: arrays是聚合类型,它们缺少自定义构造函数,因此您无法直接从C样式数组切换到std :: array(不能使用强制转换,但这很难看,并且可以可能在将来引起问题)。要转换它们,您需要使用以下内容:

#include <array>
#include <algorithm>

const size_t N = 16;

std::array<int, N> cArrayConverter(int (&arr)[N]) {
    std::array<int, N> ret;

    std::copy(std::begin(arr), std::end(arr), std::begin(ret));

    return ret;
}

因此,如果您的代码使用C风格的数组,并且将其转换为使用std :: arrays是不可行的,那么最好不要使用C风格的数组。

(注意:我将尺寸指定为N,因此您可以更轻松地在任何需要的地方重复使用代码。)

编辑:我忘记提及的一些事情:

1)设计用于在容器上操作的大多数C ++标准库函数是与实现无关的;它们不是针对特定容器设计的,而是使用迭代器在范围上运行。 (这也意味着它们适用于std::basic_string及其实例化,例如std::string。)例如,std::copy具有以下原型:

template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last,
                    OutputIterator result);
// first is the beginning of the first range.
// last is the end of the first range.
// result is the beginning of the second range.

虽然这看起来很有吸引力,但您通常不需要指定模板参数,只需让编译器为您处理。

std::array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
std::array<int, 5> arr2 = { 6, 7, 8, 9, 0 };
std::string str1 = ".dlrow ,olleH";
std::string str2 = "Overwrite me!";

std::copy(arr1.begin(), arr1.end(), arr2.begin());
// arr2 now stores { 1, 2, 3, 4, 5 }.

std::copy(str1.begin(), str1.end(), str2.begin());
// str2 now stores ".dlrow ,olleH".
// Not really necessary for full string copying, due to std::string.operator=(), but possible nonetheless.

由于依赖于迭代器,这些函数也与C风格的数组兼容(因为迭代器是指针的泛化,所有指针都是定义迭代器(但并非所有迭代器都必须是指针))。这在处理遗留代码时非常有用,因为这意味着您可以完全访问标准库中的范围函数。

int arr1[5] = { 4, 3, 2, 1, 0 };
std::array<int, 5> arr2;

std::copy(std::begin(arr1), std::end(arr1), std::begin(arr2));

您可能已经从此示例中注意到,std::array.begin()std::begin()可以与std::array互换使用。这是因为std::begin()std::end()的实现使得对于任何容器,它们具有相同的返回类型,并返回相同的值,如调用begin()end()成员该容器实例的功能。

// Prototype:
template <class Container>
auto begin (Container& cont) -> decltype (cont.begin());

// Examples:
std::array<int, 5> arr;
std::vector<char> vec;

std::begin(arr) == arr.begin();
std::end(arr) == arr.end();

std::begin(vec) == vec.begin();
std::end(vec) == vec.end();

// And so on...

C风格的数组没有成员函数,因此需要使用std::begin()std::end()。在这种情况下,两个函数被重载以提供适用的指针,具体取决于数组的类型。

// Prototype:
template <class T, size_t N>
T* begin (T(&arr)[N]);

// Examples:
int arr[5];

std::begin(arr) == &arr[0];
std::end(arr) == &arr[4];

作为一般经验法则,如果您不确定任何特定代码段是否必须使用C风格的数组,那么使用std::begin()和{{更安全“会更安全1}}。

[请注意,虽然我使用std::end()作为示例,但在标准库中使用范围和迭代器非常常见。大多数(如果不是全部)旨在对容器进行操作的函数(或更具体地,Container concept的任何实现,例如std::copy()std::arraystd::vector)使用范围,使它们与任何当前和未来的容器以及C风格的阵列兼容。但是,我不知道这种广泛的兼容性可能有例外。]

2)当按值传递std :: array时,可能会有相当大的开销,具体取决于数组的大小。因此,通过引用传递它或使用迭代器(如标准库)通常会更好。

std::string

3)所有这些示例都假设您的代码中的所有数组都具有相同的大小,由常量// Pass by reference. const size_t N = 16; void foo(std::array<int, N>& arr); 指定。为了使您的代码更多地与实现无关,您可以使用范围&amp;自己迭代,或者如果你想让代码集中在数组上,使用模板化函数。 [以this answer to another question为基础。]

N

如果这样做,你甚至可以模拟数组的成员类型,前提是你的代码可以在int以外的类型上运行。

template<size_t SZ> void foo(std::array<int, SZ>& arr);

...

std::array<int, 5> arr1;
std::array<int, 10> arr2;

foo(arr1); // Calls foo<5>(arr1).
foo(arr2); // Calls foo<10>(arr2).

有关此操作的示例,请参阅here

如果有人发现我可能错过的任何错误,请随时指出我修理或自行解决。我想我抓住了所有这些,但我并不是百分百肯定。