I have a c program where I copy one string to another but for some reason in my loop, if I remove a print statement I used for debugging once, the program crashes before I reach the print statement outside the while loop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char * cats2 = malloc(sizeof(char));
cats2[0] = '\0';
char * cats = "this string is a cool cat";
getCopyFrom(cats, cats2);
free(cats2);
return 0;
}
void getCopyFrom(char* original, char* translation){
int index = 0;
char * current = malloc(sizeof(char));
current[0] = '\0';
while(index < (strlen(original))){
printf("%d\n", index);
current = realloc(current, sizeof(char) * 2);
current[index] = original[index];
current[index + 1] = '\0';
index++;
}
printf("%s\n", current);
free(current);
}
If I remove the printf("%d\n", index); from the while loop, the program will crash before the while loop ends. If I keep it, the program runs fine until the end where it returns a access violation error.
I'm not sure why either happens, am I missing something obvious or am I just not understanding malloc and realloc correctly?
edit:)
My previous question was answered, but I have a new problem. I added translation = realloc(translation, (strlen(current) + 1) * sizeof(char)); to the code to set the size of translation to the size of current but I get another access violation. Are you not able to realloc parameters or something?
current = realloc(current, sizeof(char) * 2);
but why is the second argument to realloc() in a loop a constant? It's like we want
current = realloc(current, sizeof(char) * (index + 1));
but once you do that you'll discover your code is really slow. The * 2 meant something. You really want to keep a separate array size and allocated size, and only call realloc when array size == allocated size and double allocated size.
if (index + 1 >= alloc) {
char *new = realloc(current, sizeof(char) * (alloc = (alloc == 0) ? 4 : (alloc << 1)));
if (!new) {
/* todo handle error */
}
}
Related
I am practicing C language.
I wanted to use dynamic allocation to use only the size of the string I input as memory and check whether the input string was properly saved.
So, I wrote the following code using malloc and realloc functions.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_copy(char* str_array_f) {
void* tmp;
char buf;
unsigned char arr_size = 1;
unsigned char arr_cur = 0;
while ((buf = getchar())) {
if (buf == '\n') {
break;
}
str_array_f[arr_cur++] = (char)buf;
tmp = realloc(str_array_f, ((arr_size++) * sizeof(char)) + sizeof(char));
if (tmp != 0) {
str_array_f = tmp;
}
else {
printf("memory leak error occur! \n");
break;
}
}
str_array_f[arr_size - 1] = 0x00;
}
void main() {
int contiune = 1;
while (contiune) {
char* str_array = malloc(sizeof(char) + sizeof(char));
printf("Please type something : ");
str_copy(str_array);
printf("'str_array' have this : %s \n", str_array);
printf("-------------------------------------------------\n");
if (str_array[0] == '1') {
contiune = 0;
}
free(str_array);
}
}
And, as a result of the performance,
The following problems have occurred.
Strange values sometimes appear from the 5th character of the intermittently printed value
(To reproduce this issue, it is recommended to remove the while loop and try repeatedly)
In the case of repeatedly receiving a value by using the while loop, an error occurs after 4 repetitions.
If the allocated memory of tmp, which is a void type pointer, is released after line 22(e.g., 'free(tmp);'), when executed, no output and an error occurs immediately.
For the above 3 problems, I am not sure what is the cause and how to fix it.
Please let me know if there is a solution.
And, if there is a bad coding method in my code in terms of efficiency or various aspects, I would appreciate it if you let me know.
*Programming execution environment : Visual studio 2019
to explain what you're doing wrong I'm going to use a minimal example here
void change_x(int x) {
x = 2;
}
int main() {
int x = 1;
change_x(x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
here the integer x is copied when the function is called and changing it won't really change the x in main. similarly you are doing in your code that str_array_f = tmp; it really won't change the str_array but the copied value. and you're trying to free a pointer that was reallocated before.
the fix for the example above is not to pass the value x instead pass the address of x (which is equivalent to pass by reference in other languages)
void change_x(int* x) {
*x = 2;
}
int main() {
int x = 1;
change_x(&x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
and for your code
void str_copy(char** str_array_f) {...} // change the parameter
*str_array_f = tmp; // de reference and use it.
str_copy(&str_array); // call with it's address
And one more thing, don't reallocate more often it's not efficient. instead just just allocate your "array" type with a minimum size and when it's filled reallocate it with the size of 2 times of it (or 1.5 if you like)
This question already has answers here:
Realloc fails after the 10th iteration inside a loop
(2 answers)
Closed 5 years ago.
I am trying to receive a string of unidentified length in C through pointers and realloc() function but after the string became 30 character long , the first few characters are like this :
ÿZ └
I am running it on windows :
#include <stdio.h>
#include <stdlib.h>
char* array;
int current = 0;
int size = 10;
void add(char element)
{
if (current == size) {
size += 10;
realloc(array, size * (sizeof(char)));
}
*(array + current) = element;
current++;
}
int main()
{
array = calloc(10, sizeof(char));
char c;
loop:
c = getchar();
if (c != '\n') {
add(c);
goto loop;
}
else {
for (int i = 0; i <= current - 1; i++) {
putchar(*(array + i));
}
printf("\nThe size of the string is %d", current);
}
}
You are not storing the return value of realloc.
char *temp;
temp=realloc(array,size*(sizeof(char)));
if( temp )
array = temp;
else
// error
goto makes code harder to debug by generating a complex control flow.
If you think, you will see you can replace goto with a for or while loop too.
You can see that your else part is not part of the goto part. Once else is executed it's never going to execute again.
So create a loop with this(else part) outside the loop.
Also return type of getchar() is int.
You need to do like this:
array=realloc(array,size*(sizeof(char)));
The memory may be moved to another location. That's why you need to reassign the pointer.
Of course, it is good to do some error checking too:
char *tmp;
tmp = realloc(array,size*(sizeof(char)));
if(tmp)
array=tmp;
else
perror("realloc failed");
I am really stuck with one very simple piece of code.
This program takes argument like ./a.out -t=1,32,45,2 and prints quantity of commas in stdout. But from time to time execution works correctly and and more often throws segmentation fault.
I figured out that problem in this line of function substr_cnt (I also placed corresponding commentaries in code below):
target_counting = (char *)malloc(sizeof(char)*(strlen(target)));
In fact malloc returns NULL. If I change sizeof(char) by sizeof(char *) all starts work like a charm but I can't understand why is that. Furthermore in main function I also use malloc, and even with the same line
arg_parameter = (char *) malloc(sizeof(char)*(strlen(argv[1] - 3)));
all works just fine.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define strindex(target, source) ((size_t) strstr(target, source) - (size_t) target)
int substr_cnt( char *target, char *source ) {
int i=0;
int cnt=0;
char *target_counting;
//this is NOT working
target_counting = (char *)malloc(sizeof(char)*(strlen(target)));
//this is working
//target_counting = (char *)malloc(sizeof(char *)*(strlen(target)));
if (target_counting == NULL) {
printf("malloc failed\n");
return -1;
}
strcpy(target_counting, target);
while ((i=strindex(target_counting, source)) > 0) {
strncpy(target_counting, target_counting + i + 1, strlen(target_counting));
cnt++;
}
free(target_counting);
return cnt;
}
int main( int argc, char *argv[] )
{
int i;
int default_behavior = 0;
int arg_parametr_cnt;
char *arg_parameter;
if (argc == 1) {
default_behavior = 1;
} else if (argv[1][0] == '-' && argv[1][1] == 't' && argv[1][2] == '=') {
//this is working
arg_parameter = (char *) malloc(sizeof(char)*(strlen(argv[1] - 3)));
strncpy(arg_parameter, argv[1]+3, strlen(argv[1]));
printf("%s\n", arg_parameter);
arg_parametr_cnt = substr_cnt(arg_parameter, ",");
printf("commas: %d\n", arg_parametr_cnt);
}
else {
printf("wrong command line");
return 1;
}
return 0;
}
You have several issues here, the main point, you don't need to allocate memory at all. You can implement searching for a given substring without modifying the string and therefore work directly on the given argv parameters, e.g.
int substr_cnt(const char *haystack, const char *needle)
{
int cnt = 0;
const char *found = haystack;
while ((found = strstr(found, needle)) != NULL) {
++found;
++cnt;
}
return cnt;
}
Same for the call in main, just pass argv directly
arg_parametr_cnt = substr_cnt(argv[1] + 3, ",");
Now to answer your question, unless you really see the output of
printf("malloc failed\n");
I don't believe, malloc returns NULL, because when you allocate an even larger amount of memory, sizeof(char*) vs sizeof(char), it works.
The reasons, why your program crashes, are already covered in the other answers. To summarize
target_counting = (char *)malloc(sizeof(char)*(strlen(target))); allocates one char less than it should
while ((i=strindex(target_counting, source)) > 0) I'm not sure, what happens, when the result of strstr is NULL. strindex might return a negative number, depending on your memory layout, but I am not sure.
strncpy(target_counting, target_counting + i + 1, strlen(target_counting)); This is not really an issue, but since you copy the rest of the string, you could use strcpy(target_counting, target_counting + i + 1) instead.
arg_parameter = (char *) malloc(sizeof(char)*(strlen(argv[1] - 3))); this should be malloc(sizeof(char) * strlen(argv[1]) - 3 + 1)
strncpy(arg_parameter, argv[1]+3, strlen(argv[1])); again strcpy(arg_parameter, argv[1]+3) would be sufficient
Update:
In this version
int strindex(char *target, char *source)
{
char *idx;
if ((idx = strstr(target, source)) != NULL) {
return idx - target;
} else {
return -1;
}
}
you have an explicit test for NULL and act accordingly.
In the macro version
#define strindex(target, source) ((size_t) strstr(target, source) - (size_t) target)
there is no such test. You determine the index by calculating the difference between strstr() and the base address target. This is fine so far, but what happens, when strstr() returns NULL?
Pointer arithmetic is defined with two pointers, pointing into the same array. As soon as the two pointers point into different arrays, or one pointing into an array and the other somewhere else, the behaviour is undefined.
Technically, when you calculate NULL - target, it might yield a negative value, but it also might not. If target points to the address of 0x0f0a3a90, you could have 0x0 - 0x0f0a3a90 and get a negative value. If target points to 0xfe830780 however, it might be interpreted as a negative number, and then 0x0 - 0xfe830780 could result in a positive number.
But the main point is, you have undefined behaviour. For further reading look for pointer arithmetic, e.g. C++: Pointer Arithmetic
your malloc is not allocating space for the null terminator, you need to malloc (strlen(string)+1).
The malloc with a char* works because a pointer (is normal) 4 bytes long, so you are allocating 4 times more memory than required - minus the 1 byte need for a null terminator.
The problem may lie here: malloc(sizeof(char)*(strlen(argv[1] - 3)) in main. You are subtracting 3 from argv[1].
I think you intended to use:
malloc(sizeof(char)*(strlen(argv[1]) - 2)); // Allocate one more space for '\0' character
Doing this makes strlen to access unallocated memory.
Your program may not fail here, but later, because it is simply undefined behavior.
There are several buffer overruns, but I think that the bug that makes you program crash is the following:
strncpy(target_counting, target_counting + i + 1, strlen(target_counting));
Note that the strings in strncpy may not overlap!
I suggest that you do a memmove instead, because memmove can handle overlapping buffers:
memmove(target_counting, target_counting + i + 1, strlen(target_counting + i + 1) + 1);
I think your main issue is here :
arg_parameter = (char *) malloc(sizeof(char)*(strlen(argv[1] - 3)));
especially here
strlen(argv[1] - 3)
you pass to strlen address of argv[1]-3 which is not valid address.
actually what you meant is strlen(argv[1]) - 3. As others said you also should add one char for \0 so strlen(argv[1]) - 2
I need to create program that takes input from stdin in this format:
abcde //number of characters in word = number of words => square shape
fghij
klmno
pqrst
uvwxy
// \n separates first half from second
word1word //any amount of characters, any amount of words
word
word2
sdf
// \n to end input
My code works, but only about 50% of the time. I have couple of example inputs, that I use for testing, but for some of them my readwords function fails.
Here is my function, that reads words. Since I have no idea how many words or how long they are going to be, I use dynamic arrays and getchar() function.
void readWords(char **p,int *n,int w) /* before calling: n = 50; w = 20; p = 50x20 char array */
{
int i = 0,j = 0,x;
char tmp,prevtmp;
while (1)
{
prevtmp = tmp;
tmp = getchar();
if ((prevtmp == '\n' && tmp == '\n') || feof(stdin))
break; /* no more words to read */
if (tmp == '\n') /* end of word */
{
p[i][j] = '\0'; /* add \0 to create string format */
i++;
j = 0;
if (i == *n) /* if there is more words than there is space for them, double the size */
if (realloc(p,*n*2) != NULL)
*n*=2;
continue;
}
p[i][j] = tmp;
j++;
if (j == w) /* if width of word is larger than allocated space, double it */
{
for (x = 0; x < *n;x++);
if(realloc (p[x],w*2) != NULL);
w=w*2;
}
}
*n = i;
}
This is example of input for which this works (note:this function only reads second half after line with only \n):
dsjellivhsanxrr
riemjudhgdffcfz
<skipping>
atnaltapsllcelo
ryedunuhyxhedfy
atlanta
saltlakecity
<skipping 15 words>
hartford
jeffersoncity
And this is input that my function doesn't read properly:
<skipping>
...oywdz.ykasm.pkfwb.zazqy...
....ynu...ftk...zlb...akn....
missouri
delaware
<skipping>
minnesota
southdakota
What my function reads from this input:
e
yoming
xas
florida
lvania
ana
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
There is no difference between those two inputs (except different words and different amount and length of words), the first half gets read properly no matter what, but only the second half bugs out. How do I fix this?
P.S. sorry for long post, in case you want to see full input without skipped bytes, here is pastebin: http://pastebin.com/hBGn2tej
realloc() returns the address of the newly allocated memory, it does not update the argument passed into it. So this (and the other use of realloc()) is incorrect:
if (realloc(p,*n*2) != NULL)
and will results in the code accessing memory incorrectly, causing undefined behaviour. Store the result of realloc() to a temporary variable and check for non-NULL before updating p. The argument to realloc() also indicates the number of bytes, not the number of elements so the size argument calculation is incorrect as p is an array of char* so it should be realloc(p, sizeof(char*) * (*n * 2));. However, the change to p would not be visible to the caller. Also note that the only legal arguments to realloc() are pointers obtained from a previous call to malloc(), realloc() or calloc(). The comment p = 50x20 char array in the code suggests this is not the case.
Here is a small example that allocates an array of char* which should be helpful:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void f(char*** p)
{
/* Allocate space for two 'char*' elements.
Add a NULL pointer element as sentinel value
so caller knows where to find end of list. */
*p = malloc(sizeof(**p) * 3);
/* Allocate space for the two strings
and populate. */
(*p)[0] = malloc(10);
(*p)[1] = malloc(10);
strcpy((*p)[0], "hello");
strcpy((*p)[1], "world");
(*p)[2] = NULL;
/* Add a third string. */
char** tmp = realloc(*p, sizeof(**p) * 4);
if (tmp)
{
*p = tmp;
(*p)[2] = malloc(10);
strcpy((*p)[2], "again");
(*p)[3] = NULL;
}
}
int main()
{
char** word_list = 0;
f(&word_list);
if (word_list)
{
for (int i = 0; word_list[i]; i++)
{
printf("%s\n", word_list[i]);
free(word_list[i]);
}
}
free(word_list);
return 0;
}
Additionally:
prevtmp has an unknown value upon its first use.
getchar() actually returns an int and not a char.
My function is being passed a struct containing, among other things, a NULL terminated array of pointers to words making up a command with arguments.
I'm performing a glob match on the list of arguments, to expand them into a full list of files, then I want to replace the passed argument array with the new expanded one.
The globbing is working fine, that is, g.gl_pathv is populated with the list of expected files. However, I am having trouble copying this array into the struct I was given.
#include <glob.h>
struct command {
char **argv;
// other fields...
}
void myFunction( struct command * cmd )
{
char **p = cmd->argv;
char* program = *p++; // save the program name (e.g 'ls', and increment to the first argument
glob_t g;
memset(&g, 0, sizeof(g));
g.gl_offs = 1;
int res = glob(*p++, GLOB_DOOFFS, NULL, &g);
glob_handle_res(res);
while (*p)
{
res = glob(*p, GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
glob_handle_res(res);
}
if( g.gl_pathc <= 0 )
{
globfree(&g);
}
cmd->argv = malloc((g.gl_pathc + g.gl_offs) * sizeof *cmd->argv);
if (cmd->argv == NULL) { sys_fatal_error("pattern_expand: malloc failed\n");}
// copy over the arguments
size_t i = g.gl_offs;
for (; i < g.gl_pathc + g.gl_offs; ++i)
cmd->argv[i] = strdup(g.gl_pathv[i]);
// insert the original program name
cmd->argv[0] = strdup(program);
** cmd->argv[g.gl_pathc + g.gl_offs] = 0; **
globfree(&g);
}
void
command_free(struct esh_command * cmd)
{
char ** p = cmd->argv;
while (*p) {
free(*p++); // Segfaults here, was it already freed?
}
free(cmd->argv);
free(cmd);
}
Edit 1: Also, I realized I need to stick program back in there as cmd->argv[0]
Edit 2: Added call to calloc
Edit 3: Edit mem management with tips from Alok
Edit 4: More tips from alok
Edit 5: Almost working.. the app segfaults when freeing the command struct
Finally: Seems like I was missing the terminating NULL, so adding the line:
cmd->argv[g.gl_pathc + g.gl_offs] = 0;
seemed to make it work.
argv is an array of pointers of char *. This means that argv has space for argc char * values. If you try to copy more than that many char * values into it, you will end up with an overflow.
Most likely your glob call results in more than argc elements in gl_pathv field (i.e, gl_pathc > argc). This is undefined behavior.
It is similar to the code below:
/* Wrong code */
#include <string.h>
int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3, 4 };
memcpy(a, b, sizeof b);
Solution: you should either work with the glob_t struct directly, or allocate new space to copy gl_pathv to a new char **:
char **paths = malloc(g.gl_pathc * sizeof *paths);
if (paths == NULL) { /* handle error */ }
for (size_t i=0; i < g.gl_pathc; ++i) {
/* The following just copies the pointer */
paths[i] = g.gl_pathv[i];
/* If you actually want to copy the string, then
you need to malloc again here.
Something like:
paths[i] = malloc(strlen(g.gl_pathv[i] + 1));
followed by strcpy.
*/
}
/* free all the allocated data when done */
Edit: after your edit:
cmd->argv = calloc(g.gl_pathc, sizeof(char *) *g.gl_pathc);
it should work, but each of argv[1] to argv[g.gl_pathc + g.gl_offs - 1] is a char * that is "owned" by the struct glob. Your memcpy call is only copying the pointers. When you later do globfree(), those pointers don't mean anything anymore. So, you need to do copy the strings for your use:
size_t i;
cmd->argv = malloc((g.gl_pathc+g.gl_offs) * sizeof *cmd->argv);
for (i=g.gl_offs; i < g.gl_pathc + g.gl_offs; ++i)
cmd->argv[i] = strdup(g.gl_pathv[i]);
This makes sure you now have your own private copies of the strings. Be sure to free them (and argv) once you are done.
There are a few other problems with your code.
You are doing *p++, you should do p++, since you're not using the value of the dereferencing.
You should really check the return value of glob.
Your paths variable needs g.gl_pathc + 1 elements, not g.gl_pathc. (Or more correctly, you need to allocate g.gl_pathc + g.gl_offs times sizeof *paths bytes.)
Your for loop to copy strings should be for (j=1; j < g.gl_pathc + g.gl_offs; ++j).
Make sure you prevent shell from expanding your glob. I.e., call ./a.out '*' instead of ./a.out *.
Don't you need to multiple g.gl_pathc by sizeof(char *)?