How do i deallocate memory here? (c) - c

I have a really quick question. Why do i get heap corruption detected when i try to deallocate the array in the void translateWord() function?
I tried to deallocate line by line in a for loop but it doesnt seem to work. I thought that if i use that function more than once, and every time the function allocates memory, i should deallocate it at the end of the function. Any idea?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
void translateWord(char word[], FILE *point_out, FILE *point_1)
{
rewind(point_1);
char ch;
int gasit = 0;
int lines = count_lines(point_1);
int i = 0;
char *cp;
char *bp;
char line[255];
char **array = (char**)malloc(sizeof(char*)*2*lines);
rewind(point_1);
while (fgets(line, sizeof(line), point_1) != NULL) {
bp = line;
while (1) {
cp = strtok(bp, "=\n");
bp = NULL;
if (cp == NULL)
break;
array[i] = (char*)malloc(sizeof(char)*strlen(cp));
strcpy(array[i++], cp);
}
}
gasit = cuvant_gasit(word, array, lines, point_out, gasit);
if (gasit == 0)
{
fprintf(point_out, "<<%s>>", word);
}
for (int k = 0; k < 2 * lines; k++)
{
free(array[k]);
}
free(array);
}

There is something wrong in translateWord :
array[i] = (char*)malloc(sizeof(char)*strlen(cp));
strcpy(array[i++], cp);
The first line must be array[i] = (char*)malloc(strlen(cp) + 1); else 1 char is missing to save the final null char during the strcpy.
Note that by definition sizeof(char) is 1.
And why do you not just use strdup rather than a malloc then a strcpy ? just replace these 2 lines by array[i++] = strdup(cp);

Related

Novice C question: Working with a variable-length array of variable-length strings?

I probably got an easy one for the C programmers out there!
I am trying to create a simple C function that will execute a system command in and write the process output to a string buffer out (which should be initialized as an array of strings of length n). The output needs to be formatted in the following way:
Each line written to stdout should be initialized as a string. Each of these strings has variable length. The output should be an array consisting of each string. There is no way to know how many strings will be written, so this array is also technically of variable length (but for my purposes, I just create a fixed-length array outside the function and pass its length as an argument, rather than going for an array that I would have to manually allocate memory for).
Here is what I have right now:
#define MAX_LINE_LENGTH 512
int exec(const char* in, const char** out, const size_t n)
{
char buffer[MAX_LINE_LENGTH];
FILE *file;
const char terminator = '\0';
if ((file = popen(in, "r")) == NULL) {
return 1;
}
for (char** head = out; (size_t)head < (size_t)out + n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; head += strlen(buffer)) {
*head = strcat(buffer, &terminator);
}
if (pclose(file)) {
return 2;
}
return 0;
}
and I call it with
#define N 128
int main(void)
{
const char* buffer[N];
const char cmd[] = "<some system command resulting in multi-line output>";
const int code = exec(cmd, buffer, N);
exit(code);
}
I believe the error the above code results in is a seg fault, but I'm not experienced enough to figure out why or how to fix.
I'm almost positive it is with my logic here:
for (char** head = out; (size_t)head < (size_t)out + n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; head += strlen(buffer)) {
*head = strcat(buffer, &terminator);
}
What I thought this does is:
Get a mutable reference to out (i.e. the head pointer)
Save the current stdout line to buffer (via fgets)
Append a null terminator to buffer (because I don't think fgets does this?)
Overwrite the data at head pointer with the value from step 3
Move head pointer strlen(buffer) bytes over (i.e. the number of chars in buffer)
Continue until fgets returns NULL or head pointer has been moved beyond the bounds of out array
Where am I wrong? Any help appreciated, thanks!
EDIT #1
According to Barmar's suggestions, I edited my code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_LINE_LENGTH 512
int exec(const char* in, const char** out, const size_t n)
{
char buffer[MAX_LINE_LENGTH];
FILE *file;
if ((file = popen(in, "r")) == NULL) return 1;
for (size_t i = 0; i < n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; i += 1) out[i] = buffer;
if (pclose(file)) return 2;
return 0;
}
#define N 128
int main(void)
{
const char* buffer[N];
const char cmd[] = "<system command to run>";
const int code = exec(cmd, buffer, N);
for (int i = 0; i < N; i += 1) printf("%s", buffer[i]);
exit(code);
}
While there were plenty of redundancies with what I wrote that are now fixed, this still causes a segmentation fault at runtime.
Focusing on the edited code, this assignment
out[i] = buffer;
has problems.
In this expression, buffer is implicitly converted to a pointer-to-its-first-element (&buffer[0], see: decay). No additional memory is allocated, and no string copying is done.
buffer is rewritten every iteration. After the loop, each valid element of out will point to the same memory location, which will contain the last line read.
buffer is an array local to the exec function. Its lifetime ends when the function returns, so the array in main contains dangling pointers. Utilizing these values is Undefined Behaviour.
Additionally,
for (int i = 0; i < N; i += 1)
always loops to the maximum storable number of lines, when it is possible that fewer lines than this were read.
A rigid solution uses an array of arrays to store the lines read. Here is a cursory example (see: this answer for additional information on using multidimensional arrays as function arguments).
#include <stdio.h>
#include <stdlib.h>
#define MAX_LINES 128
#define MAX_LINE_LENGTH 512
int exec(const char *cmd, char lines[MAX_LINES][MAX_LINE_LENGTH], size_t *lc)
{
FILE *stream = popen(cmd, "r");
*lc = 0;
if (!stream)
return 1;
while (*lc < MAX_LINES) {
if (!fgets(lines[*lc], MAX_LINE_LENGTH, stream))
break;
(*lc)++;
}
return pclose(stream) ? 2 : 0;
}
int main(void)
{
char lines[MAX_LINES][MAX_LINE_LENGTH];
size_t n;
int code = exec("ls -al", lines, &n);
for (size_t i = 0; i < n; i++)
printf("%s", lines[i]);
return code;
}
Using dynamic memory is another option. Here is a basic example using strdup(3), lacking robust error handling.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **exec(const char *cmd, size_t *length)
{
FILE *stream = popen(cmd, "r");
if (!stream)
return NULL;
char **lines = NULL;
char buffer[4096];
*length = 0;
while (fgets(buffer, sizeof buffer, stream)) {
char **reline = realloc(lines, sizeof *lines * (*length + 1));
if (!reline)
break;
lines = reline;
if (!(lines[*length] = strdup(buffer)))
break;
(*length)++;
}
pclose(stream);
return lines;
}
int main(void)
{
size_t n = 0;
char **lines = exec("ls -al", &n);
for (size_t i = 0; i < n; i++) {
printf("%s", lines[i]);
free(lines[i]);
}
free(lines);
}

How to copy an array of characters to a char pointer without using strcpy

How would I go about copying the characters of a char array into a char pointer without using strcpy aka manually. For example:
char *Strings[NUM];
char temp[LEN];
int i;
for (i = 0; i < NUM; i++){
fgets(temp, LEN, stdin);
Strings[i] = malloc(strlen(temp)+1);
Strings[i] = temp; // What would go here instead of this,
// because this causes this to happen->
}
Input:
Hello
Whats up?
Nothing
Output (when the strings in the array of char pointers are printed):
Nothing
Nothing
Nothing
I'm not sure how to fix this problem.
In your example, you use these two lines:
Strings[i] = malloc(strlen(temp)+1); /* you should check return of malloc() */
Strings[i] = temp;
Which is incorrect. The second line just overwrites the pointer given back from malloc(). You need to instead use strcpy() from <string.h>:
Strings[i] = malloc(strlen(temp)+1);
strcpy(Strings[i], temp);
char *strcpy(char *dest, const char *src) copies the string pointed to, from src to dest. dest is the destination, and src is the string to be copied. Returns a pointer to dest.
You are also not checking the return of fgets(), which returns NULL on failure. You should also consider removing the \n character appended by fgets(), as the strings you copy into Strings[i] will have a trailing newline, which might not be what you want.
Since another answer showed how to do it manually, you might want to also consider just using strdup() to do the copying for you.
strdup() returns a pointer to a new string which is duplicate of string str. Memory is obtained from malloc(), and deallocated from the heap with free().
Here is some example code which does extra error checking.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 3
#define BUFFSIZE 20
int main(void) {
char *strings[LEN] = {NULL};
char buffer[BUFFSIZE] = {'\0'};
size_t slen, strcnt = 0, i;
printf("Input:\n");
for (i = 0; i < LEN; i++) {
if (fgets(buffer, BUFFSIZE, stdin) == NULL) {
fprintf(stderr, "Error from fgets()\n");
exit(EXIT_FAILURE);
}
slen = strlen(buffer);
if (slen > 0 && buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
fprintf(stderr, "Too many characters entered\n");
exit(EXIT_FAILURE);
}
if (*buffer) {
strings[strcnt] = strdup(buffer);
if (strings[strcnt] == NULL) {
fprintf(stderr, "Cannot allocate buffer\n");
exit(EXIT_FAILURE);
}
strcnt++;
}
}
printf("\nOutput:\n");
for (i = 0; i < strcnt; i++) {
printf("%s\n", strings[i]);
free(strings[i]);
strings[i] = NULL;
}
return 0;
}
So what is happening is you change the value of temp but all the pointers point to the one instance of temp. You need to allocate the memory and then copy over the array manually.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int LEN = 20;
int NUM = 3;
char* Strings[NUM];
char temp[LEN];
int i,j;
for (i=0;i<NUM;i++){
fgets(temp,LEN,stdin);
Strings[i] = (char*)malloc(strlen(temp)+1);
for(j=0;j<=strlen(temp);j++) { /* this part */
if (j == strlen(temp))
Strings[i][j - 1] = temp[j]; /* overwrite \n with the terminating \0 */
else
Strings[i][j] = temp[j];
}
}
for (i=0;i<NUM;i++)
printf("%s\n", Strings[i]);
return 0;
}

Array of pointers to char * in c using qsort

While adding string to my pointer's array, it is being overwriten by the last one. Could anyone tell me, where's my mistake?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main (){
int ile = 3;
const char * slowa[ile];
for(int j = 0; j < ile; j++){
char string[30];
gets(string);
slowa[j] = string;
printf ("%s dodalem pierwsza\n",string);
}
for (int i = 0; i < ile; i++) {
printf ("%s numer %d\n",slowa[i],i);
}
return 0;
}
The answer is in the following two lines of code:
char string[30];
...
slowa[j] = string;
The assignment sets slowa[j] to the address of the same buffer, without making a copy. Hence, the last thing that you put in the buffer would be referenced by all elements of slowa[] array, up to position of j-1.
In order to fix this problem, make copies before storing values in slowa. You can use non-standard strdup, or use malloc+strcpy:
char string[30];
gets(string);
slowa[j] = malloc(strlen(string)+1);
strcpy(slowa[j], string);
In both cases you need to call free on all elements of slowa[] array to which you have assigned values in order to avoid memory leaks.
You're always pointing to array of chars which is stack variable it's locally allocated only in scope of function, possibly each declaration of string will be on the same address as previous iteration in your loop. You could either instead of using array of chars allocate memory each loop iteration or use array and then using i.e strdup allocate memory for your new string like
slowa[j] = strdup(string) :
As others have said, you need to create copies of the strings, otherwise you set the strings to the same address, and therefore they just overwrite each other.
Additionally, I think using fgets over gets is a much safer approach. This is because gets is very prone to buffer overflow, whereas with fgets, you can easily check for buffer overflow.
This is some code I wrote a while ago which is similar to what you are trying to achieve:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PTRS 3
#define STRLEN 30
int
string_cmp(const void *a, const void *b) {
const char *str1 = *(const char**)a;
const char *str2 = *(const char**)b;
return strcmp(str1, str2);
}
int
main(void) {
char *strings[PTRS];
char string[STRLEN];
int str;
size_t len, i = 0;
while (i < PTRS) {
printf("Enter a string: ");
if (fgets(string, STRLEN, stdin) == NULL) {
fprintf(stderr, "%s\n", "Error reading string");
exit(EXIT_FAILURE);
}
len = strlen(string);
if (string[len-1] == '\n') {
string[len-1] = '\0';
} else {
break;
}
strings[i] = malloc(strlen(string)+1);
if (strings[i] == NULL) {
fprintf(stderr, "%s\n", "Cannot malloc string");
exit(EXIT_FAILURE);
}
strcpy(strings[i], string);
i++;
}
qsort(strings, i, sizeof(*strings), string_cmp);
printf("\nSuccessfully read strings(in sorted order):\n");
for (str = 0; str < i; str++) {
printf("strings[%d] = %s\n", str, strings[str]);
free(strings[str]);
strings[str] = NULL;
}
return 0;
}

Updating string until period is found in C

In this function I am going to be receiving char * words such as
person.vet.blah
and
word.friends.joe
I want to extract the first word. So for the first one I want to extract
person
and the second one I want to extract
word
How can I correctly do this? Here is my code:
char *separate_name(char *machine_name)
{
//iterate until you find period. then return
char absolute_name[1000];
int i;
for (i =0; i < strlen(machine_name); i++)
{
if (machine_name[i] == '.')
absolute_name[i] = machine_name[i];
}
return absolute_name;
}
This is just segfaulting. Any ideas what I should be doing? machine_name is going to be the "person.vet.blah" and then return absolute_name which would be "person"
Fixing your code
As others have pointed out, you can't use absolute_name outside of the function in which it was defined. This is because you're when you return the variable from your function, all that is being returned is a pointer to the beginning of the array. Outside the function, the array itself no longer exists, so the pointer is invalid and you get a segfault if you try and dereference it.
You can get around this by using malloc. Don't forget to free the memory you have allocated when you are done using it.
By the way, as well as changing your loop to a while, I also fixed the check (you were checking machine_name[i] == '.', the opposite to what you wanted).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *separate_name(char *machine_name)
{
// allocate memory on the heap
char *absolute_name = malloc(strlen(machine_name)+1);
int i = 0;
while (i < strlen(machine_name) && machine_name[i] != '.') {
absolute_name[i] = machine_name[i];
++i;
}
absolute_name[i] = '\0';
return absolute_name;
}
int main()
{
char name1[] = "person.vet.blah";
char *first1 = separate_name(name1);
if (first1 != NULL) {
printf("%s\n", first1);
free(first1);
}
char name2[] = "word.friends.joe";
char *first2 = separate_name(name2);
if (first2 != NULL) {
printf("%s\n", first2);
free(first2);
}
return 0;
}
A better alternative
strtok is the perfect tool for the job:
#include <stdio.h>
#include <string.h>
char *separate_name(char *machine_name)
{
return strtok(machine_name, ".");
}
int main()
{
char name1[] = "person.vet.blah";
char *first1 = separate_name(name1);
if (first1 != NULL) printf("%s\n", first1);
char name2[] = "word.friends.joe";
char *first2 = separate_name(name2);
if (first2 != NULL) printf("%s\n", first2);
return 0;
}
As pointed out in the comments (thanks #John), strtok modifies the string that is passed to it (it replaces the delimiter . by the \0 null byte to mark the end of the string). This isn't a problem here but is something to be aware of.
Output using either program:
person
word
#include <stdio.h>
char *separate_name(const char *machine_name){
static char absolute_name[1000];
int i;
for (i =0; i < sizeof(absolute_name)-1 ; i++){
if(machine_name[i] == '.' || machine_name[i] == '\0'){
absolute_name[i] = '\0';
break;
} else {
absolute_name[i] = machine_name[i];
}
}
return absolute_name;
}
int main(void){
printf("%s\n", separate_name("person.vet.blah"));
printf("%s\n", separate_name("word.friends.joe"));
return 0;
}

Segmentation Fault erorr

My program reads a file specified in the argument and prints out each string and its frequency inside the file.
The program works for this file: http://www.cse.yorku.ca/course/3221/dataset1.txt
but not this file: http://www.cse.yorku.ca/course/3221/dataset2.txt.
It gives Segmentation fault (core dumped) error for the second file.
What could be wrong? Please help!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char word[101];
int freq;
} WordArray;
int main(int argc, char *argv[])
{
WordArray *array = malloc(sizeof(WordArray));
FILE *file;
int i = 0;
file = fopen(argv[1], "r");
char *str = (char*) malloc (108);
while(fgets(str, 100, file) != NULL)
{
int pos = 0;
char *word = malloc (100);
while (sscanf(str, "%s%n", word, &pos ) == 1)
{
int j;
for (j = 0; j < i; j++)
{
if (strcmp(array[j].word, word) == 0)
{
array[j].freq = array[j].freq + 1;
break;
}
}
if (j==i)
{
array = (WordArray *) realloc (array, sizeof(WordArray) * (i+1));
strcpy(array[i].word, word);
array[i].freq = 1;
i++;
}
str += pos;
}
}
fclose(file);
int k;
for (k=0; k<i; k++)
{
printf("%s %d\n", array[k].word, array[k].freq);
}
return 0;
}
Several problems:
You increment str as part of the second loop and don't reset it. I think this means your program is slowly walking through memory.
You fail to free word - probably better to allocate it outside the loop and on the stack but that won't cause a crash unless you input is huge and you run out of memory.
You don't need to cast result of malloc for modern compilers (yes, it used to be needed).
May want to check the results of malloc and realloc for safety.
I assume the first item is your problem.

Resources