I want to shift each string in an array of strings one element to the left:
char historyArray[HISTORY_DEPTH][COMMAND_LENGTH];
Neither of my two attempts below work. Can someone explain to me what I'm doing wrong?
for (int i = 1; i < HISTORY_DEPTH; i++) {
strcpy(historyArray[i-1], historyArray[i]);
}
for (int i = 1; i < HISTORY_DEPTH; i++) {
historyArray[i-1] = historyArray[i];
}
define historyArray as char *historyArray[HISTORY_DEPTH]; This defines historyArray as an array of character string pointers. Then historyArray[0] points to teststring as a result of the assignment. As an array of pointers, you can handle each string pointer properly. You can then malloc a buffer pointer to use as an element in the array. and use strcpy to copy into that buffer.
char *historyArray[HISTORY_DEPTH];
// put initialization code here
for (int i = 1; i < HISTORY_DEPTH; i++) {
historyArray[i-1] = historyArray[i];
}
historyArray[HISTORY_DEPTH-1] = NULL; //empty the last element pointer
This now moves the pointers into the previous element of the array.
Note that the original contents of historyArray[0] are now lost which would cause a memory leak if you had used malloc to create it. As a result, it should have had a free() applied to it. If it was a fixed buffer and does not need to be freed then you would not have to worry about it.
char historyArray[HISTORY_DEPTH][MAX_SIZE];
for (int i = 1; i < HISTORY_DEPTH; i++) {
// Or use the memset with strlen(size+1)
// to ensure that the ending '\0' is also copied
strcpy(historyArray[i-1], historyArray[i]);
}
historyArray[HISTORY_DEPTH-1][0] = '\0'; // make the last an empty string
The strcpy of the second, will copy the contents of each string pointed to by historyArray into the buffer pointed to by the previous element without moving the pointers themselves. This assumes that each buffer is large enough to hold the character string. The last pointer continues to also hold the same data as it did before unless you put in an empty string.
Are you saying that if you have a string like
aaa, bbb, ccc
You want
aaa, ccc, ccc
as the result? Because your index starts at 1, which I suspect is not your intention. If it is the case, this can get you bbb, ccc, ccc using this
for (int i = 0; i < HISTORY_DEPTH-1; i++) {
strcpy(historyArray[i], historyArray[i+1]);
}
Related
Hi I am quite new to C and I have a question about the behavior of array initialization using [] and malloc.
int main() {
int* pointer;
for(int i = 0; i < 100; i++) {
// Init the Array
int tmp[2] = {};
// Do some operation here...
tmp[0] = 0;
tmp[1] = i;
// If the value is 1, copy that array pointer
if(i == 1) {
pointer = tmp;
}
}
// expected 1 here, but got 99
printf("%d\n", pointer[1]);
return 0;
}
Why is the output 99? I thought the array is re-inited every loop, but it turns out using the same memory address. And if I use malloc to init the array instead, the result becomes 1 as expected.
Is there any way I could get result 1 without using malloc?
Your code is invalid as you access the variable which is out of the scope using the reference. It is undefined behaviour.
Every time you assign the i to the same element to the array. Pointer only references (points to) the first element of this array. So if you change the underlaying object the value you get using the reference will change as well. If your finger is pointing to the box of 5 apples and someone eats 2 apples, your finger will point to the box of 3 apples, not 5.
You need to make a copy of the object.
if(i == 1) {
pointer = malloc(sizeof(tmp));
memcpy(pointer, tmp, sizeof(tmp));
}
or break the loop (declaring it static or moving the tmp out of the for loop scope)
for(int i = 0; i < 100; i++) {
// Init the Array
static int tmp[2];
// Do some operation here...
tmp[0] = 0;
tmp[1] = i;
// If the value is 1, copy that array pointer
if(i == 1) {
pointer = tmp;
break;
}
}
The scope of the array tmp is the block scope of the for loop
for(int i = 0; i < 100; i++) {
// Init the Array
int tmp[2] = {};
// Do some operation here...
tmp[0] = 0;
tmp[1] = i;
// If the value is 1, copy that array pointer
if(i == 1) {
pointer = tmp;
}
}
That is in each iteration of the loop a new array tmp is created and ceases to be alive after exiting the block.
Thus the pointer pointer is invalid after the for loop. Dereferencing the pointer after the for loop invokes undefined behavior.
You have gotten the result 99 only because the array tmp was not being reallocated and the memory occupied by the array was not yet overwritten. So the last value stored in this extent of memory that is the value of i equal to 99 was outputted.
Even if you will declare the array tmp before the for loop then using the pointer pointer you will get as the output the value 99 that is the value last stored in the array.
You could write for example
int tmp[2] = { 0 };
int *pointer = tmp;
for(int i = 0; i < 100; i++) {
// Do some operation here...
tmp[0] = 0;
tmp[1] = i;
}
And the last value stored in the array (when i is equal to 99)
tmp[1] = i;
will be outputted in this call
printf("%d\n", pointer[1]);
Pay attention to that such an initialization with empty braces is invalid in C opposite to C++
int tmp[2] = {};
You need to write at least like
int tmp[2] = { 0 };
As we know pointer stores a memory address.
Here, I think when you give the command: pointer = tmp;,
the address of the array stored in 'tmp' is copied to the 'pointer'.
But when the loop of i = 1 gets completed, the array that you created in that particular loop and the pointer 'tmp' gets forgotten.
Then the loop for i=2 starts, 'tmp' and the array gets created again.
It happens again till the loop end.
I think that the program is storing tmp[1] at the same location every time due to which the data stored at that changes again and again.
So, when you give the command printf("%d\n", pointer[1]);, the data at that address get printed which is no longer equal to 1, it has changed.
The mistake is that we shared the address of 'tmp' with the 'pointer'.
But when we use malloc, we lock that memory means other programs can't use that memory. ( That's why we always need to free that memory to avoid memory leaks ).
It's the reason while using malloc you get output as 1 as your other commands can't touch that particular memory.
Solution:
If you want to solve the problem without malloc.
Initialise 'pointer' as an array to store data of 'tmp'.
use this code,
pointer[0] = tmp[0]; pointer[1] = tmp[1];
at place of
pointer = tmp;.
Now, you will not be copying addresses to 'pointer' but the data in the 'tmp'.
And if you have a big array with many values in it, just use it for loop.
solution image
Also, you will get the same problem if you do it like this, all because of copying only the address, you will be doing the same thing.
Maybe you can relate,same problem image
Thanks.
I'm working on a minisql code in C and i having some issues to allocate array of strings. I made a function called "alocaString" to do this (bc i'm using that a lot), but i don't think is working.
When the code reaches the line "strncpy(lista[qtnPalavras], splitStr, 100);" in the function "listaPalavras" (that have the purpose of split a string in different types of characters) a file named "strcpy-avx2.S" is created, one of the arguments of that function (**lista) is allocated with "alocaString" so i think the problem is in that function.
I already try to use valgrind and shows "is used uninitialized in this function [-Werror=uninitialized]" to all arrays of strings that i tried to use on that function, but i'm initializing them inside of the function
int alocaString (char **string, int tamanho, int posicoes){
string = malloc (posicoes * sizeof(char*));
for (int i = 0; i < posicoes; i++){
string [i] = malloc (tamanho * sizeof(char));
if (string[i] == NULL){return 0;}
}
return **string;
}
void desalocaString (char **string, int posicoes){
for (int i = 0; i < (posicoes); i++){
free (string[i]);
}
free (string);
}
int listaPalavras(char *entrada, char **lista, char *separador){ // lista as palavras
char *splitStr;
int qtnPalavras = 0;
splitStr = strtok(entrada, separador);
while (splitStr != NULL){
strncpy(lista[qtnPalavras], splitStr, 100);
qtnPalavras++;
splitStr = strtok(NULL, separador);
}
return qtnPalavras;
}
I assume that you are using these functions like this:
alocaString(lista, tamanho, posicoes);
listaPalavras(some_string, lista, some_delimiters);
desalocaString(arr);
Even without looking at the code, it seems logically wrong to allocate an array of strings first and then populate it if you do not already know how many strings it will need to fit. If you happen to allocate an array of n strings, but your listaPalavras() functions splits the provided string into n+1 or more substrings, you're going to overflow your previously allocated array. Nonetheless, this can be done taking the appropriate precautions, like carrying around sizes and checking them to avoid overflow.
The only sane way to achieve what you want is therefore to either (A) count the number of delimiters in the string first to know in advantage how many pointers you will need or (B) dynamically allocate the needed amount in listaPalavras() while splitting. You seem to be going with something similar to option A, but your code is flawed.
The desalocaString() is the only function that seems correct.
A correct implementation of alocaString() would return the allocated array (or NULL in case of failure), but you are returning **string which is just the first character of the first string. Needless to say, this does not make much sense. You don't need to take a char ** parameter, just the sizes. Secondly, in case of failure of any of the calls to malloc() you should free the previously allocated ones before returning NULL.
char **alocaString (unsigned tamanho, unsigned posicoes) {
char **lista = malloc(posicoes * sizeof(char*));
if (lista == NULL)
return NULL;
for (unsigned i = 0; i < posicoes; i++) {
lista[i] = malloc(tamanho * sizeof(char));
if (lista[i] == NULL) {
for (unsigned j = 0; j < i; j++)
free(lista[j]);
free(lista);
return NULL;
}
}
return lista;
}
As per listaPalavras(), which has the job of splitting the given string into other strings and copying them into the previously allocated array, to avoid overflowing the given array of strings you will need to also provide its length as well as the length of the previously allocated strings as argument (let's call them posicoes and tamanho like for the above function). Moreover, strncpy() will not add a NUL-terminator (\0) to the destination string if it is not found in the source string within the first n characters (n being the third argument), so you will need to add it yourself to make sure your strings are correctly terminated.
unsigned listaPalavras(const char *entrada, char *separador, char **lista, unsigned posicoes, unsigned tamanho) {
char *splitStr;
unsigned qtnPalavras = 0;
splitStr = strtok(entrada, separador);
while (qtnPalavras < posicoes && splitStr != NULL){
strncpy(lista[qtnPalavras], splitStr, tamanho);
lista[qtnPalavras][tamanho - 1] = '\0';
qtnPalavras++;
splitStr = strtok(NULL, separador);
}
return qtnPalavras;
}
Finally the code of the caller should look something like this:
char **lista;
unsigned tamanho = 100;
unsigned posicoes = 10;
unsigned palavras;
lista = alocaString(tamanho, posicoes);
if (lista == NULL) {
// handle the error somehow
}
palavras = listaPalavras(YOUR_STRING, YOUR_DELIMITERS, lista, posicoes, tamanho);
desalocaString(lista);
This should work fine, however you are limited by the fact that:
You cannot know beforehand the number of substrings that strtok() will find.
You cannot know beforehand the length of any of those substrings.
Therefore, allocating the needed lista dynamically inside listaPalavras() would make more sense.
Finally, as a side note, the names of your functions are misleading: if you need to allocate an array of strings, you might want to choose a better name than alocaString() which seems to imply that you are allocating a single string. Maybe alocaLista() and dealocaLista() would be better choices.
So I have this code:
char inte[10];
while(j<noinput) {
fscanf(circuit,"%s",inte);
vararray[count]=inte;
count++;
j++;
}
However when I print the contents of the array like this:
for (h=0;h<noinput+2;h++){
printf("%dth variable: %s\n",h,vararray[h]);
}
The elements past the first two (which are reserved for special elements) are all equal to the LAST string that I had taken in from fscanf earlier. I have no idea how one of the strings from fscanf could be equal to multiple slots in the array when I am only setting
vararray[count]=inte;
Shouldn't this mean that each element of the array will be different since I am incrementing count every time? I am so confused. I also tried doing:
fscanf(circuit,"%s",vararray[count]);
But this also did not work and gave me null elements for certain indexes.
you are doing something too wrong. By "vararray[count]=inte;" you are doing pointer assignment so all of your vararray is getting filled by same string. I am guessing you are new to C so I will answer due to that. Correct way would look something like below
Fixed size solution:
char vararray[ROWCOUNT][BUFFERSIZE];
for(count=0; j<noinput; ++count, ++j) {
fscanf(circuit,"%s",(char*)vararray[count]);
}
With dynamic memory management
char * vararray[ROWCOUNT];
for(count=0; j<noinput; ++count, ++j) {
vararray[count] = (char*)malloc(BUFSIZE);
fscanf(circuit,"%s", vararray[count]);
}
I want to warn you in the way of becoming an expert on C nowadays is somewhat madness , i mean unless you have another choice. Examples below I put and the thing you wrote are completely unsafe and unsecure...
You're not copying the string. Here's what's happening:
char *vararray[462]; // declare an array of string pointers
char inte[10]; // declare a char array (to function as a string)
for (int i = 0; i < 462; i += 1) {
// do something
vararray[i] = inte;
}
This is causing all of the items of vararray to point to the memory also referred to as inte... but you're overwriting that each time! Instead, do something like this:
#include <string.h> // write me at the top, outside main()!
char vararray[462][10]; // declare an array of strings (max length 9)
char inte[10]; // declare a char array (to function as a string)
for (int i = 0; i < 462; i += 1) {
fscanf(circuit,"%10s",inte); // use buffer size to make xscanf safer
strncpy(vararray[i], inte, 9); // copy inte, making sure not to buffer overflow!
vararray[i][9] = '\0'; // if inte is too big, a null byte won't be added to the end of the string; make sure that it's there
}
This copies the string! Your problem should go away when you do this.
The problem here is that whenever I change the contents of studName the contents inside studArr change too.
If the input looks like this (AAA,BBB,CCC) I first store AAA inside studName and then store studName into studArr.
I'm trying to make:
studArr[0][1] = "AAA"
studArr[0][2] = "BBB"
studArr[0][3] = "CCC
but when I use this code all of them equal CCC. Is there a way I can fix this?
for (j = 0; j < NumCourses + 1; j++){
i = 0;
k = 0;
while ((c = fgetc(ifp)) != ')'){
if (c == ','){
studName[3] = '\0'; // ends sting with null char
studArr[j][k+1] = studName;
k++;
i = 0;
}
else{
studName[i] = c;
i++;
}
}
studName[3] = '\0'; // ends sting with null char
studArr[j][k+1] = studName; // store studName in studArr
}
with the assignment:
studArr[j][k+1] = studName;
you store a pointer to char[] studName.
You should allocate memory for every instance, like here:
studArr[j][k+1] = strdup(studName);
Note: remember to free allocated memory.
studName is a pointer, and each studArr[j][N] is being set to the same pointer. The contents found by the pointer are being updated, but all the duplicate copies of the same pointer will show the last contents only.
You probably need to use strncpy(), or the like. Specific details really depend on the code you have not yet shown, like declarations.
My guess it's because you assign all pointer to point to studName, and it will always contain the last read string.
You need to duplicate the string instead of just assigning the pointer. Either use strdup (which means you have to free the memory later) or make each entry an array and copy into it.
I want to remove the i-th member of a string array, and bring every member which comes after it one place prior (the i+1-th member to i and so on). I came up with the following code:
for (int j = i; j < arrSize - 1; j++) {
strcpy(members[j],members[j+1]);
}
free(members[arrSize-1]);
But that got me thinking that it might be wrong. For example, if the i-th place member contains the name "John", while the (i+1)-th place members contains the name "Joshua", which means each string is in a different length, would there be any memory leaks or any problem? Thanks in advance!
EDIT: The definiton of members:
members = malloc(maxMembersNum * sizeof(char*));
Rather than copy the contents of the strings, why not move the pointers around? That is:
for (int j = i; j < arrSize - 1; j++) {
char *temp = members[j];
members[j] = members[j+1];
members[j+1] = temp;
}
free(members[arrSize-1]);
As the comments said, the definition of members determines the outcome of the function.
If it is:
char* members[];
then it is an array of pointers, using strcpy will overwrite memory and you will eventually crash unless all of those buffers are allocated and the same size. In this case, you can just copy the pointers with
members[j] = members[j+1]
if it is:
std::string members[];
then you can treat it as a normal array and dispense with strcpy and free (just use =).
if it is:
char members[80][80]; // fixed size pre-allocated buffer
then your code will work, but free will not.
It would be simpler if lines and arrSize were members of a structure that you could pass in as a unit, but absent that:
void remove_by_index(char **lines, int line, int *arrSize) {
if (line >= *arrSize) return;
*arrSize -= 1;
if (lines[line]) free(lines[line]);
memmove(lines + line, lines + line + 1, ((*arrSize - line) * sizeof(char *)));
lines[*arrSize] = NULL;
}
Strictly speaking, either arrSize has to change to reflect deletion, or else the deleted cell has to be marked with NULL, but it's not necessary to do both as I have done here (though it's not a bad idea).
Also note that this will fail if two members of the array point to the same string, as will the other answers here--for that you'll need a more complex data structure.