Dynamic 2D array crashes - c

I am reading from a file (each line wolds 1 word) and putting each line into an array. It crashes when its about to close the file saying (* glibc detected * proj: corrupted double-linked list: 0x0000000002139240 ***). Also everything but the 1st element was copied correctly (the 1st element was supposed to be "how are you" but was instead "0"). Any help on this is greatly appreciated.
int i = -1;
int numb;
int wsize;
while (fgets(word,30,file)!=NULL)
{
if (i==-1)
{
if(word[strlen(word)-1]=='\n')
{
word[strlen(word)-1] = 0;
}
numb = atoi(word);
ptr = malloc(sizeof(char*)*numb);
}
else
{
if(word[strlen(word)-1]=='\n')
{
word[strlen(word)-1] = 0;
}
wsize = strlen(word);
ptr[i] = malloc(sizeof(char*)*wsize);
strncpy(ptr[i],word,strlen(word));
size++;
}
i++;
}
int j=0;
while(j<16) //prints to see if they were copied corectly
{ //ptr[0] was the only one that did not copy corectly
printf("%s\n",ptr[j]);
j++;
}
fclose(file);
printf("test\n"); //was never printed so I assume it crashes at fclose()
return 1;

The line ptr[i] = malloc(sizeof(char*)*wsize); is wrong, for two reasons:
It should be sizeof(char), not sizeof(char*) (or just omit this, since sizeof(char) is equal to 1 by definition)
If you want to store a string of length wsize, you need to allocate wsize+1 bytes
EDIT — More issues:
What is the purpose of the line size++;? Did you mean wsize++;?
Where does the number 16 come from in while(j<16)? I suggest you try while(j<i) instead.
If main() returns a nonzero value, this signifies that an error occurred. Change this to return 0; unless you have a good reason for returning some other value.
One more:
I just noticed you're using strncpy(). This won't add a terminating '\0' byte to the end of the string because you've asked it to copy a string of length wsize using no more than wsize bytes. Change this line to strcpy(ptr[i],word); and it should work.
After you've done that, you need to remove all the potential buffer overflows from your code. (There are lots of them.)

Related

Why does this program print strings with strange characters? Memory leak despite freeing?

I'm a beginner to C and learning it as part of uni course. One of the practice problems given to us asks us to create a function that returns a user defined string with hyphen ('-') characters added before and after. The user will also define the total number of characters. This function will be called "center" and will be called as follows: center(char userString[], totalLength). So if userString = "cat" and totalLength = 5, then center should return "-cat-".
The center function does its job just fine. It creates a string with hyphens added. However, when trying to print I'm seeing weird characters in the output. I've been told about memory leaks in lectures but I've tried to allocate and free memory.
Center function:
char *center(char s[], int totalLength){
int dashes = totalLength-strlen(s);
int i;
char* ret=malloc(totalLength+1);
int left = dashes/2;
for(i=0; i<left; i++){
ret[i]='-';
}
for(i=left;i<strlen(s)+1;i++){
ret[i]=s[i-left];
}
for(i=strlen(s)+left;i<totalLength;i++){
ret[i]='-';
}
ret[totalLength+1]='\0';
return ret;
}
Main function:
int main(void) {
char s[] = "cat";
char *ret=malloc(100);
ret = center(s,5);
printf("%s\n", ret);
free(ret);
ret = center(s, 6);
printf("%s\n", ret);
free(ret);
ret = center(s, 7);
printf("%s\n", ret);
free(ret);
return 0;
}
Actual output:
-cat-
-cat--o
--caâ””--
Expected output:
-cat-
-cat--
--cat--
char* ret=malloc(totalLength+1);
...
ret[totalLength+1]='\0';
The highest index of an array is one less than the size. A buffer of size totalLength+1 has indices from 0 to totalLength. The misplaced \0 explains the trailing junk (o) on line 2.
for(i=left;i<strlen(s)+1;i++)
// ^^
for(i=strlen(s)+left;i<totalLength;i++)
// ^^^^^
+1 and +left should be the same to ensure the later loop picks up where the earlier one left off. The mismatch is a sign that one of them is wrong, and it's causing there to be a gap of uninitialized characters, which is why you're seeing garbage (â””) in the middle of line 3.

For loop is running too often

I have a for loop which should run 4 times but is running 6 times.
Could you please explain the behaviour?
This is strange because stringarr1 is not changed.
Edit: I want to remove all '!' from my first string and want to save the letters in a second string.
#include <stdio.h>
#include <math.h>
#include <string.h>
int main(){
char stringarr1[] = "a!bc";
char stringarr2[] = "";
printf("%d\n", strlen(stringarr1)); // lenght --> 4
for (size_t i = 0; i < strlen(stringarr1); i++)
{
printf("i: %d\n", i);
if (stringarr1[i] != '!') {
stringarr2[strlen(stringarr2)] = stringarr1[i];
printf("info: != '!'\n");
}
}
}
You are overrunning the buffer for stringarr2 (length 1), which is in this case corrupting the memory-adjacent stringarr1, causing the string length to change by overwriting its nul terminator.
Then because you are reevaluating the string length on each iteration, the loop will run for a non-deterministic number of iterations - in your case just 6, but it could be worse; the behaviour you have observed is just one of several possibilities - it is undefined.
Apart from correcting the buffer length for stringarr2, it is best practice to evaluate loop-invariants once (although in this case the string length is not invariant due to a bug). So the following:
const size_t length = strlen( stringarr1 ) ;
for( size_t i = 0; i < length; i++ )
{
...
will run for 4 iterations regardless of the buffer overrun bug because the length is not reevaluated following the corruption. Re-evaluating loop-invariants can lead to very slow code execution.
Your code can run any number of times. You write beyond the end of stringarr2 so you may be smashing the stack and overwriting local variables. What you meant to do is probably something like this:
#include <stdio.h>
#include <math.h>
#include <string.h>
int main(){
char stringarr1[] = "a!bc";
char stringarr2[10];
int len = strlen(stringarr1);
printf("%d\n", len); // lenght --> 4
for (size_t i = 0; i < len; i++)
{
printf("i: %d\n", i);
if (stringarr1[i] != '!') {
stringarr2[len] = stringarr1[i];
printf("info: != '!'\n");
}
}
}
Like others said, it is not really clear what you are trying to accomplish here. But in C, a declaration like char s[] = "string" only allocates enough memory to store whatever is on the right hand side of the assignment. If that is an empty string like in your case, only a single byte is allocated, to store the end of string 'null' character. You need to either explicitly specify, like I did, the number of bytes to allocate as the array size, or use dynamic memory allocation.
The problem is that you're writing past the end of stringarr2. This triggers undefined behaviour.
To fix this, you need to allocate sufficient memory for stringarr2.
First, we must allocate the string to be long enough.
char stringarr1[] = "a!bc";
//save this in a variable beforehand because strlen loops over the string every time it is called
size_t len = strlen(stringarr1);
char stringarr2[1024] = { 0 };
{ 0 } initializes all characters in the string to 0, which means the last one will always be a null terminator after we add characters. This tells C string functions where the string ends.
Now we can put stuff in there. It seems like you're trying to append, so keep a separate iterator for the 2nd string. This is more efficient than calling strlen every loop.
for(size_t i = 0, j = 0; i < len; i++){
printf("i: %d\n", i);
if (stringarr1[i] != '!') {
stringarr2[j++] = stringarr1[i];
printf("info: != '!'\n");
}
}

Displaying lines with given number of characters

I have written such a program which suppose to returns lines which are containing at least 11 characters and 4 digits. I messed up something with types of variables I guess but I cant figure out how should I fix it.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{
char line[200];
char *temp[200];
int i = 0, k=0;
printf("Enter a string: \n");
while(fgets(line, sizeof(line),stdin))
{
int numberAlpha = 0;
int numberDigit = 0;
int i;
for(i=0; i<strlen(line); i++){
if(isalpha(line[i])) numberAlpha++;
else if(isdigit(line[i])) numberDigit++;
}
if(numberAlpha+numberDigit>10 && numberDigit>3){
temp[i]=line;
i++;
}
}
while(temp[k]!='\0'){
printf("%s", temp[k]);
k++;
}
return 0;
}
You're reusing the same buffer each time, and you're storing a pointer to that buffer in your temp array. What you're going to end up with is a bunch of the same pointer in that array, with that pointer pointing at the last line in the file.
What you can do instead is to rewrite your temp[i]=line statement to the following:
temp[i] = malloc(sizeof(line))
memcpy(temp[i], line, sizeof(line))
In so doing, you'll be creating a new array with the contents of the matching line, which won't get overwritten when you come around and read the next line out of the file.
Note that, because you're allocating that on the heap, at the end of your function you'll want to free it:
while (temp[k] != '\0') {
printf(...);
free(temp[k]);
k++
}
As said before , one issue is with copying of
temp[i]=line;
This can be solved by doing a new heap allocation and doing memcopy to temp.
The other issue that i could see is - with the value of variable i. Then temp array will always be assigned to strlen(line) index. You might be thinking of storing in the temp array from 0. Which is not happening.
This can be solved by-
int start_index=0;
while(...){
if(numberAlpha+numberDigit>10 && numberDigit>3){
temp[start_index]=line;
start_index++;
}
}
The problem is you are assigning the same address here:
temp[i]=line;
and line is used in the loop to read as well. That means it's overwritten in every iteration.
Instead, you can use strdup() (POSIX function):
temp[i] = strdup(line);
to copy the lines you are interested in. If strdup() not available you can use malloc() + strcpy() to do the same. Plus, free() them later.
In addition, be aware that:
fgets() will read in the newline character if there's room in the buffer which may not be what you want. So, you need to trim it out. You can do it with:
line[strcspn(line, "\n")] = 0; /* trim the trailing newline, if any */
The arguments to isalpha() and isdigit() should be cast to unsigned char to avoid potential undefined behaviour i.e. these two lines:
if(isalpha(line[i])) numberAlpha++;
else if(isdigit(line[i])) numberDigit++;
should be
if(isalpha((unsigned char)line[i])) numberAlpha++;
else if((unsigned char)isdigit(line[i])) numberDigit++;

Array Not Filling Properly

I am trying to deconstruct a document into its respective paragraphs, and input each paragraphs, as a string, into an array. However, each time a new value is added, it overwrites all previous values in the array. The last "paragraph" read (as denoted by newline) is the value of each non-null value of the array.
Here is the code:
char buffer[MAX_SIZE];
char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));
int pp = 0;
int i;
FILE *doc;
doc = fopen(argv[1], "r+");
assert(doc);
while((i = fgets(buffer, sizeof(buffer), doc) != NULL)) {
if(strncmp(buffer, "\n", sizeof(buffer))) {
paragraphs[pp++] = (char*)buffer;
}
}
printf("pp: %d\n", pp);
for(i = 0; i < MAX_SIZE && paragraphs[i] != NULL; i++) {
printf("paragraphs[%d]: %s", i, paragraphs[i]);
}
The output I receive is:
pp: 4
paragraphs[0]: paragraph four
paragraphs[1]: paragraph four
paragraphs[2]: paragraph four
paragraphs[3]: paragraph four
when the program is run as follows: ./prog.out doc.txt, where doc.txt is:
paragraph one
paragraph two
paragraph three
paragraph four
The behavior of the program is otherwise desired. The paragraph count works properly, ignoring the line that contains ONLY the newline character (line 4).
I assume the problem occurs in the while loop, however am unsure how to remedy the problem.
Your solution is pretty sound. Your Paragraph array is supposed to hold each paragraph, and since each paragraph element is just a small 4 bytes pointer you can afford to define a reasonable max number of them. However, since this max number is a constant, it is of little use to allocate the array dynamically.
The only meaningful use of dynamic allocation would be to read the whole text once to count the actual number of paragraphs, allocate the array accordingly and re-read the whole file a second time, but I doubt this is worth the effort.
The downside of using fixed-size paragraph array is that you must stop filling it once you reach the maximal number of elements.
You can then re-allocate a bigger array if you absolutely want to be able to process the whole Bible, but for an educational exercise I think it's reasonable to just stop recording paragraphs (thus producing a code that can store and count paragraphs up to a maximal number).
The real trouble with your code is, you don't store the paragraph contents anywhere. When you read the actual lines, it's always inside the same buffer, so each paragraph will point to the same string, which will contain the last paragraph read.
The solution is to make a unique copy of the buffer and have the current paragraph point to that.
C being already messy enough as it is, I suggest using the strdup() function, which duplicates a string (basically computing string length, allocating sufficient memory, copying the string into it and returning the new block of memory holding the new copy). You just need to remember to free this new copy once you're done using it (in your case at the end of your program).
This is not the most time-efficient solution, since each string will require a strlen and a malloc performed internally by strdump while you could have pre-allocated a big buffer for all paragraphs, but it is certainly simpler and probably more memory-efficient (only the minimal amount of memory will be allocated for each string, though each malloc consumes a few extra bytes for internal allocator housekeeping).
The bloody awkward fgets also stores the trailing \n at the end of the line, so you'll probably want to get rid of that.
Your last display loop would be simpler, more robust and more efficient if you simply used pp as a limit, instead of checking uninitialized paragraphs.
Lastly, you'd better define two different constants for max line size and max number of paragraphs. Using the same value for both makes little sense, unless you're processing perfectly square texts :).
#define MAX_LINE_SIZE 82 // max nr of characters in a line (including trailing \n and \0)
#define MAX_PARAGRAPHS 100 // max number of paragraphs in a file
void main (void)
{
char buffer[MAX_LINE_SIZE];
char * paragraphs[MAX_PARAGRAPHS];
int pp = 0;
int i;
FILE *doc;
doc = fopen(argv[1], "r+");
assert(doc != NULL);
while((fgets(buffer, sizeof(buffer), doc) != NULL)) {
if (pp != MAX_PARAGRAPHS // make sure we don't overflow our paragraphs array
&& strcmp(buffer, "\n")) {
// fgets awkwardly collects the ending \n, so get rid of it
if (buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = '\0';
// current paragraph references a unique copy of the actual text
paragraphs[pp++] = strdup (buffer);
}
}
printf("pp: %d\n", pp);
for(i = 0; i != pp; i++) {
printf("paragraphs[%d]: %s", i, paragraphs[i]);
free(paragraphs[i]); // release memory allocated by strdup
}
}
What is the proper way to allocate the necessary memory? Is the malloc on line 2 not enough?
No, you need to allocate memory for the 2D array of strings you created. The following will not work as coded.
char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));
If you have: (for a simple explanation)
char **array = {0}; //array of C strings, before memory is allocation
Then you can create memory for it like this:
int main(void)
{
int numStrings = 10;// for example, change as necessary
int maxLen = MAX_SIZE; //for example, change as necessary
char **array {0};
array = allocMemory(array, numStrings, maxLen);
//use the array, then free it
freeMemory(array, numStrings);
return 0;
}
char ** allocMemory(char ** a, int numStrings, int maxStrLen)
{
int i;
a = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(sizeof(char)*maxStrLen + 1, sizeof(char));
}
return a;
}
void freeMemory(char ** a, int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(a[i]) free(a[i]);
free(a);
}
Note: you can determine the number of lines in a file several ways, One way for example, by FILE *fp = fopen(filepath, "r");, then calling ret = fgets(lineBuf, lineLen, fp) in a loop until ret == EOF, keeping count of an index value for each loop. Then fclose(). (which you did not do either) This necessary step is not included in the code example above, but you can add it if that is the approach you want to use.
Once you have memory allocated, Change the following in your code:
paragraphs[pp++] = (char*)buffer;
To:
strcpy(paragraphs[pp++], buffer);//no need to cast buffer, it is already char *
Also, do not forget to call fclose() when you are finished with the open file.

Skip white space and return one word at a time in C

This code is supposed to skip white space and return one word at a time. A couple of questions on this code: When the code gets to the *word++=c; line I get a core dump. Have I written this line correctly? and is return correct. And Do I need to somehow allocate memory to store the word?
//get_word
int get_word(char *word,int lim){
int i=0;
int c;
int quotes=0;
int inword = 1;
while(
inword &&
(i < (lim-1)) &&
((c=getchar()) != EOF)
){
if(c==('\"')){//this is so i can get a "string"
if (quotes) {
inword = 0;
}
quotes = ! quotes;
}
else if(quotes){ //if in a string keep storing til the end of the string
*word++=c;//pointer word gets c and increments the pointer
i++;
}
else if(!isspace(c)) {//if not in string store
*word++=c;
i++;
}
else {
// Only end if we have read some character ...
if (i)
inword = 0;
}
}
*word='\0'; //null at the end to signify
return i; //value
}
It's impossible to tell why this core dumps without seeing the code that calls get_word. The failure at the line you named implies that you are passing it something invalid in the first parameter. There's nothing wrong with that line in and of itself, but if word does not point to writable memory large enough to hold your output characters, you are in trouble.
The answer to your question about allocating memory to hold it is yes - however this could be local (e.g. a char array in the caller's local variables, global, or heap-based (e.g. from char * wordHolder = malloc(wordLimit);). The fact you are asking this supports the guess that your parameter 1 value is the problem.

Resources