Multidimensional Arrays with Pointers

Hi all, I’m having a problem getting my syntax right for the class below. Could someone please help?

I am inputting a string of commands separated by semicolons:
s1=10;s2=1;p=100

In one function, I split this into the individual commands to end up with:
array[] =
{s1=10
s2=1
p=100}

The second function then splits the new array into two columns, so trying to show in words:
array2[][0] =
{s1
s2
p}
array2[][1] =
{10
1
100}

Here is the code but it has a moan when I try to compile:

#include "INPUT.h"
#include <string.h>
#define  MAXCOMMANDS    11
#define  MAXCOMMANDSIZE 11

void Input::parse(char* record, char* delim, char* aCommands,int* nCommands){
  char* pStr_  = strtok(record,delim);
  char  field_ = 0;

  while(*pStr_)
  {
    strcpy(&aCommands[field_],pStr_);
    field_++;
    pStr_ = strtok('\0',delim);
  }		
  *nCommands = field_;
}

void Input::parseLhsRhs(char* record, char* delim, char** aCommands,int* nCommands){
  for (char i=0 ; i<(*nCommands) ; i++){
    char* pCmd_  = strtok(record,delim);
    strcpy(&aCommands[i][0],pCmd_);  // Save LHS to 1st column
    pCmd_ = strtok('\0',delim);
    strcpy(&aCommands[i][1],pCmd_);  // Save RHS to 2nd column
    record++;  // Advance to next command
  }
}

void Input::sortOut(){
  char  cData_[] = "s1=10;s2=1;p=100";  // readdataIn
  int nCommands_=0;
  char aCommands_[MAXCOMMANDS][MAXCOMMANDSIZE]={
    0x0                };
  char aCommands2_[MAXCOMMANDS][MAXCOMMANDS][MAXCOMMANDSIZE]={
    0x0                };

  parse(cData_,breaker_,&aCommands_[0][MAXCOMMANDSIZE],&nCommands_);
  parseLhsRhs(&aCommands_[0][MAXCOMMANDSIZE],breaker_,&aCommands2_[0][0][MAXCOMMANDSIZE],&nCommands_);
}

The error:

INPUT.cpp: In member function ‘void Input::sortOut()’:
INPUT.cpp:43: error: no matching function for call to ‘Input::parseLhsRhs(char*, char [0], char*, int*)’
INPUT.cpp:25: note: candidates are: void Input::parseLhsRhs(char*, char*, char**, int*)

Thanks in advance!

The compiler is telling you exactly what the problem is. There is a function, Input::parseRhsLhs, with a specific set of arguments defined. You are not calling it with arguments of that type.

  while(*pStr_)

is wrong. Get rid of the * in that statement.

    strcpy(&aCommands[field_],pStr_);

Randomly stuffing & in to get the compiler to quit complaining is not the thing to do. aCommands is defined to be a pointer to a block of characters, not a pointer to an array of blocks of characters. You are trying to treat it as an array of arrays, when it is not that.

You can't copy an array of characters into a single character, which is what aCommands[field_] is.

  parse(cData_,breaker_,&aCommands_[0][MAXCOMMANDSIZE],&nCommands_);

If you are going to call parse with a 2D array, why is it not defined to take a 2D array?

Thanks for the help,

I'm really struggling to get my head round how to reference an array when it comes to strings.

As I understand, to have an array of strings I'm assuming I need to define a 2D array containing the size of the string + 1 (to take the null end of the string), and the other dimension that defines the number of strings: char arrayOfStrings[nStrings][sizeString];

However, doing this, if I want to copy a string to this I need strcpy(destination,original), how do I reference the destination since the size of the string wouldnt normally be defined in strcpy? strcpy(str2,str1) would be ok. in my case str2 is a 2D array so needs to be referenced as such: strcpy(arrayOfStrings[0][],"hello") which throws up an error.

Also, how do I pass arrayOfStrings into the function?

Argh, I'm mega confused!

A 2D array is an array of arrays. To copy to a specific element in the array, you really are not concerned about the 2nd dimension of the array.

char someStuff[10][64]; // An array of 10 strings of length 64
strcpy(someStuff[4], "here is some text); // put some text in string 4

in my case str2 is a 2D array so needs to be referenced as such: strcpy(arrayOfStrings[0][],"hello") which throws up an error.

As well it should. See above.

Also, how do I pass arrayOfStrings into the function?

Two possibilities.

void fun1(char arr[MAXCOMMANDS][]) {}
void fun2(char **arr) {}

Cheers Paul,

I'll give it a go in the morning, nearly midnight here now, I'm sure I tried it but then again maybe not.

Waitout for the results, I will post if/when it works.

Thanks very much!

I’ve tried what you mentioned, but I still stand with an error:

INPUT.cpp: In member function ‘void Input::sortOut()’:
INPUT.cpp:19: error: expected primary-expression before ‘]’ token

Code:

void Input::sortOut(){
  char  cData_[] = "s1=10;s2=1;p=100";  // readdataIn
  int nCommands_=0;
  char aCommands_[MAXCOMMANDS][MAXCOMMANDSIZE]={
    0x0  };
  char aCommands2_[MAXCOMMANDS][MAXCOMMANDS][MAXCOMMANDSIZE]={
    0x0  };

  parse(cData_,breaker_,aCommands_[0][],&nCommands_);
  //parseLhsRhs(&aCommands_[0][MAXCOMMANDSIZE],breaker_,&aCommands2_[0][0][MAXCOMMANDSIZE],&nCommands_);
}

void Input::parse(char* record, char* delim, char** aCommands,int* nCommands){
  char* pStr_  = strtok(record,delim);
  char  field_ = 0;

  while(pStr_)
  {
    strcpy(aCommands[field_],pStr_);
    field_++;
    pStr_ = strtok('\0',delim);
  }		
  *nCommands = field_;
}

void Input::parseLhsRhs(char* record, char* delim, char** aCommands,int* nCommands){
  for (char i=0 ; i<(*nCommands) ; i++){
    char* pCmd_  = strtok(record,delim);
    strcpy(&aCommands[i][0],pCmd_);  // Save LHS to 1st column
    pCmd_ = strtok('\0',delim);
    strcpy(&aCommands[i][1],pCmd_);  // Save RHS to 2nd column
    record++;  // Advance to next command
  }
}

Thanks

Please define what it is you want to send to the parse() function in sortOut(), in English, not in terms of the data known to the function.

cData_ which is the local to sortOut() string of data (will eventually be replaced with a read from the serial port)

breaker_ which is the delimiter, local to the class, defined by a setter in another bit of code. it is simply ";"

aCommands_ which is the array of pointers to tokens of cData_, local to the sortOut() function, hence why it needs to be passed in. (is this right?)

nCommands_ which is the number of commands in cData_, determined by parse() but local to sortOut()

aCommands_ which is the array of pointers to tokens of cData_

aCommands_ is NOT an array of pointers. It is a 2D array of characters.

However, the difference is pretty minor.

Pay close attention, though, to what you wrote. aCommands_ is..., not aCommands_[0] is... or aCommands_[0][].

The function expects a 2D array, or an array of pointers, or a pointer to some pointers to some data. aCommands_ fits this description. aCommands_[0][] does not.

Ok cheers. In my head, aCommands_ needs to be a list of the separated parts of the original string, cData_

To that end, does each entry in aCommands_ need to be the length of the string, or can it be an array of single bytes, each of which is the token produced by strtok()?

Meaning:

 char cData_[lengthOriginalString];  // =>  "s1=10;s2=1;p=100"
 char aCommands_[nCommands][1];  // where [0][0] is a pointer to the token containing "s1=10", [1][0] is a pointer to the token containing "s2=1" etc??

Therefore, meaning I have a fundamental problem with the code?

Therefore, meaning I have a fundamental problem with the code?

There is either a fundamental flaw in your code, in your understanding of arrays and pointers, or in your ability to use the correct terms to describe that you are trying to.

where [ 0 ][ 0 ] is a pointer to the token containing "s1=10",

The element at [ 0 ][ 0 ] is a character, not a pointer to a string. You could have an array of pointers, which really makes more sense in light of what you are trying to do.

char *aCommands_[nCommands];

Then, when strtok() gives you a pointer, COPY the data pointed to be that pointer to another pointer (strdup()) and store that pointer in the aCommands_ array.

char *pToken = strtok(/* some stuff */);
aCommands_[n] = strdup(pToken);

You can't just assign the value of pToken to aCommands_[n] because pToken is only valid between two calls to strtok(). You can copy what is pointed to, which is what strdup() does, while pToken is valid.

Don't forget to free all the memory allocated by strdup() when you are done with it.

Thanks very much paul, that has helped a lot. I have redefined it all as pointers now (saved a lot of memory!).

I am getting one error now, on my second parse function, to do with the passing of the external array into the function. I can’t figure out what I am doing wrong. Ive gone through with a toothcomb to the best of my limited knowledge but cant see the typo. Any ideas?

void Input::sortOut(){
  char  cData_[]   = "s1=10;s2=1;p=100";  // readdataIn
  int   nCommands_ = 0;
  char aCommands_[MAXCOMMANDS]={
    0x0  };
  char aCommands2_[MAXCOMMANDS][2]={
    0x0  };

  parse2(cData_,breaker_,aCommands_,&nCommands_);
  parseLhsRhs2(aCommands_,breaker_,aCommands2_,&nCommands_);
}

void Input::parse2(char* record, char* delim, char* aCommands,int* nCommands){
  char* pStr_  = strtok(record,delim);
  char  field_ = 0;

  while(pStr_)
  {
    aCommands[field_] = *strdup(pStr_);
    field_++;
    pStr_ = strtok('\0',delim);
  }		
  *nCommands = field_;
}

void Input::parseLhsRhs2(char* record, char* delim, char** aCommands,int* nCommands){
  for (char i=0 ; i<(*nCommands) ; i++){
    char* pCmd_  = strtok(record,delim);
    aCommands[i][0] = *strdup(pCmd_);
    pCmd_ = strtok('\0',delim);
    aCommands[i][1] = *strdup(pCmd_);
    record++;  // Advance to next command
  }
}

Error:

INPUT.cpp: In member function ‘void Input::sortOut()’:
INPUT.cpp:20: error: no matching function for call to ‘Input::parseLhsRhs2(char [11], char [0], char [11][2], int*)’
/INPUT.h:24: note: candidates are: void Input::parseLhsRhs2(char*, char*, char**, int*)

Thanks very much paul, that has helped a lot.

Apparently not enough.

    aCommands[field_] = *strdup(pStr_);

Why did you put that * in front of strdup? We must get that squared away before fixing anything else.

"Because it wouldn't compile otherwise" is not the correct answer.

Damn, that was going to be my quote!

Without the * in front of strdup() I get this happy message:

INPUT.cpp: In member function 'void Input::sortOut()': INPUT.cpp:20: error: no matching function for call to 'Input::parseLhsRhs2(char [11], char [0], char [11][2], int*)' /INPUT.h:24: note: candidates are: void Input::parseLhsRhs2(char*, char*, char*, int) INPUT.cpp: In member function 'void Input::parse2(char*, char*, char*, int*)': INPUT.cpp:31: error: invalid conversion from 'char*' to 'char' INPUT.cpp: In member function 'void Input::parseLhsRhs2(char*, char*, char*, int)': INPUT.cpp:41: error: invalid conversion from 'char*' to 'char' INPUT.cpp:43: error: invalid conversion from 'char*' to 'char'

Im confused since I thought strtok() and strdup() returned pointers, so why cant I assign them to char* pStr_

Im confused since I thought strtok() and strdup() returned pointers

They do.

so why cant I assign them to char* pStr_

You can.

This is wrong, though.

    aCommands[field_] = *strdup(pStr_);

aCommands[field_] is a char. You can not assign a char pointer, which is what strdup() returns, to a char.

Your declaration for aCommands in the function implementation is wrong. It should be char **aCommands, not char *aCommands.

I have now got rid of the bodges to make it compile, added a pointer to the arrays.

Still having trouble getting the 2D array to work though:

INPUT.cpp: In member function ‘void Input::sortOut()’:
INPUT.cpp:20: error: cannot convert ‘char ()[2]’ to ‘char**’ in initialization
INPUT.cpp: In member function 'void Input::parseLhsRhs2(char
, char*, char**, int*)’:
INPUT.cpp:44: error: invalid conversion from ‘char*’ to ‘char’
INPUT.cpp:46: error: invalid conversion from ‘char*’ to ‘char’

void Input::sortOut(){
  char  cData_[]   = "s1=10;s2=1;p=100";  // readdataIn
  char* pData_ = (char*)cData_;
  int   nCommands_ = 0;
  char aCommands_[MAXCOMMANDS]={
    0x0  };
  char aCommands2_[MAXCOMMANDS][2]={
    0x0  };
  char* pCommands_ = aCommands_;
  char** pCommands2_ = aCommands2_;

  parse2(pData_,breaker_,pCommands_,&nCommands_);
  parseLhsRhs2(aCommands_,breaker_,pCommands2_,&nCommands_);
}

void Input::parse2(char* record, char* delim, char* aCommands,int* nCommands){
  char* pStr_  = strtok(record,delim);
  char  field_ = 0;

  while(pStr_)
  {
    aCommands = strdup(pStr_);
    field_++;
    pStr_ = strtok('\0',delim);
  }		
  *nCommands = field_;
}

void Input::parseLhsRhs2(char* record, char* delim, char** aCommands,int* nCommands){
  for (char i=0 ; i<(*nCommands) ; i++){
    char* pCmd_ = strtok(record,delim);
    aCommands[i][0] = strdup(pCmd_);
    pCmd_ = strtok('\0',delim);
    aCommands[i][1] = strdup(pCmd_);
    record++;  // Advance to next command
  }
}

In parseRhSLhs2(), aCommands[n][n] is assigned a pointer. That means that aCommands needs to be a char *** object, not a char ** object.

This means that you need to pass an array of pointers to parseRhsLhs2(). So, in sortOut(), char *aCommands_[MAXCOMMANDS}; char *aCommands2_[MAXCOMMANDS][2];

Get rid of the pCommands_ and pCommands2_ variables. They are not needed.

In Input::parse2(), the third argument should be of type char **, not char *, and:

    aCommands[filed_] = strdup(pStr_);

In Input::parseLhsRhs2(), the third argument should be of type char **, not char *.

It really didnt like that lol

INPUT.cpp: In member function ‘void Input::sortOut()’:
INPUT.cpp:20: error: no matching function for call to ‘Input::parse2(char [17], char [0], char [11], int*)’
/INPUT.h:22: note: candidates are: void Input::parse2(char*, char*, char**, int*)
INPUT.cpp:21: error: no matching function for call to ‘Input::parseLhsRhs2(char [11], char [0], char [11][2], int*)’
/INPUT.h:24: note: candidates are: void Input::parseLhsRhs2(char*, char*, char***, int*)

void Input::sortOut(){
  char  cData_[]   = "s1=10;s2=1;p=100";  // readdataIn
  int   nCommands_ = 0;
  char aCommands_[MAXCOMMANDS]={
    0x0  };
  char aCommands2_[MAXCOMMANDS][2]={
    0x0  };

  parse2(cData_,breaker_,aCommands_,&nCommands_);
  parseLhsRhs2(aCommands_,breaker_,aCommands2_,&nCommands_);
}

void Input::parse2(char* record, char* delim, char** aCommands,int* nCommands){
  char* pStr_  = strtok(record,delim);
  char  field_ = 0;

  while(pStr_)
  {
    aCommands[field_] = strdup(pStr_);
    field_++;
    pStr_ = strtok('\0',delim);
  }		
  *nCommands = field_;
}

void Input::parseLhsRhs2(char* record, char* delim, char*** aCommands,int* nCommands){
  for (char i=0 ; i<(*nCommands) ; i++){
    char* pCmd_ = strtok(record,delim);
    aCommands[i][0] = strdup(pCmd_);
    pCmd_ = strtok('\0',delim);
    aCommands[i][1] = strdup(pCmd_);
    record++;  // Advance to next command
  }
}

It really didnt like that lol

I'm getting tired of trying to tell you that the variable you are trying to pass to a function, the call to that function, the function definition, AND the function code ALL need to match.

You can't just change the function definition and expect it to work.

You changed the definition of parse2, but not the type of the argument you are trying to pass to it. The compiler told you that. That's what the first message is saying.

You changed the definition of parseLhsRhs2, but not the type of the argument you are trying to pass to it. The compiler told you that. That's what the second message is saying.

Ok ok, I’m still learning.

The definitions are:

char  aCommands_[MAXCOMMANDS]
char  aCommands_[MAXCOMMANDS][2]

Which are arrays, which by definition in cpp are pointers.

The functions are:

void Input::parse2(char* record, char* delim, char** aCommands,int* nCommands)
void Input::parseLhsRhs2(char* record, char* delim, char*** aCommands,int* nCommands)

Which is what you said it needs to be

In Input::parse2(), the third argument should be of type char **, not char *, and:
In Input::parseLhsRhs2(), the third argument should be of type char ***, not char **.

The function commands assign pointers to:

aCommands[field_]
aCommands[i][0]

Which is the same format as the original definitions.

The function calls are:

parse2(cData_,breaker_,aCommands_,&nCommands_);
  parseLhsRhs2(aCommands_,breaker_,aCommands2_,&nCommands_);

As I am passing the whole array into the function.

I really don’t see where the inconsistency is?