Admittedly what I was trying to do was at best educated guess work.
I have an array of strings, and I was trying to account for if someone entered a string that was too large, or the array received too much input (it needs to be dynamic for what I'm trying to do)
here's the section of code that's breaking:
if (strlen(token) > wordlength)
{
wordlength *= 2;
for(int j = 0; j < numwords; j++)
{
char* tmpw = realloc(wordarray[j], wordlength);
assert(tmpw != NULL);
wordarray[j] = tmpw;
printf("increased size of words to %zu \n", wordlength);
}
}
Explanations:
Token is the next word being taken in (I'm parsing a string) so I compare it to the current word length, if it's too big, i double word length and try to adjust the array accordingly.
If you need any more information let me know
initialization of wordarray:
wordarray = malloc(numwords);
for(int i = 0; i < numwords; i++)
wordarray[i] = malloc(wordlength);
Another place where realloc crashes:
if (arraycounter > numwords)
{
numwords *= 2;
char** tmp = realloc(wordarray, numwords);
assert(tmp != NULL);
wordarray = tmp;
for(int h = arraycounter; h < numwords; h++)
wordarray[h] = malloc(wordlength);
printf("increased size of wordarray to %zu \n", numwords);
}
In this situation, it would attempt to increase the size of the array if it was about to go over the initial set limit, so would not be affected due to token running out of memory (tested with 20 small words and it crashed on its attempt to resize)
You need
wordarray = malloc(numwords * sizeof *wordarray);
Also, what do you want to happen when your program is compiled without assert and run on a system with low memory? I mean, the use of assert() is probably wrong.
Related
{
char name_entry[ARRAY_SIZE];//bunu dinamik olarak atamıyorum her seferinde gireceğimiz ismin uzunluğunu sormak pratik olmayacaktır
int isimsayisi;
char** names;
printf("Kac Isim Gireceksiniz:");
scanf("%d", &isimsayisi);
names = (char**)malloc(sizeof(char) * (isimsayisi));
for (int k = 0; k < isimsayisi; k++) {
printf("\nismi giriniz :");
scanf("%s", name_entry);
names[k] = (char*)malloc(strlen(name_entry) + 1);
if (names[k] == NULL) {
printf("bellek yetersiz!..\n");
exit(EXIT_FAILURE);
}
strcpy(names[k], name_entry);
}
printf("\nGirilen Isimler : \n");
for (int k = 0; k < isimsayisi; k++) {
printf("%s \n", names[k]);
}
printf("\n");
for (int i = 0; i < isimsayisi; i++)
free(names[i]);
_getch();
}
In this code Im try to take names to a 2d string array then printf what have ı took from user then deallocate the memory but ı cant if I print free(names); after the for loop it gives a windows tab error. Its work like that to 2 names if I take more then 2 names its causes a breakpoint
Edit:isimsayisi means how much names the users wants to enter
This
names = (char**)malloc(sizeof(char) * (isimsayisi));
should be
names = malloc(sizeof(char *) * (isimsayisi));
you don't need to cast malloc() in C (different story for C++).
names is char **; therefore, when allocating memory, you need to use the sizeof(char *), which is definitely different than sizeof(char).
sizeof(char) is the size of a single character and is guaranteed to be 1, while sizeof(char *) is the size of an address and varies based on your machine (8 for a 64-bit CPU).
I get an error that I free an invalid pointer.
consider size as 100 (maximum number of words), and max_str_len as 50 (max number of letters in a word, doesn't contain '\0'. the function is set to scan a sentence and store every word in words array.
int read_words(char* words[], int size, int max_str_len){
int wordsCounter = 0;
for (int i = 0; i <size; ++i) {
words[i]=(char*)malloc(sizeof(char)*(max_str_len+1));
if(words[i]==NULL){
//in case of failure it frees every word.
for (int j = 0; j <i ; ++j) {
free(words[j]);
}
return MALLOCERROR;
}
for (int j = 0; j <max_str_len+1; ++j) {
if(scanf("%c", &words[i][j])==EOF){
wordsCounter++;
words[i][j]='\0';
if(j<max_str_len)
free(&words[i][j+1]);
return wordsCounter;
}
if (words[i][j]==' ') {
words[i][j] = '\0';
if(j<max_str_len)
free(&words[i][j+1]);
break;
}
}
wordsCounter++;
}
return wordsCounter;
}
I see what's going on here; you're allocating a large buffer, reading into it, and if you didn't need all that space for the word, then you want to give back the memory. That makes sense.
You can't tell the allocator: free starting from position but we can do almost the same thing with realloc(), which takes a pointer and resizes the array, possibly moving it to a new location. In your case of shortening an array, it should work really well.
Instead of
if (j < max_str_len)
free(&words[i][j+1]); // INVALID
try
if (j < max_str_len)
words[i] = realloc(words[i], j+1); // +1 for the NUL byte
This should do what you're looking for.
Consider this simple program that concatenates all specified parameters and prints them in standard output. I used 2 for loops to append the strings, one to calculate the length of that string and one to concatenate the strings. Is there a way doing it with only one loop? It wouldn't be more efficient reallocating memory for each string to concatenate, would it? How would Java's StringBuilder be implemented in C? Would it loop twice as I did?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
size_t len = 0;
// start for loop at i = 1 to skip the program name specified in argv
for(int i = 1; i < argc; i++)
len += strlen(argv[i]) + 1; // +1 for the space
char* toAppend = (char*)malloc(len * sizeof(char) + 1);
toAppend[0] = '\0'; // first string is empty and null terminated
for(int i = 1; i < argc; i++)
{
strcat(toAppend, argv[i]);
strcat(toAppend, " ");
}
printf(toAppend);
free(toAppend);
}
Your method of allocation is efficient, measuring the total length and allocating just once. But the concatenation loop repeatedly measures the length of the output buffer from the start to concatenate to it, resulting in quadratic runtime.
To fix it track your position as you go:
size_t pos = 0;
for(int i = 1; i < argc; i++) {
size_t len = strlen(argv[i]);
memcpy(toAppend+pos, argv[i], len);
pos += len;
toAppend[pos] = ' ';
pos++;
}
toAppend[pos] = 0;
This is the most efficient way to actually concatenate in memory, but the most efficient of all is not to concatenate. Instead:
for(int i = 1; i < argc; i++)
printf("%s ", argv[i]);
The whole reason stdio is buffered is so you don't have to build arbitrary-length in-memory buffers to do efficient output; instead it buffers up to a fixed size automatically and flushes when the buffer is full.
Note that your usage of printf is wrong and dangerous in the event that your input contains a % character anywhere; it should be printf("%s", toAppend);.
If you're writing to POSIX (or POSIX-ish) systems rather than just plain C, another option would be fmemopen, which would allow you to write the loop just as:
for(int i = 1; i < argc; i++)
fprintf(my_memfile, "%s ", argv[i]);
efficient way to concatenate strings in c
An efficient way is to calculate the string lengths - and remember them.
size_t sum = 1; // for \0
if (argc > 2) sum += argc - 2. // spaces
size_t length[argc]; // This is a VLA, available C99 and optionally in C11
for(int i = 1; i < argc; i++)
length[i] = strlen(argv[i]);
sum += length[i];
}
Then allocate, and then check for errors.
char *dest = malloc(sum);
if (dest == NULL) Handle_OutOfMemory();
Copy each string in turn
char *p = dest;
for(int i = 1; i < argc; i++)
// Use either memcpy() or strcpy().
// memcpy() tends to be faster for long strings than strcpy().
memcpy(p, argv[i], length[i]);
p += length[i]; // advance insertion point
if (i > 1) {
*p++ = ' '; // space separators
}
}
*p = '\0';
Now use dest[].
printf("<%s>\n", dest);
Free resources when done.
free(dest);
It wouldn't be more efficient reallocating memory for each string to concatenate, would it?
Usually repetitive re-allocations is best avoided, yet for small short strings it really makes scant difference. Focus on big O. My answer is O(n). Relocating in a loop tends to be O(n*n).
If performance was critical, try various approaches and profile for the intended system. The point being what is fast on one machine may differ on another. Usually it is best to first code a reasonable clear approach.
The most efficient way is probably to not use any str functions and copy the characters "by hand":
char* toAppend = malloc(len + 1);
size_t j = 0;
for(size_t i = 1; i < argc; i++)
{
for(size_t k = 0; argv[i][k]; k++)
toAppend[j++] = argv[i][k];
toAppend[j++] = ' ';
}
toAppend[j - 1] = '\0'; // Remove the last space and NULL-terminate the string
I am experiencing an issue where the invocation of realloc seems to modify the contents of another string, keyfile.
It's supposed to run through a null-terminated char* (keyfile), which contains just above 500 characters. The problem, however, is that the reallocation I perform in the while-loop seems to modify the contents of the keyfile.
I tried removing the dynamic reallocation with realloc and instead initialize the pointers in the for-loop with a size of 200*sizeof(int) instead. The problem remains, the keyfile string is modified during the (re)allocation of memory, and I have no idea why. I have confirmed this by printing the keyfile-string before and after both the malloc and realloc statements.
Note: The keyfile only contains the characters a-z, no digits, spaces, linebreaks or uppercase. Only a text of 26, lowercase letters.
int **getCharMap(const char *keyfile) {
char *alphabet = "abcdefghijklmnopqrstuvwxyz";
int **charmap = malloc(26*sizeof(int));
for (int i = 0; i < 26; i++) {
charmap[(int) alphabet[i]] = malloc(sizeof(int));
charmap[(int) alphabet[i]][0] = 0; // place a counter at index 0
}
int letter;
int count = 0;
unsigned char c = keyfile[count];
while (c != '\0') {
int arr_count = charmap[c][0];
arr_count++;
charmap[c] = realloc(charmap[c], (arr_count+1)*sizeof(int));
charmap[c][0] = arr_count;
charmap[c][arr_count] = count;
c = keyfile[++count];
}
// Just inspecting the results for debugging
printf("\nCHARMAP\n");
for (int i = 0; i < 26; i++) {
letter = (int) alphabet[i];
printf("%c: ", (char) letter);
int count = charmap[letter][0];
printf("%d", charmap[letter][0]);
if (count > 0) {
for (int j = 1; j < count+1; j++) {
printf(",%d", charmap[letter][j]);
}
}
printf("\n");
}
exit(0);
return charmap;
}
charmap[(int) alphabet[i]] = malloc(sizeof(int));
charmap[(int) alphabet[i]][0] = 0; // place a counter at index 0
You are writing beyond the end of your charmap array. So, you are invoking undefined behaviour and it's not surprising that you are seeing weird effects.
You are using the character codes as an index into the array, but they do not start at 0! They start at whatever the ASCII code for a is.
You should use alphabet[i] - 'a' as your array index.
The following piece of code is a source of troubles:
int **charmap = malloc(26*sizeof(int));
for (int i = 0; i < 26; i++)
charmap[...] = ...;
If sizeof(int) < sizeof(int*), then it will be performing illegal memory access operations.
For example, on 64-bit platforms, the case is usually sizeof(int) == 4 < 8 == sizeof(int*).
Under that scenario, by writing into charmap[13...25], you will be accessing unallocated memory.
Change this:
int **charmap = malloc(26*sizeof(int));
To this:
int **charmap = malloc(26*sizeof(int*));
Basically, I'm trying to convert a bunch of char inputs to ints and assign them to a dynamic int array. The string input and tokenization seem to work fine. The issue (from what I can tell) seems to be with the reallocation of the int array; after the array is reallocated twice, the pointer to the int array returns NULL.
What I tried to do was double the size of the int array every time the number of tokens meets or surpasses (size divided by sizeof(int)). The realloc statement works each time this condition is met.
I thought using a pointer to a pointer was the end-all solution to this. I bet it's some really obvious issue, but I'm at my wit's end here. If you request any further elaboration, I'll try my best. Understand that I've only taken C for a semester and have struggled most of the way.
Also, truth be told, this was part of a class assignment which has since passed. I'd prefer an explanation about what's wrong more than a full-on code, if that's alright.
I have a lot of printf statements, so apologies for any clutter.
EDIT: Replaced all instances of newArray within the input() function with *resize. However, I've never tried assigning values through pointers to pointers, so feel free to correct me with a syntactic example if you know how I messed up. Segmentation fault occurs here:
for (k = (numElem - count); k < numElem; k++)
{
printf("\nk = %i\n", k);
printf("j = %i\n", j);
printf("numElem = %i\n", numElem);
printf("results[j]: %s\n\n\n", results[j]);
/* Segmentation fault regardless of what is assigned
to *resize[k]. */
*resize[k] = atoi(results[j]); // PROBLEM HERE
j++;
}
The source code has been updated to reflect upon this. To make this ridiculously long post a little more subdued, let's state that I did this in main():
int *newArray = malloc(MAXTOKEN * sizeof(int));
input(&newArray);
free(newArray);
Moving on.
/* String input takes in char values,
tokenizes them, converts the results
to int, assigns them to newresizeay. */
int input(int **resize)
{
int i, j, k, count;
int numElem = 0;
int currentSize = MAXTOKEN;
char str[MAXSTRING];
char *results[MAXTOKEN];
/* This entire loop takes place at least once,
provided the first input isn't NULL. */
do
{
i = 0, j = 0, k = 0;
/* Char input process. Takes place until the user
presses ENTER. */
printf("Input integer values separated by spaces, or "
"press ENTER to exit.\n");
while ( ((str[i] = getchar() ) != '\n') && (i < MAXSTRING) )
i++;
printf("\n\n");
str[i] = '\0';
/* Tokenization of the chars that were input */
count = 0;
if (results[0] = strtok(str, " \t"))
count++;
while (results[count] = strtok(NULL, " \t") )
count++;
/* numElem = 1 if the first input prompt established
str[0] as NULL */
if ( (count < 1) && (numElem < 1) )
count = 1;
numElem += count;
printf("numElem: %i\ncurrentSize: %i\n", numElem, currentSize);
/* If the number of elements to assign meet or surpass
the amount of [memory / sizeof(int)], exponentially
increase the size of the int resizeay. */
if ( numElem >= currentSize )
{
*resize = realloc(*resize, (currentSize) * sizeof(int));
if (*resize == NULL)
printf("\n\nYep, it threw up.\n\n");
currentSize *= 2;
}
printf("\nSize should be: %i\n", currentSize * 4);
printf("Actual size: %d\n", _msize(*resize));
/* The tokenized chars are converted to integers and
assigned to the int resizeay. */
for (k = (numElem - count); k < numElem; k++)
{
printf("\nk = %i\n", k);
printf("j = %i\n", j);
printf("numElem = %i\n", numElem);
printf("results[j]: %s\n\n\n", results[j]);
*resize[k] = atoi(results[j]); // PROBLEM HERE
j++;
}
for (i = 0; i < numElem; i++)
printf("resize[%i]: %i\n", i, *resize[i]);
printf("\n\n\n");
} while (str[0] != NULL);
}
The input function receives both resize and arr. main sends the same pointer to both. This is a bug.
When resize is resized, arr stays the same and may point to an invalid address (when realloc returns a different address).
How to fix:
Remove arr function argument and only use resize.
When you call the realloc function,if the new memory block is smaller than previous ,it will maintain the original state pointing to the memory block which previous used.If the new memory block is larger than previous,the system will re allocate memory on the heap and the previous memory is released.
Among other problems:
char *results[MAXTOKEN];
should be
char *results[MAXTOKEN + 1];
because here the maximum value of count will be MAXTOKEN in this loop :
while (results[count] = strtok(NULL, " \t") )
count++;
and
char str[MAXSTRING];
is pretty scary, because as soon as the user enters more than MAXSTRIN (=11) characters without pressing Enter, you will get a buffer overflow.