How to copy a malloced string into another string in C - c

I have this function that randomly generates a string
char *generate_random_string(int seed) {
if (seed != 0) {
srand(seed);
}
char *alpha_num_str =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
char *random_str = malloc(RAND_STR_LEN);
for (int i = 0; i < RAND_STR_LEN; i++) {
random_str[i] = alpha_num_str[rand() % (strlen(alpha_num_str) - 1)];
}
return random_str;
}
I want to copy the return value of this function if I give it a seed of '1' into a string called initialisation_vector and this is currently how I am doing it:
char initialisation_vector[RAND_STR_LEN + 1] = {0};
strcpy(initialisation_vector, generate_random_string(1));
However, when I run the code I get a malloc buffer overflow error from the strcpy line. What am I doing wrong, how do I allocate enough memory for this?

The main issue:
Strings is C are expected to be null/zero terminated. This means that an n characters string should be allocated with n+1 bytes and the last one should be '\0'.
The problem in your code is that random_str is allocated to contain exactly RAND_STR_LEN characters and therefore misses the null termination.
You should change:
char *random_str = malloc(RAND_STR_LEN);
for (int i = 0; i < RAND_STR_LEN; i++) {
random_str[i] = alpha_num_str[rand() % (strlen(alpha_num_str) - 1)];
}
To:
/*-------------------------------------vvv--*/
char *random_str = malloc(RAND_STR_LEN + 1);
for (int i = 0; i < RAND_STR_LEN; i++) {
random_str[i] = alpha_num_str[rand() % (strlen(alpha_num_str) - 1)];
}
random_str[RAND_STR_LEN] = '\0'; /* add zero termination */
Note:
If you want to select random characters from all the available ones in alpha_num_str, you should use: rand() % (strlen(alpha_num_str)) (without the -1). This is because x % n will return values of 0..n-1.
Also for efficiency reasons you can calculate it once before the loop and store in a variable.

Related

buffer overrun while trying to link two strings together, why do I have this error?

(in C, using visual studio 2022 preview), I have to do a program that link two strings together. Here's what I did:
I wrote two for-loops to count characters of first string and second
string,
I checked (inside the link function if the pointers are null (first and second). If they are null, then "return NULL".
I created "char *result". this is a new string and this is the string to be returned. I allocated enough memory to store nprime, nsecond, and 1 more character (the zero terminator). I used a malloc.
then, I checked if result is null. if it's null then "return NULL".
then, I wrote 2 for-loops to perform the linking between the first string and the second string. And here I got a compiler warning (because I think it's in compile time not in debug time). buffer overrun, the writable size is
"nprime+nsecond+1" but 2 bytes might be written.
my theory is that the program is trying to write outside the result-array, so there could be a loss of data, I tried to edit my code, therefore I write "nprime+nsecond+2" instead but it doesn't work, and it keeps showing me the same buffer overrun error.
#include <stdlib.h>
char* link( const char* first, const char* second) {
size_t nprime = 0;
size_t nsecond = 0;
if (first == NULL) {
return NULL;
}
if (second == NULL) {
return NULL;
}
for (size_t i = 0; first[i] < '\0'; i++) {
nprime++;
}
for (size_t i = 0; second[i] < '\0'; i++) {
nsecond++;
}
char* result = malloc(nprime + nsecond + 1);
if (result == NULL) {
return NULL;
}
for (size_t i = 0; i < nprime; i++) {
result[i] = first[i];
}
for (size_t i = 0; i < nsecond; i++) {
result[nprime + i] = second[i];
}
result[nprime + nsecond] = 0;
return result;
}
this is the main:
int main(void) {
char s1[] = "this is a general string ";
char s2[] = "this is a general test.";
char* s;
s = link(s1, s2);
return 0;
}
The warning is given due to the wrong conditions you defined in the first 2 for loops. The right loops should be as follows:
for (size_t i = 0; first[i] != '\0'; i++) {
nprime++;
}
for (size_t i = 0; second[i] != '\0'; i++) {
nsecond++;
}
With the conditions you defined (i.e. first[i] < '\0') you are just counting how many chars in the given string have an ASCII code lower than the ASCII code of \0 and exit the loop as soon as you find a char not fulfilling such condition.
Since '\0' has ASCII value 0, your nprime and nsecond are never incremented, leading to a malloc with insufficient room for the chars you actually need.

C allocation memory error. Don't find something like this

Could you help please ?
When I execute this code I receive that:
AAAAABBBBBCCCCCBBBBBCOMP¬ıd┐╔ LENGTH 31
There are some weirds characters after letters, while I've allocate just 21 bytes.
#include <stdio.h>
#include <stdlib.h>
char * lineDown(){
unsigned short state[4] = {0,1,2,1};
char decorationUp[3][5] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
char * deco = malloc(21);
int k;
int p = 0;
for(int j = 0; j < 4; j++){
k = state[j];
for(int i = 0; i < 5; i++){
*(deco+p) = decorationUp[k][i];
p++;
}
}
return deco;
}
int main(void){
char * lineDOWN = lineDown();
int k = 0;
char c;
do{
c = *(lineDOWN+k);
printf("%c",*(lineDOWN+k));
k++;
}while(c != '\0');
printf("LENGTH %d\n\n",k);
}
The function does not build a string because the result array does not contain the terminating zero though a space for it was reserved when the array was allocated.
char * deco = malloc(21);
So you need to append the array with the terminating zero before exiting the function
//...
*(deco + p ) = '\0';
return deco;
}
Otherwise this do-while loop
do{
c = *(lineDOWN+k);
printf("%c",*(lineDOWN+k));
k++;
}while(c != '\0')
will have undefined behavior.
But even if you will append the array with the terminating zero the loop will count the length of the stored string incorrectly because it will increase the variable k even when the current character is the terminating zero.
Instead you should use a while loop. In this case the declaration of the variable c will be redundant. The loop can look like
while ( *( lineDOWN + k ) )
{
printf("%c",*(lineDOWN+k));
k++;
}
In this case this call
printf("\nLENGTH %d\n\n",k);
^^
will output the correct length of the string equal to 20.
And you should free the allocated memory before exiting the program
free( lineDOWN );
As some other wrote here in their answers that the array decorationUp must be declared like
char decorationUp[3][6] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
then it is not necessary if you are not going to use elements of the array as strings and you are not using them as strings in your program.
Take into account that your program is full of magic numbers. Such a program is usually error-prone. Instead you should use named constants.
In
char decorationUp[3][5] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
your string needs 6 characters to also place the null char, even in that case you do not use them as 'standard' string but only array of char. To get into the habit always reverse the place for the ending null character
you can do
char decorationUp[3][6] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
Note it is useless to give the first size, the compiler counts for you
Because in main you stop when you read the null character you also need to place it in deco at the end, so you need to allocate 21 for it. As before you missed the place for the null character, but here that produces an undefined behavior because you read after the allocated block.
To do *(deco+p) is not readable, do deco[p]
So for instance :
char * lineDown(){
unsigned short state[] = {0,1,2,1};
char decorationUp[][6] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
char * deco = malloc(4*5 + 1); /* a formula to explain why 21 is better than 21 directly */
int k;
int p = 0;
for(int j = 0; j < 4; j++){
k = state[j];
for(int i = 0; i < 5; i++){
deco[p] = decorationUp[k][i];
p++;
}
}
deco[p] = 0;
return deco;
}

How to find an element in an array of structs in C?

I have to write a function that finds a product with given code from the given array. If product is found, a pointer to the corresponding array element is returned.
My main problem is that the given code should first be truncated to seven characters and only after that compared with array elements.
Would greatly appreciate your help.
struct product *find_product(struct product_array *pa, const char *code)
{
char *temp;
int i = 0;
while (*code) {
temp[i] = (*code);
code++;
i++;
if (i == 7)
break;
}
temp[i] = '\0';
for (int j = 0; j < pa->count; j++)
if (pa->arr[j].code == temp[i])
return &(pa->arr[j]);
}
Why don't you just use strncmp in a loop?
struct product *find_product(struct product_array *pa, const char *code)
{
for (size_t i = 0; i < pa->count; ++i)
{
if (strncmp(pa->arr[i].code, code, 7) == 0)
return &pa->arr[i];
}
return 0;
}
temp is a pointer which is uninitialized and you are dereferencing it which will lead to undefined behavior.
temp = malloc(size); // Allocate some memory size = 8 in your case
One more mistake I see is
if (pa->arr[j].code == temp[i]) // i is already indexing `\0`
should be
strcmp(pa->arr[j].code,temp); // returns 0 if both the strings are same
This code can completely be avoided if you can use strncmp()
As pointed out by others, you are using temp uninitialized and you are always comparing characters with '\0'.
You don't need a temp variable:
int strncmp ( const char * str1, const char * str2, size_t num );
Compare characters of two strings
Compares up to num characters of the
C string str1 to those of the C string str2.
/* Don't use magic numbers like 7 in the body of function */
#define PRODUCT_CODE_LEN 7
struct product *find_product(struct product_array *pa, const char *code)
{
for (int i = 0; i < pa->count; i++) {
if (strncmp(pa->arr[i].code, code, PRODUCT_CODE_LEN) == 0)
return &(pa->arr[i]);
}
return NULL; /* Not found */
}
When you write char* temp; you are just declaring an uninitialized pointer
In your case since you say that the code is truncated to 7 you could create a buffer
on the stack with place for the code
char temp[8];
Writing
temp[i] = (*code);
code++;
i++;
Can be simplified to:
temp[i++] = *code++;
In your loop
for (int j = 0; j < pa->count; j++)
if (pa->arr[j].code == temp[i])
return &(pa->arr[j]);
You are comparing the address of code and the character value of temp[i] which incidentally could be 8 and outside the array.
Instead what you want to do is compare what code points to and what temp contains:
for (int j = 0; j < pa->count; j++)
if (!strncmp(pa->arr[j].code, temp, 7)
return &(pa->arr[j]);
You should also return NULL; if nothing was found, seems you do not return anything.
Probably a good thing is also to make sure your temp[] always contains 7 characters.

realloc() seems to affect already allocated memory

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*));

problem in char array?

char *funcNames[]= {"VString","VChar","VArray","VData"};
for(int i=0;i<4;i++)
{
char* temp = funcNames[i];
int len = strlen(funcNames[i]);
for(int j = 0;j<len ;j++)
{
if(j!=0)
{
char arr = temp[j];
}
}
}
here i want to separate "V" from all string in char array ...and create another char array without "V" in starting of the string.i want another char array {String,char,array,data}...i cant make a char array ....help me to solve my issue...
Do you really need a copy? You could just make a new array pointing into the original strings:
char *newArray[4];
for (i = 0; i < 4; i++) {
newArray[i] = funcNames[i] + 1;
}
If you do need to make copies then you'll have to use dynamical allocation to create the buffers to hold the copies. What you will do is declare an array of pointers and place an allocated string buffer in each of the array's entries:
char *newArray[4];
for (i = 0; i < 4; i++) {
newArray[i] = malloc(sizeof(char) * streln(funcNames[0]));
strcpy(newArray[i], funcNames[i] + 1);
}
You will have to call free() on each allocated buffer.
Or if you don't want to do allocation and are know the maximum length of the strings in funcNames:
#define MAX_FUNC_NAME_LEN 32
char newArray[4][MAX_FUNC_NAME_LEN];
for (i = 0; i < 4; i++) {
strcpy(newArray[i], funcNames[i] + 1);
}
There's only small differences between arrays and pointers so I'd opt for:
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main (void) {
int i;
char *funcNames[]= {"VString","VChar","VArray","VData"};
// This is the code that dupicates your strings by allocating an array,
// then allocating each string within that array (and copying).
// Note we use strlen, not strlen+1 to mallocsince we're replacing the
// 'V' at the start with the zero byte at the end. Also we strcpy
// from char offset 1, not 0 (to skip the fist char).
char **newNames = malloc (sizeof(char*) * sizeof(funcNames) / sizeof(*funcNames));
assert (newNames != NULL);
for (i = 0; i < sizeof(funcNames) / sizeof(*funcNames); i++) {
newNames[i] = malloc (strlen (funcNames[i]));
assert (newNames[i] != NULL);
strcpy (newNames[i], funcNames[i] + 1);
}
/* Use your newNames here */
for (i = 0; i < sizeof(funcNames) / sizeof(*funcNames); i++) {
printf ("funcNames[%d] #%08x = '%s'\n", i, funcNames[i], funcNames[i]);
printf (" newNames[%d] #%08x = '%s'\n", i, newNames[i], newNames[i]);
putchar ('\n');
}
// Finished using them.
// Free the strings themselves, then free the array.
for (i = 0; i < sizeof(funcNames) / sizeof(*funcNames); i++)
free (newNames[i]);
free (newNames);
return 0;
}
You can see from the output that the locations of the variables in memory are different and that the content of the new strings is what you wanted:
funcNames[0] #00402000 = 'VString'
newNames[0] #006601c0 = 'String'
funcNames[1] #00402008 = 'VChar'
newNames[1] #006601d0 = 'Char'
funcNames[2] #0040200e = 'VArray'
newNames[2] #006601e0 = 'Array'
funcNames[3] #00402015 = 'VData'
newNames[3] #006601f0 = 'Data'

Resources