c++ passing array through function [duplicate] - c++
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Sizeof an array in the C programming language?
I've been fiddling with C to become better acquainted with it and think I may have stumbled upon a initialization/pointer issue that I'm unsure of how to resolve. The below program is an implementation of ROT13, so it takes an input string, and shifts each letter by 13, resulting in the cipher text. The output of my program displays the correct shift, but it won't work for more than 4 characters, making me wonder if sizeof is being used incorrectly. Any other suggestions are appreciated, I'm sure I've messed a few things up at this point.
#include <stdio.h>
#include <string.h>
void encrypt(char *);
int main(void){
char input[] = "fascs";
encrypt(input);
return 0;
}
void encrypt(char *input){
char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
printf("Input: %s \n", input);
int inputCount = sizeof(input);
printf("Characters in Input: %i \n\n", inputCount);
//holds encrypted text
char encryptedOutput[inputCount];
//Initialize counters
int i, j = 0;
// loop through alphabet array, if input=current letter, shift 13 mod(26),
// push result to output array, encryptedOutput
for(i = 0; i < inputCount; i++){
for(j = 0; j < 26; j++){
if(input[i] == alphabet[j]){
encryptedOutput[i] = alphabet[(j + 13) % 26];
}
}
}
//Nul Termination for printing purposes
encryptedOutput[i] = '\0';
printf("Rot 13: %s \n\n", encryptedOutput);
}
sizeof() in encrypt will not behave as you want it to. Inside encrypt, the sizeof(char *) is 4(on a 32bit machine) or 8(on a 64 bit machine), which you can see is the size of a pointer.
To get the sizeof(input) you must change sizeof to strlen. Hence solution = strlen(input)
Why this happens?? when you pass an array into a function, that array is internally represented as a pointer. At the called-function's end input is just a pointer, which gives either 4 or 8 bytesize depending upon your machine.
To get the sizeof of input, just use a macro like this:
#define SIZEOF(x) (sizeof(x)/sizeof(x[0]))
and use this in the function that defines x. In your program, x is input in main()
sizeof returns the size of the type of its argument. It cannot determine how many characters are in a pointer to a character array.
You should consider using the strlen function if you know that your string is null-terminated.
input has type char* (read as "pointer to char"). sizeof(input) gives you the size of the pointer. You probably want to use strlen to find the length of the string, or pass the length in to the function as an additional argument.
This line causes your problem.
int inputCount = sizeof(input);
sizeof only determines the size of the variable in this case char *. And every pointer has the size of 4 bytes on a 32 bit system.
You can't determine the size of an array during runtime. You could either
* pass the size of the input as an parameter
* because in your case it is a string, use the strlen in string.h to get the length of the string if the string is terminated by \0.
But in both cases you can't simply allocate the output buffer using
char output[variable_containing_size];
You would need to use malloc() to dynamically allocate memory during runtime or even easier pass the output parameter as parameter to your function.
#include <stdio.h>
#include <string.h>
#define BUFFER_LENGTH 80
void encrypt(const char * input, char *output);
int main(void){
char input[BUFFER_LENGTH] = "fascs";
char output[BUFFER_LENGTH] = {0}; // initialize every field with \0
encrypt(input, output);
return 0;
}
void encrypt(const char *input, char *output){
char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
printf("Input: %s \n", input);
int inputCount = strlen(input);
printf("Characters in Input: %i \n\n", inputCount);
//Initialize counters
int i, j = 0;
// loop through alphabet array, if input=current letter, shift 13 mod(26),
// push result to output array, output
for(i = 0; i < inputCount; i++){
for(j = 0; j < 26; j++){
if(input[i] == alphabet[j]){
output[i] = alphabet[(j + 13) % 26];
}
}
}
//Nul Termination for printing purposes
output[i] = '\0';
printf("Rot 13: %s \n\n", output);
}
But in this case the encrypt() function does no size checks at all, and if you're not careful this could easily lead to buffer overflows.
Related
Arduino scrolling text program freeze after some time
I use a 16x2 character LCD to display some text. What I want is first line is fixed and second line is scrolling. I wrote a program which works fine but the problem is after some time Arduino does not respond. I suspect there might be a bug or memory leak in the code. The relevant code is like this. void scrollTextFromRight(int line, char text[]) { const char space[16] = " "; char screen[16]; char * longText; longText = malloc(sizeof(char) * (sizeof(text) + 17)); memset(longText, '\0', sizeof(char) * (sizeof(text) + 17)); memset(screen, '\0', sizeof(screen)); for (int i = 0; i < 16; ++i) { longText[i] = space[i]; } for (int j = 0; j < sizeof(text) + 17; ++j) { longText[16+j] = text[j]; } for (int i = 0; i < sizeof(text) + 17; ++i) { lcd.setCursor(0, line); strncpy(screen, longText + i, 17 ); lcd.print(screen); delay(350); } } I call this function from main program like this: scrollTextFromRight(1, "Scrolling text"); Update 1 : After reading the comments and answers I freed allocated memory space with free function. I uploaded the new code and testing whether it works as expected. I added this part after the third for loop. free longText; Update 2 : After reading the comments I decided to use Arduino's String class. The code became like this: void scrollTextFromRight(int line, String text) { const String space = " "; const String longText = space + text + ' '; int displaySize = 16; for (int i = 0; i <= longText.length(); ++i) { lcd.setCursor(0, line); String display = longText.substring(i, i + displaySize); lcd.print(display); delay(350); } }
When you declare the argument as char text[], the compiler translates it as char* text. That is, it's a pointer. And getting the size of a pointer (e.g. sizeof(text)) gives you the size of the pointer and not whatever it points to. If it's a null-terminated byte string, then use strlen to get the length (but note that the null-terminator not counted). Or even better, stop using C strings and functions, because Arduino is actually programmed in C++ and have its own standard String class that should be used for all strings. Also note that const char space[16] = " "; creates an array of 16 elements, and set all those elements to the space character ' '. But it's not a null-terminated string, because the terminator doesn't fit in the array. You also know about the memset function, but seems to have forgotten the memcpy function when you copy from your arrays. Instead of the explicit loop copying from space, you could simply do memcpy(longText, space, sizeof space); // Using sizeof since space is not null-terminated Lastly, be careful with the strncpy function, it might not null-terminate the destination string.
Generate random char/digit
I`m trying to found fastest way to generate random digit/char array. char *randomGet(int num) { srand(time(NULL)); const char ab[37] = { "0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ" };//Alphabet&Digit char *targ = new char[num]; for (int i = 0; i < num; i++) { strcat(targ, ab[rand() % 38]); } return targ; } So far I've come up with this, but it does not work (argument of type char is incompatible with parameter of type const char *). Help me find the best solution to my problem. Ty.
strcat() takes a char* as input, but you are giving it a single char instead, thus the compiler error. Also, the buffer that strcat() writes to must be null terminated, but your targ buffer is not null terminated initially, and you are not allocating enough space for a final null terminator anyway. You don't need to use strcat() at all. Since you are looping anyway, just use the loop counter as the index where to write in the buffer: Also, you are using the wrong integer value when modulo the return value of rand(). You are producing a random index that may go out of bounds of your ab[] array. Try this instead: char *randomGet(int num) { srand(time(NULL)); static const char ab[] = "0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ"; //Alphabet&Digit char *targ = new char[num+1]; for (int i = 0; i < num; ++i) { targ[i] = ab[rand() % 36]; } targ[num] = '\0'; return targ; }
I'd make two changes. First, make the internal source array static: static const char ab[] = "0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ"; Note that this version does not specify the array size; the compiler will figure it out from the initializer. Second, pass in a pointer to the target array: void randomGet(char* targ, int num) { static const char ab[] = "0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ"; for (int i = 0; i < num - 1; ++i) targ[i] = ab[rand() % (sizeof ab - 1)]; targ[num - 1] = '\0'; } This way, the caller decides how to allocate memory for the string.
Filling and comparing char* inside a function
I wrote the function readChar() which is designed to read the characters send by my WiFi module one by one(function works has advertised) and pass them to a char buffer of increasing size. The function should stop when char *endChar (multiple characters) have been detected or the number of character returned by timedRead() has exceeded size_t length. I have several issues: 1/. I don't understand the syntax (found inside the Arduino Stream library) : *buffer++ = (char)c; Can you explain how the array buffer gets filled? And why buffer[index] = (char)c; doesn't work here? 2/. I would like to compare buffer and endChar in the loop, possibly by using strcmp(buffer,endChar) (maybe there is a better way). But that doesn't seem to work. In fact when printing the ASCII values of my char *buffer then seem to increment from the end of the buffer. E.G.: So what is the best way to do that comparison? The code, inserted in the loop: _dbgSerial->println("buffer"); for (int i = 0; i < 32; i++){ _dbgSerial->print(buffer[i], DEC); _dbgSerial->print(","); } _dbgSerial->println(""); prints: buffer 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0,0,0, Here is the function readChar(): size_t Debugwifi::readChar(char *endChar, char *buffer, size_t length) { if (length < 1) return 0; size_t index = 0; while (index < length) { int c = timedRead(); if (c < 0 ) break; //buffer[index] = (char)c; *buffer++ = (char)c; _dbgSerial->println("buffer"); for (int i = 0; i < 32; i++){ _dbgSerial->print(buffer[i], DEC); _dbgSerial->print(","); } _dbgSerial->println(""); if (strcmp(buffer,endChar)==0) { break; _dbgSerial->println("brk");} index++; } return index; }
As Rickard has explained, *buffer++ = (char)c; is how you assign a character to the memory a pointer points at, and then increment the pointer. However, your function has a lot of problems - you keep comparing unset memory with *endChar. I suggest: size_t Debugwifi::readChar(const char * const endStr, // const pointer to const. char * const buffer, const size_t length) { if (length < 1) return 0; const size_t endLen = strlen(endStr); for (size_t index = 0; index < length; index++) { const int c = timedRead(); if (c < 0 ) break; buffer[index] = (char)c; // Debug _dbgSerial->println("buffer"); for (size_t i = 0; i < length; i++){ // Better to use size_t here, // and compare against length not 32 _dbgSerial->print(buffer[i], DEC); _dbgSerial->print(","); } _dbgSerial->println(""); // Finished? if (index >= endLen) { if (memcmp(&buffer[index-endLen], endStr, endLen)==0) { _dbgSerial->println("brk"); // Must do this *before* "break"! break; } } } return index; } I have added a lot of consts. It's hard to have too many. The important point is that once you have read enough characters, to start comparing the last characters you have read to the end marker. Note that this function does not remove the end marker, and if you pass a 32-byte zero-filled array and it reads 32 characters, the result will NOT be zero terminated. Finally, I changed the argument name to endStr because I had expected endChar to be a pointer to a single character - not a NUL-terminated string.
*buffer++ = (char) c; First writes the value of c to what buffer is currently pointing to. Then it increments the buffer This is also why your loop to print buffer doesn't work. You start printing from the position after what was just filled. This is also why your strcmp doesn't work. It doesn't actually compare what you have filled your buffer with. It compares the content beyond what have been filled. If you want your printing code to work you should save the initial value of buffer before the loop; const char *buffer_start = buffer; Then use that in your printing code instead of buffer.
After padding a string with zeroes - it prints unspecified characters? (C++)
Basically, here, I'm trying to reverse an array, and convert the reversed int array into a string (I'm trying to write the equivalent of Java's BigInteger class in C++ - basically turning the input into big endian ordering, breaking down the operations, reversing the result back to little endian format, and returning the string). And as you can see below, it outputs some strange characters (I think it's an out of range reference?) - but I'm not entirely sure what caused this output? I would really appreciate if you could take a look at it: Sample input int a[] = {1, 2, 3}; int rA[3]; reverseIntArray(a, rA, 3); string aString = intArrayToString(a, 3); cout << aString << endl; Console output 123\216\377 As you can see - it calculates the answer correctly, with the exception of the \277_\377. I'll post the rest of the relevant functions: reverseIntArray void reverseIntArray(int array[], int reversedArray[], int arrayLength) { for (int i = 0; i < arrayLength; i++) { reversedArray[i] = array[arrayLength - 1 - i]; } } intArrayToString string intArrayToString(int digits[], int length) { // convert int array to char array char digitsChar[length]; for (int i = 0; i < length; i++) { digitsChar[i] = '0' + digits[i]; } // convert char array to string string intString(digitsChar); return intString; } I'm quite sure this is a subtle issue to do with pointers, but I'm still relatively new to C++ (migrating from Java) and I've stared at this for hours but haven't come up with any ideas.
The std::string constructor you are using is assuming that the string you pass is properly terminated, which it isn't and that leads to undefined behavior as the std::string constructor goes beyond the end of the digitsChar array. Three possible solutions: Make room for another character in the digitsChar array and terminate it: char digitsChar[size + 1]; for (...) { ... } digitsChar[3] = '\0'; string intString(digitsChar); Use another constructor where you pass the length of the character array: string intString(digitsChar, length); Append the characters directly to the string: string intString; for (int i = 0; i < length; i++) { intString += '0' + digits[i]; } There are of course other solutions as well, like for example using std::ostringstream.
Stack around the variable 'uChar' was corrupted
Here is the code which converts a hex string to byte array, it works fine but when the loop goes end and the complier reaches to the end of function it throws this error: "Stack around the variable 'uChar' was corrupted" void Cfsp::stringToHex(unsigned char hexArray[], LPCTSTR string) { int stringLength=strlen(string); int j=0; unsigned char uChar = 0; for (int x = 0; x < stringLength; x+=2) { sscanf_s(&string[x], "%02x", &uChar); hexArray[j] = uChar; j++; } } Here is where I initiate the array and call the function. unsigned char Key[16]; stringToHex( Key,"2f145a8b11d33217"); I know when stringToHex would convert the given string (16 chars length) to byte array it only fills 8 Bytes(as char). I just wanted to make a reserved area in the buffer.
This is why the xxx_s functions are not safe :-) People can misuse these safe functions as easily as the so-called "unsafe" ones. sscanf with the %x format specifier wants an int pointer, not a char one. It will write the entire (for example) 32 bit value starting at uChar and not caring one bit what it overwrites in the process. It's only because you have stack protection enabled that the code catches this. You should be using something like: void Cfsp::stringToHex (unsigned char hexArray[], LPCTSTR string) { int stringLength = strlen (string); int j = 0; unsigned int uChar = 0; // <-- INT rather than char. for (int x = 0; x < stringLength; x+=2) { sscanf_s (&string[x], "%02x", &uChar); hexArray[j] = uChar; j++; } }
The reason you get corruption is because you send sscanf_s an unsigned char * where it expects int *
I don't know how sscanf_s works, but from the sscanf manpage: x Matches an unsigned hexadecimal integer; the next pointer must be a pointer to unsigned int.
The error is probably caused by "%02x" which requires an int as storage, not an unsigned char. The issue could also be that you called strlen(LPCTSTR). LPCTSTR might or might not have a terminating zero, if it doesn't strlen invokes undefined behaviour. Use _tcslen(LPCTSTR) instead.