Why is my array of pointers getting overwritten after dynamic allocation? - c

I'm working on a little C program for a class that reads the lines in from a file and then sorts them using qsort. Long story short, I am dynamically allocating memory for every line of a file, stored as a char*, in an array of char*. The reading in and storing ostensibly works fine based upon the output (see below), but when I print out the lines, they are all duplicates of the last line in the file. Can anyone point out my (most likely painfully obvious) error?
Here is the relevant code to the problem I'm currently running into:
char* trim_white_space(char* str);
char* get_line(FILE* infile, char temp[]);
int main(int argc, char* argv[]) {
FILE* infile;
char* input_file = argv[1];
int cnt = 0;
char temp[MAX_LINE_LENGTH]; //to hold each line as it gets read in
char* tempPointer = temp;
if (argc < 2) {
printf("No input file provided");
return EXIT_FAILURE;
}
//determine the number of lines in the file
infile = fopen(input_file, "r");
int num_lines_in_file = num_lines(infile);
fclose(infile);
//allocate pointers for each line
char** lines = (char**) malloc(num_lines_in_file * sizeof(char*));
//temporarily store each line, and then dynamically allocate exact memory for them
infile = fopen(input_file, "r");
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
tempPointer = get_line(infile, temp);
lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
lines[cnt] = trim_white_space(tempPointer);
printf("%d: %s\n", cnt, lines[cnt]);
}
fclose(infile);
//print the unsorted lines (for debugging purposes)
printf("Unsorted list:\n");
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
printf("%s\n", lines[cnt]);
}
char* get_line(FILE* infile, char temp[]) {
fgets(temp, MAX_LINE_LENGTH-1, infile);
char* pntr = temp;
return pntr;
}
char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace(*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace(*end)) end--;
// Write new null terminator
*(end+1) = 0;
return str;
}
I have this sample input file 5-1input.dat:
Hi guys
x2 My name is
Slim Shady
For real
And here's the output I'm getting:
user#user-VirtualBox ~/Desktop/Low-level/HW5 $ ./homework5-1 5-1input.dat
0: Hi guys
1: x2 My name is
2: Slim Shady
3: For real
Unsorted list:
For real
For real
For real
For real

As in the comments, you should change your loop to:
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
tempPointer = get_line(infile, temp);
lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
strncpy(lines[cnt], trim_white_space(tempPointer), strlen(tempPointer)+1);
printf("%d: %s\n", cnt, lines[cnt]);
}
The size in strncpy is based on the size of malloc you've used.
Of course you can optimize this code, e.g. to count strlen only once, etc.

Related

Getting error when trying release heap-allocated memory

I am working on a program that reads text, line by line from input file. Once the line is read, the program reverses order of words in that string, prints it to the output file and starts reading next line. My program reads only specific number of characters from one line, meaning that if line contains more characters then that specific number, all of them have to skipped until next line is reached. My program seems to work fine.
One of the task requirements is to use dynamically allocated arrays. That is the part where my main problem lies. Once I try to free heap-allocated memory, the program fails with error message that says: HEAP CORRUPTION DETECTED. It must be that I messed up something while working with them. However, I am unable to find the real reason.
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 255
int readLine(FILE** stream, char** buffer, int* bufferSize);
void reverseString(char* buffer, char** reverse, int bufferSize, int lastLine);
int main(int argc, char** argv)
{
char* buffer = NULL;
char* reverse = NULL;
int bufferSize = 0;
int lastLine = 0;
FILE* intputStream = fopen(argv[1], "r");
FILE* outputStream = fopen(argv[2], "w");
if (intputStream == NULL || outputStream == NULL)
{
printf("Input or output file cannot be opened\n");
return 0;
}
while (!feof(intputStream))
{
lastLine = readLine(&intputStream, &buffer, &bufferSize);
reverse = (char*)malloc(sizeof(char) * bufferSize);
if (reverse != NULL)
{
reverseString(buffer, &reverse, bufferSize, lastLine);
fputs(reverse, outputStream);
}
}
fclose(intputStream);
fclose(outputStream);
free(buffer);
free(reverse);
return 0;
}
int readLine(FILE** stream, char** buffer, int* bufferSize)
{
char tempBuffer[BUFFER_SIZE] = { 0 };
int lastLine = 0;
if (*stream != NULL)
{
fgets(tempBuffer, BUFFER_SIZE, *stream);
char ignoredChar[100] = { 0 };
*bufferSize = strlen(tempBuffer);
// Ignoring in the same line left characters and checking if this is the last line
if (tempBuffer[(*bufferSize) - 1] != '\n')
{
fgets(ignoredChar, 100, *stream);
if (!feof(*stream))
lastLine = 1;
}
// Allocating memory and copying line to dynamically-allocated array
*buffer = (char*)malloc(sizeof(char) * (*bufferSize));
if (*buffer != NULL)
{
memcpy(*buffer, tempBuffer, (*bufferSize));
(*buffer)[(*bufferSize)] = '\0';
}
}
// Return whether or not the last line is read
return lastLine;
}
void reverseString(char* buffer, char** reverse, int bufferSize, int lastLine)
{
int startingValue = (lastLine ? bufferSize - 1 : bufferSize - 2);
int wordStart = startingValue, wordEnd = startingValue;
int index = 0;
while (wordStart > 0)
{
if (buffer[wordStart] == ' ')
{
int i = wordStart + 1;
while (i <= wordEnd)
(*reverse)[index++] = buffer[i++];
(*reverse)[index++] = ' ';
wordEnd = wordStart - 1;
}
wordStart--;
}
for (int i = 0; i <= wordEnd; i++)
{
(*reverse)[index] = buffer[i];
index++;
}
if (!lastLine)
(*reverse)[index++] = '\n';
(*reverse)[index] = '\0';
}
One of the problems is in readLine where you allocate and copy your string like this (code shortened to the relevant parts):
*bufferSize = strlen(tempBuffer);
*buffer = (char*)malloc(sizeof(char) * (*bufferSize));
(*buffer)[(*bufferSize)] = '\0';
This will not allocate space for the null-terminator. And you will write the null-terminator out of bounds of the allocated memory. That leads to undefined behavior.
You need to allocate an extra byte for the null-terminator:
*buffer = malloc(*bufferSize + 1); // +1 for null-terminator
[Note that I don't cast the result, and don't use sizeof(char) because it's specified to always be equal to 1.]
Another problem is because you don't include the null-terminator in the bufferSize the allocation for reverse in main will be wrong as well:
reverse = (char*)malloc(sizeof(char) * bufferSize);
Which should of course be changed to:
reverse = malloc(bufferSize + 1); // +1 for null-terminator

Where is the error? (C program won't print out the content of dynamic array)

The task was to read the first 20 lines from a specific file and format them to use only specific parts, the next step was to store those formatted strings in a dynamic array (char ** str | a pointer to a pointer), send it to a function and print it out with said function
Here is the main code:
int main(int argc, char* argv[]){
FILE* file = fopen("./passwd.txt", "r"); // open file
if (!file)
{
perror("Error opening file");
return 1;
}
char line [MAXCHARS];
int counter = 0;
char ** str;
str = malloc(20 * sizeof (char*));
while (fgets(line, MAXCHARS, file) && counter < 20) {
char * position;
if ((position = strchr(line,':'))){
char * end_char;
*position = 0; //setting here the value 0, will terminate the string line (first column)
if((position = strchr(++position,':')) && (end_char = strchr(++position,':'))){ //increment the position to skip the second column
*end_char = 0; //set the value 0 to end_char to terminate the string pointed by position
char * temp_str = "\0";
sprintf(temp_str, "{%d} - {%s}\n", atoi(position), line ); //concatenate line and userID into one string and store it into a temporary string
*(str + counter) = malloc(sizeof (char) * strlen(temp_str)); //set the memory space for the temp_string into the array
*(str + counter) = temp_str; //copy the string into the array
}
}
counter++;
}
printArray(str);
fclose(file);
if (line)
free(line);
return 0;
}
And here is the print function:
void printArray(char ** array){
for(int i = 0; i < 20; i++){
printf("%s",*(array+i));
free(*(array+i));
}
free(array);
}
I cannot find the error, the code compiles with
Process finished with exit code -1073741819 (0xC0000005)
So at least it compiles, and I think is just a problem in my pointers handling skills, but I'm not able to find the error.
Can someone help me?
there are 3 errors in your program :
use temp_str which haven't allocated.
char * temp_str = "\0";
sprintf(temp_str, "{%d} - {%s}\n", atoi(position), line );
save temp_str local pointer's address to str+counter and use the pointer after it went out of scope at printArray=> undefined behavior
line is not a pointer, can't use free
if (line)
{
free(line);
}
lets try this. https://godbolt.org/z/7KPfnTEMY I correct these points

Extracting the first two words in a sentence in C without pointers

I am getting used to writing eBPF code as of now and want to avoid using pointers in my BPF text due to how difficult it is to get a correct output out of it. Using strtok() seems to be out of the question due to all of the example codes requiring pointers. I also want to expand it to CSV files in the future since this is a means of practice for me. I was able to find another user's code here but it gives me an error with the BCC terminal due to the one pointer.
char str[256];
bpf_probe_read_user(&str, sizeof(str), (void *)PT_REGS_RC(ctx));
char token[] = strtok(str, ",");
char input[] ="first second third forth";
char delimiter[] = " ";
char firstWord, *secondWord, *remainder, *context;
int inputLength = strlen(input);
char *inputCopy = (char*) calloc(inputLength + 1, sizeof(char));
strncpy(inputCopy, input, inputLength);
str = strtok_r (inputCopy, delimiter, &context);
secondWord = strtok_r (NULL, delimiter, &context);
remainder = context;
getchar();
free(inputCopy);
Pointers are powerful, and you wont be able to avoid them for very long. The time you invest in learning them is definitively worth it.
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
Extracts the word with the index "n" in the string "str".
Words are delimited by a blank space or the end of the string.
}*/
char *getWord(char *str, int n)
{
int words = 0;
int length = 0;
int beginIndex = 0;
int endIndex = 0;
char currentchar;
while ((currentchar = str[endIndex++]) != '\0')
{
if (currentchar == ' ')
{
if (n == words)
break;
if (length > 0)
words++;
length = 0;
beginIndex = endIndex;
continue;
}
length++;
}
if (n == words)
{
char *result = malloc(sizeof(char) * length + 1);
if (result == NULL)
{
printf("Error while allocating memory!\n");
exit(1);
}
memcpy(result, str + beginIndex, length);
result[length] = '\0';
return result;
}else
return NULL;
}
You can easily use the function:
int main(int argc, char *argv[])
{
char string[] = "Pointers are cool!";
char *word = getWord(string, 2);
printf("The third word is: '%s'\n", word);
free(word); //Don't forget to de-allocate the memory!
return 0;
}

Parsing and data overwriting issues in C using custom strtok

I'm reading in a .csv file, which I then need to parse into tokens. I tried using strtok(), but that unfortunately cannot return null fields (which my data is fulll of). So I went with a home-made version of strtok that I found, strtok_single, which returns the correct values that I need.
The data is input into my array correctly; but there is something wrong because before the initilization loops finish, the data gets overwritten. I've tried print statements and analyzing the problem but I just can't figure out what's wrong. Any insight at all would be helpful.
Here is the homemade strtok function I'm using:
char* strtok_single(char* str, char const* delims) {
static char* src = NULL;
char* p, *ret = 0;
if (str != NULL)
src = str;
if (src == NULL)
return NULL;
if ((p = strpbrk(src, delims)) != NULL) {
*p = 0;
ret = src;
src = ++p;
}
return ret;
}
Here is my code:
int main() {
int numLines = 0;
int ch, i, j;
char tmp[1024];
char* field;
char line[1024];
FILE* fp = fopen("filename.csv", "r");
// count number of lines in file
while ((ch = fgetc(fp)) != EOF) {
if (ch == '\n')
numLines++;
}
fclose(fp);
// Allocate memory for each line in file
char*** activity = malloc(numLines * sizeof(char**));
for (i = 0; i < numLines; i++) {
activity[i] = malloc(42 * sizeof(char*));
for (j = 0; j < 42; j++) {
activity[i][j] = malloc(100 * sizeof(char));
}
}
// read activity file and initilize activity matrix
FILE* stream = fopen("filename.csv", "r");
i = 0;
while (fgets(line, 1024, stream)) {
j = 0;
int newlineLoc = strcspn(line, "\n");
line[newlineLoc] = ',';
strcpy(tmp, line);
field = strtok_single(tmp, ",");
while (field != NULL) {
for (j = 0; j < 42; j++) {
activity[i][j] = field;
field = strtok_single(NULL, ",");
// when I print activity[i][j] here, the values are correct
}
// when I print activity[i][j] here, the values are correct for the
// first iteration
// and then get overwritten by partial data from the next line
}
i++;
} // close while
fclose(stream);
// by the time I get to here my matrix is full of garbage
// some more code that prints the array and frees memory
} // close main
activity[i][j] = field;
When the loops finish, each activity[i][j] points to somewhere in tmp, which is overwritten in each loop. Instead, since you pre-allocate space in each activity[i][j], you should just copy the contents of the string to that:
strcpy(activity[i][j], field);
Being careful of buffer overflow (i.e. if field is more than 99 characters).
Also, the sizeof(char) is superfluous since it's always 1 by definition.
Your line "activity[i][j] = field;" is backwards - you want the pointer assigned to the malloc'd memory.

How do I return a specific line from a file if it partially matches a string parameter? [C]

I am new to C but I am trying to write a function that returns a line from a file depending on the parameter used. It will return the last line that contains that parameter. I think it's better explained using an example:
Here is the contents of the file:
1 one onehello
2 two twohello
3 one threehello
So, if I call the function like this:
lineContaining("one")
It should return "one threehello"
Here is what I have so far, it also includes a main function that tests the function:
char *readStringedCommand(char *str1)
{
int size = 1024;
char *buffer = malloc(size);
char *result = malloc(size);
FILE *fp;
fp = fopen("test.txt", "r");
while(fgets(buffer, 1024, fp)) //get a line from a file
{
printf("while1 entered: %s", buffer);
int i = 0;
int j = 0;
int k = 0;
while(buffer[i] != '\n') //read all the way to the end of a line
{
printf("while2 entered: %s", buffer+i);
k = i;
while(buffer[k]==str1[j]) //while two characters match
{
printf("while3 entered");
k++;
j++;
strcat(result, buffer+k); //append the character to the result
if(str1[j] = '\0') //if the next character of str1 is the last one
{
strncat(result, buffer+k, 20); //append the rest of buffer to the result
return result;
printf("result = %s", result);
}
}
result[0] = '\0'; //clear result for the next line
j = 0; //set str1's position to 0
k = 0;
i++;
}
}
return "errorrrrrr";
}
int main(int argc, const char* argv[])
{
int num1 = 1;
char str1[] = "one onehello";
int num2 = 2;
char str2[] = "two twohello";
int num3 = 3;
char str3[] = "one threehello";
hwrite(num1, str1); //a function I made that writes a line to a file
hwrite(num2, str2);
hwrite(num3, str3);
printf("%s", readStringedCommand("one"));
return 0;
}
Okay, the function gives me an error:
while1 entered: 1 one onehello
while2 entered: 1 one onehello
while2 entered: one onehello
while2 entered: one onehello
Segmentation fault (core dumped)
Considering it gives me the error at the third while loop, I think the problem is there. I unfortunately don't know what is wrong here. I am sure there are more errors after that point but this one is confusing me.
MY QUESTIONS:
How do I fix this segmentation error?
The code is obviously very ugly but I suck at C. Is there a better way to solve this question?
Thanks for reading all of this and I would appreciate some help. =(
EDIT: After fixing some errors suggested by you guys, I no longer get the segmentation error. The function returns " onehello" instead, which is wrong. It should return "one threehello". But I am making progress, and for that I am thankful.
if(str1[j] = '\0')
should be
if(str1[j] == '\0')
you probably want to compare values
the loop while(buffer[i] != '\n') might not exit if your file is missing a newline character, what might happen in the last line of in .txt file.
So if I understand your problem correctly, your goal can be achieved in a much simpler way:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char *readStringedCommand(char *str1)
{
int size = 1024;
char *buffer = malloc(size);
char *result = malloc(size);
/* Check allocations */
if (!buffer || !result) {
fprintf(stderr, "Allocation failure, exiting.\n");
exit(EXIT_FAILURE);
}
result[0] = 0; // 0-terminate result
FILE *fp;
fp = fopen("test.txt", "r");
/* Check whether file was opened successfully */
if (!fp) {
fprintf(stderr, "Couldn't open file.\n");
exit(EXIT_FAILURE);
}
while(fgets(buffer, size, fp)) //get a line from a file
{
if (strstr(buffer, str1)) { // Check whether the line contains the needle
strcpy(result, buffer); // Copy the line if it does
}
}
// close file
fclose(fp);
// Free memory we don't need anymore
free(buffer);
// Now, what if no line contained the needle?
if (result[0] == 0) {
// Hmm, maybe
free(result);
return NULL; // ?
} else {
return result;
}
}
Just use strstr from the standard library to check whether str1 is contained in each line, copy each line that contains it to result, ignore the others. Then at the end, result contains the last line that contained str1 - if any.
If you don't want the entire line, but only the part starting at the first occurrence of str1, capture the pointer that strstr returns, and use that as the source for the strcpy:
char *needle;
if ((needle = strstr(buffer, str1))) {
strcpy(result, needle);
}

Resources