LDN
LDN

Reputation: 39

std::array equivalent in C

I'm new to C and C++, and I've read that at least in C++ it's preferable to use std::array or std::vector when using vectors and arrays, specially when passing these into a function.

In my research I found the following, which makes sense. I suppose using std::vector would fix the problem of indexing outside of the variable's scope.

void foo(int arr[10]) { arr[9] = 0; }

void bar() {
    int data[] = {1, 2};
    foo(data);
}

The above code is wrong but the compiler thinks everything is fine and issues no warning about the buffer overrun.

Instead use std::array or std::vector, which have consistent value semantics and lack any 'special' behavior that produces errors like the above.

(answer from bames53, thanks btw!)

What I want to code is

float foo(int X, int Y, int l){
    // X and Y are arrays of length l
    float z[l];
    for (int i = 0; i < l; i ++){
        z[i] = X[i]+Y[i];
    }
    return z;
}

int bar(){
    int l = 100;
    int X[l];
    int Y[l];
    float z[l];
    z = foo(X,Y,l);
    return 0;
}

I want this to be coded in C, so my question is is there a std::vector construct for C? I couldn't find anything on that.

Thanks in advance, also please excuse my coding (I'm green as grass in C and C++)

Upvotes: 3

Views: 1495

Answers (3)

JVApen
JVApen

Reputation: 11317

Your question might be sensitive for some programmers of the language. Using constructs of one language into another can be considered cursing as different languages have different design decisions.

C++ and C share a huge part, in a way that C code can (without a lot of modifications) be compiled as C++. However, if you learn to master C++, you will realize that a lot of strange things happen because how C works.

Back to the point: C++ contains a standard library with containers as std::vector. These containers make use of several C++ constructions that ain't available in C:

  • RAII (the fact that a Destructor gets executed when the instance goes out-of-scope) will prevent a memory leak of the allocated memory
  • Templates will allow type safety to not mix doubles, floats, classes ...
  • Operator overloading will allow different signatures for the same function (like erase)
  • Member functions

None of these exist in C, so in order to have a similar structure, several adaptions are required for getting a data structure that behaves almost the same.

In my experience, most C projects have their own generic version of data structures, often based on void*. Often this will look similar like:

 struct Vector
 {
      void *data;
      long  size;
      long  capacity;
 };

 Vector *CreateVector()
 {
      Vector *v = (Vector *)(malloc(sizeof(Vector)));
      memset(v, 0, sizeof(Vector));
      return v;
 }

 void DestroyVector(Vector *v)
 {
      if (v->data)
      {
          for (long i = 0; i < v->size; ++i)
              free(data[i]);
          free(v->data);
      }
      free(v);
 }

 // ...

Alternatively, you could mix C and C++.

 struct Vector
 {
      void *cppVector;
 };

 #ifdef __cplusplus
 extern "C" {
 #endif

 Vector CreateVector()
 void DestroyVector(Vector v)

 #ifdef __cplusplus
 }
 #endif

vectorimplementation.cpp

 #include "vector.h"

 struct CDataFree
 {
       void operator(void *ptr) { if (ptr) free(ptr); }
 };
 using CData = std::unique_ptr<void*, CDataFree>;

 Vector CreateVector()
 {
      Vector v;
      v.cppVector = static_cast<void*>(std::make_unique<std::vector<CData>>().release());
      return v;
 }

 void DestroyVector(Vector v)
 {
      auto cppV = static_cast<std::vector<CData>>(v.cppVector);
      auto freeAsUniquePtr = std::unique_ptr<std::vector<CData>>(cppV);
 }

 // ...

Upvotes: 0

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

The closest equivalent of std::array in c is probably a preprocessor macro defintion like

#define ARRAY(type,name,length) \
     type name[(length)]

Upvotes: -1

melpomene
melpomene

Reputation: 85837

Standard C has nothing like std::vector or other container structures. All you get is built-in arrays and malloc.

I suppose using std::vector would fix the problem of indexing outside of the variable's scope.

You might think so, but you'd be wrong: Indexing outside of the bounds of a std::vector is just as bad as with a built-in array. The operator[] of std::vector doesn't do any bounds checking either (or at least it is not guaranteed to). If you want your index operations checked, you need to use arr.at(i) instead of arr[i].

Also note that code like

float z[l];
...
return z;

is wrong because there are no array values in C (or C++, for that matter). When you try to get the value of an array, you actually get a pointer to its first element. But that first element (and all other elements, and the whole array) is destroyed when the function returns, so this is a classic use-after-free bug: The caller gets a dangling pointer to an object that doesn't exist anymore.

The customary C solution is to have the caller deal with memory allocation and pass an output parameter that the function just writes to:

void foo(float *z, const int *X, const int *Y, int l){
    // X and Y are arrays of length l
    for (int i = 0; i < l; i ++){
        z[i] = X[i]+Y[i];
    }
}

That said, there are some libraries that provide dynamic data structures for C, but they necessarily look and feel very different from C++ and std::vector (e.g. I know about GLib).

Upvotes: 5

Related Questions